Skip to content

Commit

Permalink
Adding Bigtable RowFilter base class.
Browse files Browse the repository at this point in the history
Also adding regex filters for a row key (bytes) and a
family name (string).

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

In addition to more classes for primitive properties, parent classes are
forthcoming to handle the non-primitive cases of filter Chain, Interleave
and Condition (ternary).

Also renaming some redunant unit test names in test_column_family.py and
ditching use of NotImplementedError in bigtable base classes (for
both GC rule and row filter).
  • Loading branch information
dhermes committed Dec 17, 2015
1 parent 1e2d80a commit e3c046f
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 33 deletions.
8 changes: 0 additions & 8 deletions gcloud/bigtable/column_family.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,6 @@ class GarbageCollectionRule(object):
don't support that feature and instead support via native classes.
"""

def to_pb(self):
"""Converts the :class:`GarbageCollectionRule` to a protobuf.
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
always since a virtual class.
"""
raise NotImplementedError

def __ne__(self, other):
return not self.__eq__(other)

Expand Down
93 changes: 93 additions & 0 deletions gcloud/bigtable/row.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@


from gcloud._helpers import _to_bytes
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2


class Row(object):
Expand All @@ -31,3 +32,95 @@ class Row(object):
def __init__(self, row_key, table):
self._row_key = _to_bytes(row_key)
self._table = table


class RowFilter(object):
"""Basic filter to apply to cells in a row.
These values can be combined via :class:`RowFilterChain`,
:class:`RowFilterUnion` and :class:`ConditionalRowFilter`.
.. note::
This class is a do-nothing base class for all row filters.
"""

def __ne__(self, other):
return not self.__eq__(other)


class _RegexFilter(RowFilter):
"""Row filter that uses a regular expression.
The ``regex`` must be valid RE2 patterns. See Google's
`RE2 reference`_ for the accepted syntax.
.. _RE2 reference: https://github.com/google/re2/wiki/Syntax
:type regex: bytes or str
:param regex: A regular expression (RE2) for some row filter.
"""

def __init__(self, regex):
self.regex = regex

def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return other.regex == self.regex


class RowKeyRegexFilter(_RegexFilter):
"""Row filter for a row key regular expression.
The ``regex`` must be valid RE2 patterns. See Google's
`RE2 reference`_ for the accepted syntax.
.. _RE2 reference: https://github.com/google/re2/wiki/Syntax
.. note::
Special care need be used with the expression used. Since
each of these properties can contain arbitrary bytes, the ``\\C``
escape sequence must be used if a true wildcard is desired. The ``.``
character will not match the new line character ``\\n``, which may be
present in a binary value.
:type regex: bytes
:param regex: A regular expression (RE2) to match cells from rows with row
keys that satisfy this regex. For a
``CheckAndMutateRowRequest``, this filter is unnecessary
since the row key is already specified.
"""

def to_pb(self):
"""Converts the row filter to a protobuf.
:rtype: :class:`.data_pb2.RowFilter`
:returns: The converted current object.
"""
return data_pb2.RowFilter(row_key_regex_filter=self.regex)


class FamilyNameRegexFilter(_RegexFilter):
"""Row filter for a family name regular expression.
The ``regex`` must be valid RE2 patterns. See Google's
`RE2 reference`_ for the accepted syntax.
.. _RE2 reference: https://github.com/google/re2/wiki/Syntax
:type regex: str
:param regex: A regular expression (RE2) to match cells from columns in a
given column family. For technical reasons, the regex must
not contain the ``':'`` character, even if it is not being
used as a literal.
"""

def to_pb(self):
"""Converts the row filter to a protobuf.
:rtype: :class:`.data_pb2.RowFilter`
:returns: The converted current object.
"""
return data_pb2.RowFilter(family_name_regex_filter=self.regex)
36 changes: 11 additions & 25 deletions gcloud/bigtable/test_column_family.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,6 @@ def test_with_negative_seconds(self):
self.assertEqual(result.nanos, -(10**9 - 1000 * microseconds))


class TestGarbageCollectionRule(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.bigtable.column_family import GarbageCollectionRule
return GarbageCollectionRule

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_to_pb_virtual(self):
gc_rule = self._makeOne()
self.assertRaises(NotImplementedError, gc_rule.to_pb)


class TestMaxVersionsGCRule(unittest2.TestCase):

def _getTargetClass(self):
Expand All @@ -84,16 +70,16 @@ def _getTargetClass(self):
def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test___eq__max_num_versions(self):
gc_rule1 = self._makeOne(2)
gc_rule2 = self._makeOne(2)
self.assertEqual(gc_rule1, gc_rule2)

def test___eq__type_differ(self):
gc_rule1 = self._makeOne(10)
gc_rule2 = object()
self.assertNotEqual(gc_rule1, gc_rule2)

def test___eq__same_value(self):
gc_rule1 = self._makeOne(2)
gc_rule2 = self._makeOne(2)
self.assertEqual(gc_rule1, gc_rule2)

def test___ne__same_value(self):
gc_rule1 = self._makeOne(99)
gc_rule2 = self._makeOne(99)
Expand All @@ -119,18 +105,18 @@ def _getTargetClass(self):
def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test___eq__max_age(self):
max_age = object()
gc_rule1 = self._makeOne(max_age=max_age)
gc_rule2 = self._makeOne(max_age=max_age)
self.assertEqual(gc_rule1, gc_rule2)

def test___eq__type_differ(self):
max_age = object()
gc_rule1 = self._makeOne(max_age=max_age)
gc_rule2 = object()
self.assertNotEqual(gc_rule1, gc_rule2)

def test___eq__same_value(self):
max_age = object()
gc_rule1 = self._makeOne(max_age=max_age)
gc_rule2 = self._makeOne(max_age=max_age)
self.assertEqual(gc_rule1, gc_rule2)

def test___ne__same_value(self):
max_age = object()
gc_rule1 = self._makeOne(max_age=max_age)
Expand Down
72 changes: 72 additions & 0 deletions gcloud/bigtable/test_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,75 @@ def test_constructor_with_non_bytes(self):
row_key = object()
with self.assertRaises(TypeError):
self._makeOne(row_key, None)


class Test_RegexFilter(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.bigtable.row import _RegexFilter
return _RegexFilter

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_constructor(self):
regex = object()
row_filter = self._makeOne(regex)
self.assertTrue(row_filter.regex is regex)

def test___eq__type_differ(self):
regex = object()
row_filter1 = self._makeOne(regex=regex)
row_filter2 = object()
self.assertNotEqual(row_filter1, row_filter2)

def test___eq__same_value(self):
regex = object()
row_filter1 = self._makeOne(regex=regex)
row_filter2 = self._makeOne(regex=regex)
self.assertEqual(row_filter1, row_filter2)

def test___ne__same_value(self):
regex = object()
row_filter1 = self._makeOne(regex=regex)
row_filter2 = self._makeOne(regex=regex)
comparison_val = (row_filter1 != row_filter2)
self.assertFalse(comparison_val)


class TestRowKeyRegexFilter(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.bigtable.row import RowKeyRegexFilter
return RowKeyRegexFilter

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_to_pb(self):
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2

regex = b'row-key-regex'
row_filter = self._makeOne(regex)
pb_val = row_filter.to_pb()
expected_pb = data_pb2.RowFilter(row_key_regex_filter=regex)
self.assertEqual(pb_val, expected_pb)


class TestFamilyNameRegexFilter(unittest2.TestCase):

def _getTargetClass(self):
from gcloud.bigtable.row import FamilyNameRegexFilter
return FamilyNameRegexFilter

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_to_pb(self):
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2

regex = u'family-regex'
row_filter = self._makeOne(regex)
pb_val = row_filter.to_pb()
expected_pb = data_pb2.RowFilter(family_name_regex_filter=regex)
self.assertEqual(pb_val, expected_pb)

0 comments on commit e3c046f

Please sign in to comment.