Skip to content
Open
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
@@ -0,0 +1,111 @@
"""
Tests for $expr argument shapes and expression evaluation.

Covers valid argument forms (field references, comparison expressions,
deeply nested operators), system variables ($$ROOT, $$CURRENT, $literal),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add $$NOW since its also system var?

computed result truthiness, and constant expression evaluation.
"""

import pytest

from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import (
QueryTestCase,
)
from documentdb_tests.framework.assertions import assertResult
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

ALL_TESTS: list[QueryTestCase] = [
QueryTestCase(
id="field_ref_truthy",
doc=[{"_id": 1, "a": 5}],
filter={"$expr": "$a"},
expected=[{"_id": 1, "a": 5}],
msg="non-zero field value is truthy",
),
QueryTestCase(
id="field_ref_falsy",
doc=[{"_id": 1, "a": 0}],
filter={"$expr": "$a"},
expected=[],
msg="zero field value is falsy",
),
QueryTestCase(
id="field_ref_missing",
doc=[{"_id": 1, "b": 1}],
filter={"$expr": "$a"},
expected=[],
msg="missing field is falsy",
),
QueryTestCase(
id="comparison_two_fields",
doc=[{"_id": 1, "a": 5, "b": 3}, {"_id": 2, "a": 1, "b": 3}],
filter={"$expr": {"$gt": ["$a", "$b"]}},
expected=[{"_id": 1, "a": 5, "b": 3}],
msg="$expr with comparison expression",
),
QueryTestCase(
id="deeply_nested_operators",
doc=[{"_id": 1, "a": 4.5}, {"_id": 2, "a": 1.2}],
filter={"$expr": {"$gt": [{"$add": [1, {"$abs": {"$ceil": "$a"}}]}, 5]}},
expected=[{"_id": 1, "a": 4.5}],
msg="$expr with deeply nested expression operators",
),
QueryTestCase(
id="system_var_root",
doc=[{"_id": 1, "a": 1}],
filter={"$expr": {"$eq": [{"$type": "$$ROOT"}, "object"]}},
expected=[{"_id": 1, "a": 1}],
msg="$expr with $$ROOT system variable",
),
QueryTestCase(
id="system_var_current",
doc=[{"_id": 1, "a": 5}, {"_id": 2, "a": -1}],
filter={"$expr": {"$gt": ["$$CURRENT.a", 0]}},
expected=[{"_id": 1, "a": 5}],
msg="$expr with $$CURRENT system variable",
),
QueryTestCase(
id="literal_dollar_string",
doc=[{"_id": 1, "a": "$hello"}, {"_id": 2, "a": "world"}],
filter={"$expr": {"$eq": [{"$literal": "$hello"}, "$a"]}},
expected=[{"_id": 1, "a": "$hello"}],
msg="$expr with $literal preserving dollar-prefixed string",
),
QueryTestCase(
id="computed_zero_falsy",
doc=[{"_id": 1, "arr": [0]}],
filter={"$expr": {"$arrayElemAt": ["$arr", 0]}},
expected=[],
msg="computed zero from $arrayElemAt is falsy",
),
QueryTestCase(
id="computed_missing_falsy",
doc=[{"_id": 1, "arr": []}],
filter={"$expr": {"$arrayElemAt": ["$arr", 0]}},
expected=[],
msg="$arrayElemAt on empty array returns missing (falsy)",
),
QueryTestCase(
id="constant_eq_true",
doc=[{"_id": 1, "a": 1}],
filter={"$expr": {"$eq": [1, 1]}},
expected=[{"_id": 1, "a": 1}],
msg="$expr evaluates constant expression — always true",
),
QueryTestCase(
id="constant_add_truthy",
doc=[{"_id": 1, "a": 1}],
filter={"$expr": {"$add": [1, 2]}},
expected=[{"_id": 1, "a": 1}],
msg="$expr truthiness on computed non-zero result",
),
]


@pytest.mark.parametrize("test", pytest_params(ALL_TESTS))
def test_expr_argument_handling(collection, test):
"""Test $expr argument shapes and expression evaluation."""
collection.insert_many(test.doc)
result = execute_command(collection, {"find": collection.name, "filter": test.filter})
assertResult(result, expected=test.expected, error_code=test.error_code, msg=test.msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Tests for $expr edge cases and unusual expression forms.

Covers empty object/array as $expr argument, $or mixing regular query
and $expr, field path through array of objects, and $$REMOVE behavior.
"""

import pytest

from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import (
QueryTestCase,
)
from documentdb_tests.framework.assertions import assertResult
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

ALL_TESTS: list[QueryTestCase] = [
QueryTestCase(
id="expr_empty_object",
doc=[{"_id": 1}],
filter={"$expr": {}},
expected=[{"_id": 1}],
msg="$expr with empty object {} — truthy, matches all",
),
QueryTestCase(
id="expr_empty_array",
doc=[{"_id": 1}],
filter={"$expr": []},
expected=[{"_id": 1}],
msg="$expr with empty array [] — truthy, matches all",
),
QueryTestCase(
id="or_regular_and_expr",
doc=[
{"_id": 1, "a": 1, "b": 5, "c": 3},
{"_id": 2, "a": 2, "b": 1, "c": 5},
{"_id": 3, "a": 3, "b": 1, "c": 1},
],
filter={"$or": [{"a": 1}, {"$expr": {"$gt": ["$b", "$c"]}}]},
expected=[{"_id": 1, "a": 1, "b": 5, "c": 3}],
msg="$or mixing regular query and $expr",
),
QueryTestCase(
id="field_path_through_array",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a case with $expr with a field path traversing an array whose elements are scalars (not objects), to cover array-flattening behavior.

doc=[{"_id": 1, "items": [{"price": 50}, {"price": 150}]}],
filter={"$expr": {"$in": [150, "$items.price"]}},
expected=[{"_id": 1, "items": [{"price": 50}, {"price": 150}]}],
msg="$expr with field path through array of objects",
),
QueryTestCase(
id="cond_with_remove",
doc=[{"_id": 1, "a": 5}, {"_id": 2, "a": -1}],
filter={"$expr": {"$cond": [{"$gt": ["$a", 0]}, "$a", "$$REMOVE"]}},
expected=[{"_id": 1, "a": 5}],
msg="$expr with $$REMOVE in $cond false branch — falsy",
),
QueryTestCase(
id="bare_remove_falsy",
doc=[{"_id": 1}],
filter={"$expr": "$$REMOVE"},
expected=[],
msg="$expr with bare $$REMOVE — falsy, no documents match",
),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing: $expr inside $not, e.g. {"$expr": {"$not": [{"$gt": ["$a", 0]}]}}


@pytest.mark.parametrize("test", pytest_params(ALL_TESTS))
def test_expr_complex(collection, test):
"""Test $expr edge cases and unusual expressions."""
collection.insert_many(test.doc)
result = execute_command(collection, {"find": collection.name, "filter": test.filter})
assertResult(result, expected=test.expected, error_code=test.error_code, msg=test.msg)
Loading
Loading