Skip to content

Commit

Permalink
feat: Use server side != for queries. (#950)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorced-jim authored Feb 27, 2024
1 parent dab9edf commit 106772f
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 21 deletions.
2 changes: 2 additions & 0 deletions google/cloud/ndb/_datastore_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
13 changes: 3 additions & 10 deletions google/cloud/ndb/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 "
Expand Down
13 changes: 5 additions & 8 deletions tests/unit/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__():
Expand Down
14 changes: 11 additions & 3 deletions tests/unit/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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()

Expand Down

0 comments on commit 106772f

Please sign in to comment.