diff --git a/gcloud/bigtable/column_family.py b/gcloud/bigtable/column_family.py index cc0cf611675a..411ba4f875ce 100644 --- a/gcloud/bigtable/column_family.py +++ b/gcloud/bigtable/column_family.py @@ -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 ` - always since a virtual class. - """ - raise NotImplementedError - def __ne__(self, other): return not self.__eq__(other) diff --git a/gcloud/bigtable/row.py b/gcloud/bigtable/row.py index b5eaac0f0239..219be8dbf6b3 100644 --- a/gcloud/bigtable/row.py +++ b/gcloud/bigtable/row.py @@ -16,6 +16,7 @@ from gcloud._helpers import _to_bytes +from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2 class Row(object): @@ -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) diff --git a/gcloud/bigtable/test_column_family.py b/gcloud/bigtable/test_column_family.py index 6182b8550136..67444fb6bb9a 100644 --- a/gcloud/bigtable/test_column_family.py +++ b/gcloud/bigtable/test_column_family.py @@ -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): @@ -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) @@ -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) diff --git a/gcloud/bigtable/test_row.py b/gcloud/bigtable/test_row.py index bd65819e3fec..7ed989c6ebc0 100644 --- a/gcloud/bigtable/test_row.py +++ b/gcloud/bigtable/test_row.py @@ -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)