Skip to content

Commit 014c160

Browse files
committed
Implementing Bigtable ConditionalRowFilter.
This is a ``Condition`` filter, which is effectively a ternary operator (predicate determines if the true or false filter is applied). [1]: https://github.com/GoogleCloudPlatform/gcloud-python/blob/9e2b07d92bbbf8640a3a767ccf4796245ba5b51c/gcloud/bigtable/_generated/_bigtable_data.proto#L232-L245
1 parent 9e2b07d commit 014c160

2 files changed

Lines changed: 172 additions & 0 deletions

File tree

gcloud/bigtable/row.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,60 @@ def to_pb(self):
652652
:returns: The converted current object.
653653
"""
654654
return data_pb2.RowFilter(apply_label_transformer=self.label)
655+
656+
657+
class ConditionalRowFilter(RowFilter):
658+
"""Conditional row filter which exhibits ternary behavior.
659+
660+
Executes one of two filters based on another filter. If the ``base_filter``
661+
returns any cells in the row, then ``true_filter`` is executed. If not,
662+
then ``false_filter`` is executed.
663+
664+
.. note::
665+
666+
The ``base_filter`` does not execute atomically with the true and false
667+
filters, which may lead to inconsistent or unexpected results.
668+
669+
Additionally, executing a :class:`ConditionalRowFilter` has poor
670+
performance on the server, especially when ``false_filter`` is set.
671+
672+
:type base_filter: :class:`RowFilter`
673+
:param base_filter: The filter to condition on before executing the
674+
true/false filters.
675+
676+
:type true_filter: :class:`RowFilter`
677+
:param true_filter: (Optional) The filter to execute if there are any cells
678+
matching ``base_filter``. If not provided, no results
679+
will be returned in the true case.
680+
681+
:type false_filter: :class:`RowFilter`
682+
:param false_filter: (Optional) The filter to execute if there are no cells
683+
matching ``base_filter``. If not provided, no results
684+
will be returned in the false case.
685+
"""
686+
687+
def __init__(self, base_filter, true_filter=None, false_filter=None):
688+
self.base_filter = base_filter
689+
self.true_filter = true_filter
690+
self.false_filter = false_filter
691+
692+
def __eq__(self, other):
693+
if not isinstance(other, self.__class__):
694+
return False
695+
return (other.base_filter == self.base_filter and
696+
other.true_filter == self.true_filter and
697+
other.false_filter == self.false_filter)
698+
699+
def to_pb(self):
700+
"""Converts the row filter to a protobuf.
701+
702+
:rtype: :class:`.data_pb2.RowFilter`
703+
:returns: The converted current object.
704+
"""
705+
condition_kwargs = {'predicate_filter': self.base_filter.to_pb()}
706+
if self.true_filter is not None:
707+
condition_kwargs['true_filter'] = self.true_filter.to_pb()
708+
if self.false_filter is not None:
709+
condition_kwargs['false_filter'] = self.false_filter.to_pb()
710+
condition = data_pb2.RowFilter.Condition(**condition_kwargs)
711+
return data_pb2.RowFilter(condition=condition)

gcloud/bigtable/test_row.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,3 +777,118 @@ def test_to_pb(self):
777777
pb_val = row_filter.to_pb()
778778
expected_pb = data_pb2.RowFilter(apply_label_transformer=label)
779779
self.assertEqual(pb_val, expected_pb)
780+
781+
782+
class TestConditionalRowFilter(unittest2.TestCase):
783+
784+
def _getTargetClass(self):
785+
from gcloud.bigtable.row import ConditionalRowFilter
786+
return ConditionalRowFilter
787+
788+
def _makeOne(self, *args, **kwargs):
789+
return self._getTargetClass()(*args, **kwargs)
790+
791+
def test_constructor(self):
792+
base_filter = object()
793+
true_filter = object()
794+
false_filter = object()
795+
cond_filter = self._makeOne(base_filter,
796+
true_filter=true_filter,
797+
false_filter=false_filter)
798+
self.assertTrue(cond_filter.base_filter is base_filter)
799+
self.assertTrue(cond_filter.true_filter is true_filter)
800+
self.assertTrue(cond_filter.false_filter is false_filter)
801+
802+
def test___eq__(self):
803+
base_filter = object()
804+
true_filter = object()
805+
false_filter = object()
806+
cond_filter1 = self._makeOne(base_filter,
807+
true_filter=true_filter,
808+
false_filter=false_filter)
809+
cond_filter2 = self._makeOne(base_filter,
810+
true_filter=true_filter,
811+
false_filter=false_filter)
812+
self.assertEqual(cond_filter1, cond_filter2)
813+
814+
def test___eq__type_differ(self):
815+
base_filter = object()
816+
true_filter = object()
817+
false_filter = object()
818+
cond_filter1 = self._makeOne(base_filter,
819+
true_filter=true_filter,
820+
false_filter=false_filter)
821+
cond_filter2 = object()
822+
self.assertNotEqual(cond_filter1, cond_filter2)
823+
824+
def test_to_pb(self):
825+
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
826+
from gcloud.bigtable.row import CellsRowOffsetFilter
827+
from gcloud.bigtable.row import RowSampleFilter
828+
from gcloud.bigtable.row import StripValueTransformerFilter
829+
830+
row_filter1 = StripValueTransformerFilter(True)
831+
row_filter1_pb = row_filter1.to_pb()
832+
833+
row_filter2 = RowSampleFilter(0.25)
834+
row_filter2_pb = row_filter2.to_pb()
835+
836+
row_filter3 = CellsRowOffsetFilter(11)
837+
row_filter3_pb = row_filter3.to_pb()
838+
839+
row_filter4 = self._makeOne(row_filter1, true_filter=row_filter2,
840+
false_filter=row_filter3)
841+
filter_pb = row_filter4.to_pb()
842+
843+
expected_pb = data_pb2.RowFilter(
844+
condition=data_pb2.RowFilter.Condition(
845+
predicate_filter=row_filter1_pb,
846+
true_filter=row_filter2_pb,
847+
false_filter=row_filter3_pb,
848+
),
849+
)
850+
self.assertEqual(filter_pb, expected_pb)
851+
852+
def test_to_pb_true_only(self):
853+
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
854+
from gcloud.bigtable.row import RowSampleFilter
855+
from gcloud.bigtable.row import StripValueTransformerFilter
856+
857+
row_filter1 = StripValueTransformerFilter(True)
858+
row_filter1_pb = row_filter1.to_pb()
859+
860+
row_filter2 = RowSampleFilter(0.25)
861+
row_filter2_pb = row_filter2.to_pb()
862+
863+
row_filter3 = self._makeOne(row_filter1, true_filter=row_filter2)
864+
filter_pb = row_filter3.to_pb()
865+
866+
expected_pb = data_pb2.RowFilter(
867+
condition=data_pb2.RowFilter.Condition(
868+
predicate_filter=row_filter1_pb,
869+
true_filter=row_filter2_pb,
870+
),
871+
)
872+
self.assertEqual(filter_pb, expected_pb)
873+
874+
def test_to_pb_false_only(self):
875+
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
876+
from gcloud.bigtable.row import RowSampleFilter
877+
from gcloud.bigtable.row import StripValueTransformerFilter
878+
879+
row_filter1 = StripValueTransformerFilter(True)
880+
row_filter1_pb = row_filter1.to_pb()
881+
882+
row_filter2 = RowSampleFilter(0.25)
883+
row_filter2_pb = row_filter2.to_pb()
884+
885+
row_filter3 = self._makeOne(row_filter1, false_filter=row_filter2)
886+
filter_pb = row_filter3.to_pb()
887+
888+
expected_pb = data_pb2.RowFilter(
889+
condition=data_pb2.RowFilter.Condition(
890+
predicate_filter=row_filter1_pb,
891+
false_filter=row_filter2_pb,
892+
),
893+
)
894+
self.assertEqual(filter_pb, expected_pb)

0 commit comments

Comments
 (0)