FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Adding Bigtable RowFilter. · googleapis/google-cloud-python@15b001d · GitHub

Commit 15b001d

Browse files
committed
Adding Bigtable RowFilter.
These come from https://github.com/GoogleCloudPlatform/cloud-bigtable-client/blob/6a498cd3e660c7ed18299e980c1658d67661e69b/bigtable-protos/src/main/proto/google/bigtable/v1/bigtable_data.proto#L321-L404 and parent classes are forthcoming to handle the non-primitive cases of filter Chain, Interleave and Condition (ternary). Of the remaining allowed fields, missing: - column_range_filter - timestamp_range_filter - value_range_filter Since they (will) depend on non-primitive types.
1 parent 94c007d commit 15b001d

2 files changed

Lines changed: 336 additions & 0 deletions

File tree

‎gcloud/bigtable/row.py‎

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,197 @@ class Row(object):
3131
def __init__(self, row_key, table):
3232
self._row_key = _to_bytes(row_key)
3333
self._table = table
34+
35+
36+
# NOTE: For developers, this class may seem to be a bit verbose, i.e.
37+
# a list of property names and **kwargs may do the trick better
38+
# than actually listing every single argument. However, for the sake
39+
# of users and documentation, listing every single argument is more
40+
# useful.
41+
# pylint: disable=too-many-arguments
42+
class RowFilter(object):
43+
"""Basic filter to apply to cells in a row.
44+
45+
These values can be combined via :class:`RowFilterChain`,
46+
:class:`RowFilterUnion` and :class:`ConditionalRowFilter`.
47+
48+
The regex filters must be valid RE2 patterns. See Google's
49+
`RE2 reference`_ for the accepted syntax.
50+
51+
.. _RE2 reference: https://github.com/google/re2/wiki/Syntax
52+
53+
.. note::
54+
55+
At most one of the keyword arguments can be specified at once.
56+
57+
.. note::
58+
59+
For :class:`bytes` regex filters (``row_key``, ``column_qualifier`` and
60+
``value``), special care need be used with the expression used. Since
61+
each of these properties can contain arbitrary bytes, the ``\\C``
62+
escape sequence must be used if a true wildcard is desired. The ``.``
63+
character will not match the new line character ``\\n``, which may be
64+
present in a binary value.
65+
66+
:type sink: bool
67+
:param sink: ADVANCED USE ONLY. Hook for introspection into the RowFilter.
68+
Outputs all cells directly to the output of the read rather
69+
than to any parent filter. Cannot be used within the
70+
``predicate_filter``, ``true_filter``, or ``false_filter``
71+
of a :class:`ConditionalRowFilter`.
72+
73+
:type pass_all_filter: bool
74+
:param pass_all_filter: Matches all cells, regardless of input.
75+
Functionally equivalent to leaving ``filter``
76+
unset, but included for completeness.
77+
78+
:type block_all_filter: bool
79+
:param block_all_filter: Does not match any cells, regardless of input.
80+
Useful for temporarily disabling just part of
81+
a filter.
82+
83+
:type row_key_regex_filter: bytes
84+
:param row_key_regex_filter: A regular expression (RE2) to match cells from
85+
rows with row keys that satisfy this regex.
86+
For a ``CheckAndMutateRowRequest``, this
87+
filter is unnecessary since the row key is
88+
already specified.
89+
90+
:type row_sample_filter: float
91+
:param row_sample_filter: Non-deterministic filter. Matches all cells from
92+
a row with probability p, and matches no cells
93+
from the row with probability 1-p. (Here, the
94+
probability p is ``row_sample_filter``.)
95+
96+
:type family_name_regex_filter: str
97+
:param family_name_regex_filter: A regular expression (RE2) to match cells
98+
from columns in a given column family. For
99+
technical reasons, the regex must not
100+
contain the ``':'`` character, even if it
101+
isnot being uses as a literal.
102+
103+
:type column_qualifier_regex_filter: bytes
104+
:param column_qualifier_regex_filter: A regular expression (RE2) to match
105+
cells from column that match this
106+
regex (irrespective of column
107+
family).
108+
109+
:type value_regex_filter: bytes
110+
:param value_regex_filter: A regular expression (RE2) to match cells with
111+
values that match this regex.
112+
113+
:type cells_per_row_offset_filter: int
114+
:param cells_per_row_offset_filter: Skips the first N cells of the row.
115+
116+
:type cells_per_row_limit_filter: int
117+
:param cells_per_row_limit_filter: Matches only the first N cells of the
118+
row.
119+
120+
:type cells_per_column_limit_filter: int
121+
:param cells_per_column_limit_filter: Matches only the most recent N cells
122+
within each column. This filters a
123+
(family name, column) pair, based on
124+
timestamps of each cell.
125+
126+
:type strip_value_transformer: bool
127+
:param strip_value_transformer: If :data:`True`, replaces each cell's value
128+
with the empty string. As the name
129+
indicates, this is more useful as a
130+
transformer than a generic query / filter.
131+
132+
:type apply_label_transformer: str
133+
:param apply_label_transformer: Applies the given label to all cells in the
134+
output row. This allows the client to
135+
determine which results were produced from
136+
which part of the filter.
137+
138+
Values must be at most 15 characters long,
139+
and match the pattern [a-z0-9\\-]+.
140+
141+
Due to a technical limitation, it is not
142+
currently possible to apply multiple labels
143+
to a cell.
144+
145+
:raises: :class:`TypeError <exceptions.TypeError>` if not exactly one
146+
value set in the constructor.
147+
"""
148+
149+
def __init__(self,
150+
sink=None,
151+
pass_all_filter=None,
152+
block_all_filter=None,
153+
row_key_regex_filter=None,
154+
row_sample_filter=None,
155+
family_name_regex_filter=None,
156+
column_qualifier_regex_filter=None,
157+
value_regex_filter=None,
158+
cells_per_row_offset_filter=None,
159+
cells_per_row_limit_filter=None,
160+
cells_per_column_limit_filter=None,
161+
strip_value_transformer=None,
162+
apply_label_transformer=None):
163+
self.sink = sink
164+
self.pass_all_filter = pass_all_filter
165+
self.block_all_filter = block_all_filter
166+
self.row_key_regex_filter = row_key_regex_filter
167+
self.row_sample_filter = row_sample_filter
168+
self.family_name_regex_filter = family_name_regex_filter
169+
self.column_qualifier_regex_filter = column_qualifier_regex_filter
170+
self.value_regex_filter = value_regex_filter
171+
self.cells_per_row_offset_filter = cells_per_row_offset_filter
172+
self.cells_per_row_limit_filter = cells_per_row_limit_filter
173+
self.cells_per_column_limit_filter = cells_per_column_limit_filter
174+
self.strip_value_transformer = strip_value_transformer
175+
self.apply_label_transformer = apply_label_transformer
176+
self._check_single_value()
177+
178+
def _check_single_value(self):
179+
"""Checks that exactly one value is set on the instance.
180+
181+
:raises: :class:`TypeError <exceptions.TypeError>` if not exactly one
182+
value set on the instance.
183+
"""
184+
values_set = (
185+
int(self.sink is not None) +
186+
int(self.pass_all_filter is not None) +
187+
int(self.block_all_filter is not None) +
188+
int(self.row_key_regex_filter is not None) +
189+
int(self.row_sample_filter is not None) +
190+
int(self.family_name_regex_filter is not None) +
191+
int(self.column_qualifier_regex_filter is not None) +
192+
int(self.value_regex_filter is not None) +
193+
int(self.cells_per_row_offset_filter is not None) +
194+
int(self.cells_per_row_limit_filter is not None) +
195+
int(self.cells_per_column_limit_filter is not None) +
196+
int(self.strip_value_transformer is not None) +
197+
int(self.apply_label_transformer is not None)
198+
)
199+
if values_set != 1:
200+
raise TypeError('Exactly one value must be set in a row filter')
201+
202+
def __eq__(self, other):
203+
if not isinstance(other, self.__class__):
204+
return False
205+
return (
206+
other.sink == self.sink and
207+
other.pass_all_filter == self.pass_all_filter and
208+
other.block_all_filter == self.block_all_filter and
209+
other.row_key_regex_filter == self.row_key_regex_filter and
210+
other.row_sample_filter == self.row_sample_filter and
211+
other.family_name_regex_filter == self.family_name_regex_filter and
212+
(other.column_qualifier_regex_filter ==
213+
self.column_qualifier_regex_filter) and
214+
other.value_regex_filter == self.value_regex_filter and
215+
(other.cells_per_row_offset_filter ==
216+
self.cells_per_row_offset_filter) and
217+
(other.cells_per_row_limit_filter ==
218+
self.cells_per_row_limit_filter) and
219+
(other.cells_per_column_limit_filter ==
220+
self.cells_per_column_limit_filter) and
221+
other.strip_value_transformer == self.strip_value_transformer and
222+
other.apply_label_transformer == self.apply_label_transformer
223+
)
224+
225+
def __ne__(self, other):
226+
return not self.__eq__(other)
227+
# pylint: enable=too-many-arguments

‎gcloud/bigtable/test_row.py‎

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,145 @@ def test_constructor_with_non_bytes(self):
4646
row_key = object()
4747
with self.assertRaises(TypeError):
4848
self._makeOne(row_key, None)
49+
50+
51+
class TestRowFilter(unittest2.TestCase):
52+
53+
_PROPERTIES = (
54+
'sink',
55+
'pass_all_filter',
56+
'block_all_filter',
57+
'row_key_regex_filter',
58+
'row_sample_filter',
59+
'family_name_regex_filter',
60+
'column_qualifier_regex_filter',
61+
'value_regex_filter',
62+
'cells_per_row_offset_filter',
63+
'cells_per_row_limit_filter',
64+
'cells_per_column_limit_filter',
65+
'strip_value_transformer',
66+
'apply_label_transformer',
67+
)
68+
69+
def _getTargetClass(self):
70+
from gcloud.bigtable.row import RowFilter
71+
return RowFilter
72+
73+
def _makeOne(self, *args, **kwargs):
74+
return self._getTargetClass()(*args, **kwargs)
75+
76+
def test_constructor_defaults(self):
77+
# Fails unless exactly one property is passed.
78+
with self.assertRaises(TypeError):
79+
self._makeOne()
80+
81+
def test_constructor_too_many_values(self):
82+
# Fails unless exactly one property is passed.
83+
with self.assertRaises(TypeError):
84+
self._makeOne(row_key_regex_filter=b'value',
85+
cells_per_column_limit_filter=10)
86+
87+
def _row_filter_values_check(self, row_filter, value_name, value):
88+
for prop_name in self._PROPERTIES:
89+
prop_value = getattr(row_filter, prop_name)
90+
if prop_name == value_name:
91+
self.assertEqual(prop_value, value)
92+
else:
93+
self.assertTrue(prop_value is None)
94+
95+
def test_constructor_sink(self):
96+
value = True
97+
row_filter = self._makeOne(sink=value)
98+
self._row_filter_values_check(row_filter, 'sink', value)
99+
100+
def test_constructor_pass_all_filter(self):
101+
value = True
102+
row_filter = self._makeOne(pass_all_filter=value)
103+
self._row_filter_values_check(row_filter, 'pass_all_filter', value)
104+
105+
def test_constructor_block_all_filter(self):
106+
value = True
107+
row_filter = self._makeOne(block_all_filter=value)
108+
self._row_filter_values_check(row_filter, 'block_all_filter', value)
109+
110+
def test_constructor_row_key_regex_filter(self):
111+
value = b'row-key-regex'
112+
row_filter = self._makeOne(row_key_regex_filter=value)
113+
self._row_filter_values_check(
114+
row_filter, 'row_key_regex_filter', value)
115+
116+
def test_constructor_row_sample_filter(self):
117+
value = 0.25
118+
row_filter = self._makeOne(row_sample_filter=value)
119+
self._row_filter_values_check(row_filter, 'row_sample_filter', value)
120+
121+
def test_constructor_family_name_regex_filter(self):
122+
value = u'family-regex'
123+
row_filter = self._makeOne(family_name_regex_filter=value)
124+
self._row_filter_values_check(
125+
row_filter, 'family_name_regex_filter', value)
126+
127+
def test_constructor_column_qualifier_regex_filter(self):
128+
value = b'column-regex'
129+
row_filter = self._makeOne(column_qualifier_regex_filter=value)
130+
self._row_filter_values_check(
131+
row_filter, 'column_qualifier_regex_filter', value)
132+
133+
def test_constructor_value_regex_filter(self):
134+
value = b'value-regex'
135+
row_filter = self._makeOne(value_regex_filter=value)
136+
self._row_filter_values_check(row_filter, 'value_regex_filter', value)
137+
138+
def test_constructor_cells_per_row_offset_filter(self):
139+
value = 76
140+
row_filter = self._makeOne(cells_per_row_offset_filter=value)
141+
self._row_filter_values_check(
142+
row_filter, 'cells_per_row_offset_filter', value)
143+
144+
def test_constructor_cells_per_row_limit_filter(self):
145+
value = 189
146+
row_filter = self._makeOne(cells_per_row_limit_filter=value)
147+
self._row_filter_values_check(
148+
row_filter, 'cells_per_row_limit_filter', value)
149+
150+
def test_constructor_cells_per_column_limit_filter(self):
151+
value = 10
152+
row_filter = self._makeOne(cells_per_column_limit_filter=value)
153+
self._row_filter_values_check(
154+
row_filter, 'cells_per_column_limit_filter', value)
155+
156+
def test_constructor_strip_value_transformer(self):
157+
value = True
158+
row_filter = self._makeOne(strip_value_transformer=value)
159+
self._row_filter_values_check(row_filter, 'strip_value_transformer',
160+
value)
161+
162+
def test_constructor_apply_label_transformer(self):
163+
value = u'label'
164+
row_filter = self._makeOne(apply_label_transformer=value)
165+
self._row_filter_values_check(row_filter, 'apply_label_transformer',
166+
value)
167+
168+
def test___eq__(self):
169+
# Fool the constructor by passing exactly 1 value.
170+
row_filter1 = self._makeOne(strip_value_transformer=True)
171+
row_filter2 = self._makeOne(strip_value_transformer=True)
172+
# Set every value so we can compare them all.
173+
for prop_name in self._PROPERTIES:
174+
fake_val = object()
175+
setattr(row_filter1, prop_name, fake_val)
176+
setattr(row_filter2, prop_name, fake_val)
177+
self.assertEqual(row_filter1, row_filter2)
178+
179+
def test___eq__type_differ(self):
180+
# Fool the constructor by passing exactly 1 value.
181+
row_filter1 = self._makeOne(strip_value_transformer=True)
182+
row_filter2 = object()
183+
self.assertNotEqual(row_filter1, row_filter2)
184+
185+
def test___ne__same_value(self):
186+
# Fool the constructor by passing exactly 1 value.
187+
row_filter1 = self._makeOne(strip_value_transformer=True)
188+
row_filter2 = self._makeOne(strip_value_transformer=True)
189+
comparison_val = (row_filter1 != row_filter2)
190+
self.assertFalse(comparison_val)

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL