Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ def _translate_filter(
return "false" if operator == "eq" else f"IS_DEFINED({access})"
parameter = _add_parameter(parameters, value)
if operator == "eq":
return f"(IS_DEFINED({access}) AND {access} = {parameter})"
return f"(IS_DEFINED({access}) AND NOT IS_NULL({access}) AND {access} = {parameter})"
return f"(IS_DEFINED({access}) AND (IS_NULL({access}) OR {access} != {parameter}))"
if operator in ("gt", "gte", "lt", "lte", "between"):
if field.type_ not in ("str", "int", "float"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,14 @@ async def test_full_batch_item_size_and_json_preflight() -> None:
),
(
Filter("text", "eq", "O'Reilly"),
'(IS_DEFINED(c["content"]) AND c["content"] = @filter_0)',
'(IS_DEFINED(c["content"]) AND NOT IS_NULL(c["content"]) AND c["content"] = @filter_0)',
["O'Reilly"],
),
(
FilterGroup("not", [Filter("optional", "eq", "value")]),
'(NOT (IS_DEFINED(c["optional"]) AND NOT IS_NULL(c["optional"]) AND c["optional"] = @filter_0))',
["value"],
),
(
Filter("text", "ne", "x"),
'(IS_DEFINED(c["content"]) AND (IS_NULL(c["content"]) OR c["content"] != @filter_0))',
Expand Down Expand Up @@ -765,7 +770,7 @@ async def test_full_batch_item_size_and_json_preflight() -> None:
],
)
def test_filter_translation_matches_portable_semantics(
expression: Filter,
expression: Filter | FilterGroup,
clause: str,
values: list[Any],
) -> None:
Expand All @@ -790,7 +795,7 @@ def test_filter_groups_and_rejections() -> None:
clause, parameters = collection._prepare_filter(group)
assert clause == (
'((IS_DEFINED(c["count"]) AND NOT IS_NULL(c["count"]) AND c["count"] >= @filter_0) '
'AND (NOT (IS_DEFINED(c["active"]) AND c["active"] = @filter_1)))'
'AND (NOT (IS_DEFINED(c["active"]) AND NOT IS_NULL(c["active"]) AND c["active"] = @filter_1)))'
)
assert [item["value"] for item in parameters] == [1, False]
for expression, exception in (
Expand Down Expand Up @@ -958,7 +963,7 @@ async def test_filtered_get_query_is_parameterized_and_bounded() -> None:
kwargs = container.query_items.call_args.kwargs
query = kwargs["query"]
assert "hello" not in query
assert 'WHERE (IS_DEFINED(c["content"]) AND c["content"] = @filter_0)' in query
assert 'WHERE (IS_DEFINED(c["content"]) AND NOT IS_NULL(c["content"]) AND c["content"] = @filter_0)' in query
assert 'ORDER BY c["count"] DESC OFFSET @skip LIMIT @top' in query
assert kwargs["parameters"] == [
{"name": "@filter_0", "value": "hello"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ async def test_disposable_vector_database_crud_filter_and_search() -> None:
}
assert await default_collection.get(filter=Filter("count", "eq", True)) == []
assert await default_collection.get(filter=Filter("active", "eq", 1)) == []
not_value = FilterGroup("not", [Filter("optional", "eq", "value")])
assert {item["key"] for item in await default_collection.get(filter=not_value)} == {"one", "three"}

missing_optional = {**multi_records[0], "id": "missing"}
missing_optional.pop("optional")
Expand Down
Loading