From b56cabf42e0b130ac1a74bca68ec2e356b63247c Mon Sep 17 00:00:00 2001 From: Akshay Joshi Date: Tue, 26 May 2026 11:23:07 -0400 Subject: [PATCH] bigtable: add ValueBitmaskFilter for data client --- .../google/cloud/bigtable/data/row_filters.py | 31 +++++++++++++++++++ .../tests/unit/data/test_row_filters.py | 22 +++++++++++++ 2 files changed, 53 insertions(+) diff --git a/packages/google-cloud-bigtable/google/cloud/bigtable/data/row_filters.py b/packages/google-cloud-bigtable/google/cloud/bigtable/data/row_filters.py index 007a09f5f830..6ae59875e94e 100644 --- a/packages/google-cloud-bigtable/google/cloud/bigtable/data/row_filters.py +++ b/packages/google-cloud-bigtable/google/cloud/bigtable/data/row_filters.py @@ -484,6 +484,37 @@ def _to_dict(self) -> dict[str, bytes]: return {"value_regex_filter": self.regex} +class ValueBitmaskFilter(RowFilter): + """Row filter for a value bitmask. + + Matches only cells with values that satisfy the condition + ``(value & mask) == mask``. The mask length must exactly match the value + length, otherwise the cell is not considered a match. + + :type mask: bytes or str + :param mask: A bitmask to match against cell values. String values + will be encoded as ASCII. + """ + + def __init__(self, mask: bytes | str): + self.mask: bytes = _to_bytes(mask) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return other.mask == self.mask + + def __ne__(self, other): + return not self == other + + def _to_dict(self) -> dict[str, Any]: + """Converts the row filter to a dict representation.""" + return {"value_bitmask_filter": data_v2_pb2.ValueBitmask(mask=self.mask)} + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(mask={self.mask!r})" + + class LiteralValueFilter(ValueRegexFilter): """Row filter for an exact value. diff --git a/packages/google-cloud-bigtable/tests/unit/data/test_row_filters.py b/packages/google-cloud-bigtable/tests/unit/data/test_row_filters.py index 6be9b4a2b252..db518a56137e 100644 --- a/packages/google-cloud-bigtable/tests/unit/data/test_row_filters.py +++ b/packages/google-cloud-bigtable/tests/unit/data/test_row_filters.py @@ -1987,6 +1987,28 @@ def test_literal_value__write_literal_regex(input_arg, expected_bytes): assert filter_.regex == expected_bytes +class TestValueBitmaskFilter: + @staticmethod + def _target_class(): + from google.cloud.bigtable.data.row_filters import ValueBitmaskFilter + + return ValueBitmaskFilter + + def test_to_dict(self): + from google.cloud.bigtable_v2.types import data as data_v2_pb2 + + mask = b"\xaa" * 8 + row_filter = self._target_class()(mask) + expected = {"value_bitmask_filter": data_v2_pb2.ValueBitmask(mask=mask)} + assert row_filter._to_dict() == expected + + def test_to_pb(self): + mask = b"\xaa" * 8 + row_filter = self._target_class()(mask) + pb = row_filter._to_pb() + assert pb.value_bitmask_filter.mask == mask + + def _ColumnRangePB(*args, **kw): from google.cloud.bigtable_v2.types import data as data_v2_pb2