From 106772f031f6c37500a0d463698e59008f9bf19a Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Tue, 27 Feb 2024 14:29:20 -0800 Subject: [PATCH] feat: Use server side != for queries. (#950) --- google/cloud/ndb/_datastore_query.py | 2 ++ google/cloud/ndb/query.py | 13 +++---------- tests/unit/test_model.py | 13 +++++-------- tests/unit/test_query.py | 14 +++++++++++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/google/cloud/ndb/_datastore_query.py b/google/cloud/ndb/_datastore_query.py index 90c32ba1..553c8285 100644 --- a/google/cloud/ndb/_datastore_query.py +++ b/google/cloud/ndb/_datastore_query.py @@ -56,6 +56,8 @@ "<=": query_pb2.PropertyFilter.Operator.LESS_THAN_OR_EQUAL, ">": query_pb2.PropertyFilter.Operator.GREATER_THAN, ">=": query_pb2.PropertyFilter.Operator.GREATER_THAN_OR_EQUAL, + "!=": query_pb2.PropertyFilter.Operator.NOT_EQUAL, + "IN": query_pb2.PropertyFilter.Operator.IN, } _KEY_NOT_IN_CACHE = object() diff --git a/google/cloud/ndb/query.py b/google/cloud/ndb/query.py index 6a599630..7fa46706 100644 --- a/google/cloud/ndb/query.py +++ b/google/cloud/ndb/query.py @@ -604,8 +604,6 @@ class FilterNode(Node): The constructor for this type may not always return a :class:`FilterNode`. For example: - * The filter ``name != value`` is converted into - ``(name > value) OR (name < value)`` (a :class:`DisjunctionNode`) * The filter ``name in (value1, ..., valueN)`` is converted into ``(name = value1) OR ... OR (name = valueN)`` (also a :class:`DisjunctionNode`) @@ -639,11 +637,6 @@ def __new__(cls, name, opsymbol, value): if isinstance(value, model.Key): value = value._key - if opsymbol == _NE_OP: - node1 = FilterNode(name, _LT_OP, value) - node2 = FilterNode(name, _GT_OP, value) - return DisjunctionNode(node1, node2) - if opsymbol == _IN_OP: if not isinstance(value, (list, tuple, set, frozenset)): raise TypeError( @@ -704,17 +697,17 @@ def _to_filter(self, post=False): representation of the filter. Raises: - NotImplementedError: If the ``opsymbol`` is ``!=`` or ``in``, since + NotImplementedError: If the ``opsymbol`` is ``in``, since they should correspond to a composite filter. This should never occur since the constructor will create ``OR`` nodes for - ``!=`` and ``in`` + ``in`` """ # Avoid circular import in Python 2.7 from google.cloud.ndb import _datastore_query if post: return None - if self._opsymbol in (_NE_OP, _IN_OP): + if self._opsymbol in (_IN_OP): raise NotImplementedError( "Inequality filters are not single filter " "expressions and therefore cannot be converted " diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 5e6a11cb..3250d22d 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -479,15 +479,12 @@ def test___eq__(): def test___ne__(): prop = model.Property("name", indexed=True) value = 7.0 - expected = query_module.DisjunctionNode( - query_module.FilterNode("name", "<", value), - query_module.FilterNode("name", ">", value), - ) + expected = query_module.FilterNode("name", "!=", value) - or_node_left = prop != value - assert or_node_left == expected - or_node_right = value != prop - assert or_node_right == expected + ne_node_left = prop != value + assert ne_node_left == expected + ne_node_right = value != prop + assert ne_node_right == expected @staticmethod def test___lt__(): diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py index df7df55a..589c9bcc 100644 --- a/tests/unit/test_query.py +++ b/tests/unit/test_query.py @@ -656,11 +656,14 @@ def test_constructor_in_invalid_container(): @staticmethod def test_constructor_ne(): - or_node = query_module.FilterNode("a", "!=", 2.5) + ne_node = query_module.FilterNode("a", "!=", 2.5) filter_node1 = query_module.FilterNode("a", "<", 2.5) filter_node2 = query_module.FilterNode("a", ">", 2.5) - assert or_node == query_module.DisjunctionNode(filter_node1, filter_node2) + assert ne_node != query_module.DisjunctionNode(filter_node1, filter_node2) + assert ne_node._value == 2.5 + assert ne_node._opsymbol == "!=" + assert ne_node._name == "a" @staticmethod def test_pickling(): @@ -693,10 +696,15 @@ def test__to_filter_post(): filter_node = query_module.FilterNode("speed", ">=", 88) assert filter_node._to_filter(post=True) is None + @staticmethod + def test__to_ne_filter_op(): + filter_node = query_module.FilterNode("speed", "!=", 88) + assert filter_node._to_filter(post=True) is None + @staticmethod def test__to_filter_bad_op(): filter_node = query_module.FilterNode("speed", ">=", 88) - filter_node._opsymbol = "!=" + filter_node._opsymbol = "in" with pytest.raises(NotImplementedError): filter_node._to_filter()