Add ALL and EXACTLY_ONE array filter operators for MongoDB and Postgres - #326
Merged
Conversation
lrathod
requested review from
avinashkolluru,
kotharironak,
puneet-traceable,
skjindal93,
suddendust and
suresh-prakash
as code owners
September 7, 2026 07:47
4 tasks
Add two new ArrayOperator values for filtering on array-valued attributes: - ALL: array attribute must contain every value specified in the filter - ONE: array attribute must contain exactly one element, and that element must be one of the specified values MongoDB: ALL uses $setIsSubset with an $ifNull guard; ONE combines $size == 1 with $in on the first element via $arrayElemAt. Postgres: native array columns use @> (ALL) and array_length + && (ONE); JSONB array paths use jsonb_typeof-guarded @> containment and jsonb_array_length respectively. Both parsers require the inner filter to carry a constant value list; non-constant RHS expressions throw UnsupportedOperationException since these are set-level operators, not per-element predicates like ANY. Co-authored-by: Cursor <cursoragent@cursor.com>
…mantics - Resolve native array element type from the compile-time type info on the field expression (ArrayIdentifierExpression/IdentifierExpression DataType) instead of inferring from filter values; value inference is now only a fallback when no type info is present. The runtime jsonb_typeof guard is retained only for schemaless JSONB/nested array paths. - Add unit + integration tests covering ALL/ONE on nested array fields (e.g. props.colors, scope.environmentScope.environmentIds) for both MongoDB and Postgres. - Add integration test documenting that ALL is set-containment: duplicates in the document array ([red, red] ALL [red]) still match in both backends. Co-authored-by: Cursor <cursoragent@cursor.com>
Consolidate the ALL/ONE integration tests into DocStoreQueryV1Test as a nested ArrayMatchAllOneOperatorTest class, per review feedback: - Nested JSONB array path (props.colors) covered on both MongoDB and Postgres via the shared document collection - Native array columns (tags TEXT[], flags BOOLEAN[]) covered on the flat collection with both typed (compile-time DataType) and untyped (value-inference fallback) ArrayIdentifierExpression variants - JSONB array column (props.colors) covered on the flat collection - Duplicate-containing arrays ([red, red] ALL [red] -> true) covered via a dedicated collection, documenting set-containment semantics on real DBs Co-authored-by: Cursor <cursoragent@cursor.com>
Negative coverage: - ALL/ONE reject a non-constant RHS with UnsupportedOperationException in both Mongo and Postgres parsers - Empty value lists are rejected at construction by ConstantExpression - Integration: non-array JSONB values do not match and do not error on Postgres, exercising the jsonb_typeof guard Semantics documentation via integration tests on both datastores: - ALL is order-independent: [red, blue] ALL [blue, red] matches - ALL/ONE on a three-level nested array field (props.metadata.colors), including docs with missing intermediate objects Co-authored-by: Cursor <cursoragent@cursor.com>
…ONE semantics - Native Postgres arrays: drop COALESCE - NULL arrays are excluded by WHERE semantics anyway, and the unwrapped column reference keeps the filter GIN-indexable (SARGable) - JSONB ONE: replace the per-value OR chain with a single <@ containment against the full filter list (with exactly one element, membership and containment are equivalent) - one bound param instead of N - Mongo: guard ALL/ONE with $cond/$isArray so documents holding a non-array scalar no longer error out ($setIsSubset/$size reject non-array operands), matching the Postgres jsonb_typeof behavior; subsumes $ifNull - Document that ONE counts raw elements, not distinct values ([red, red] ONE [red] is false), with an integration test on both stores; non-array scalar test now runs on Mongo too Co-authored-by: Cursor <cursoragent@cursor.com>
Aligns with the service-level MATCH_EXACTLY_ONE name and reads
unambiguously ("exactly one element, in the given set"). Also notes a
future EXACTLY (set-equality) operator in the enum javadoc.
Co-authored-by: Cursor <cursoragent@cursor.com>
[true,false] matches ids 5 and 8 (each a one-element flags array), not a parser bug; add [true]→1 to pin the singleton-true case. Co-authored-by: Cursor <cursoragent@cursor.com>
…fy IN semantics - Flat JSONB scalar no-match test (jsonb_typeof guard on the flat path) - Flat native TEXT[] variants of order-independence and duplicate-element tests (WITH_TYPE/WITHOUT_TYPE), mirroring array_match_test.json - Javadoc: inner IN is element membership; ALL = subset, EXACTLY_ONE = singleton whose element is in the RHS set - Flat-collection postgres parser unit tests Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Document (mongo + postgres) and flat native-array variants asserting that rows with missing/NULL or empty array fields never match, with positive controls so the tests are not vacuous. Co-authored-by: Cursor <cursoragent@cursor.com>
…_ONE length check jsonb @>/<@ are total on non-array scalars (false/NULL, never error), so the CASE guard was unnecessary for ALL and for the EXACTLY_ONE containment conjunct. jsonb_array_length raises on non-arrays and Postgres does not guarantee WHERE conjunct evaluation order, so the length check keeps its CASE guard. Co-authored-by: Cursor <cursoragent@cursor.com>
…top-level semantics Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
lrathod
force-pushed
the
ASP-3008/array-match-all-one-operators
branch
from
September 7, 2026 08:51
c884914 to
16ec81e
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #326 +/- ##
============================================
+ Coverage 81.06% 81.16% +0.10%
- Complexity 1617 1679 +62
============================================
Files 243 243
Lines 7656 7762 +106
Branches 755 769 +14
============================================
+ Hits 6206 6300 +94
- Misses 960 967 +7
- Partials 490 495 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… SHA) Co-authored-by: Cursor <cursoragent@cursor.com>
suddendust
approved these changes
Sep 7, 2026
suddendust
left a comment
Contributor
There was a problem hiding this comment.
LGTM. Review here: #323
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two new
ArrayOperatorvalues for filtering on array-valued attributes:[red, red]ALL[red]is true).[red, red]EXACTLY_ONE[red]is false).Both work on top-level and nested array fields (e.g.
props.colors,scope.environmentScope.environmentIds), on MongoDB and Postgres.MongoDB
ALL→{"$expr": {"$setIsSubset": [<values>, <guarded array>]}}EXACTLY_ONE→$andof$size == 1and$inon$arrayElemAt [path, 0]<guarded array>is{"$cond": [{"$isArray": "$path"}, "$path", []]}— documents holding a missing/null/non-array scalar value simply do not match instead of erroring ($setIsSubset/$sizereject non-array operands), matching the Postgres behaviour.Postgres
ALL→col @> ?;EXACTLY_ONE→array_length(col, 1) = 1 AND col && ?. NoCOALESCE— NULL arrays are excluded byWHEREsemantics anyway, and the unwrapped column reference keeps the filter GIN-indexable (SARGable).ALL→(CASE WHEN jsonb_typeof(path) = 'array' THEN path ELSE '[]'::jsonb END) @> ?::jsonb;EXACTLY_ONE→jsonb_array_length(<guarded>) = 1 AND <guarded> <@ ?::jsonb(single bound param — with exactly one element, membership ≡ containment). The runtimejsonb_typeofguard is retained only for schemaless JSONB paths.DataType(ArrayIdentifierExpression#getElementDataType()), falling back to inference from the filter values only when the field carries no type info.Design note
Both parsers require the inner
RelationalExpressionto carry a constant value list; a non-constant RHS throwsUnsupportedOperationException. This is intentional —ALL/EXACTLY_ONEare set-level operators, unlikeANYwhich supports arbitrary per-element sub-filters. Empty value lists are rejected at construction byConstantExpression.Future consideration (noted in the enum javadoc): an
EXACTLYoperator for set equality — array contains exactly the filter values, no more and no less.Test coverage / EXACTLY_ONE semantics
MATCH_EXACTLY_ONEdescribes the stored array cardinality, not the length of the RHS:Example with column
tags:["A", "B"]["A"]["B"]["A", "B", "C"]["A", "B"]["A"]["A", "B"]Same translation on both stores: Mongo
$size=1 +$in; Postgres nativearray_length=1 AND &&/ JSONB length +<@.Test plan
MongoArrayFilterParserTest— operator structure,$isArrayguards, nested paths, non-constant RHS rejection, no double$exprwrappingPostgresQueryParserTest— ALL/EXACTLY_ONE × JSONB/native array, nested JSONB paths, compile-time type precedence over value inference, UNSPECIFIED fallback, non-constant RHS rejectionDocStoreQueryV1Test(nestedArrayMatchAllOneOperatorTest) — integration tests on both datastores: nested JSONB array fields, native array columns (typed + untyped viaPostgresArrayTypeProvider), JSONB array column on flat collections, 3-level nested paths with missing intermediate objects, non-array scalar values, duplicates ([red, red]), and order-independence — run in CI:document-store:build(compile + unit tests + spotless) passes locallyMade with Cursor
Nested arrays
testAllAndOneTreatNestedArrayElementsAsOpaque.