feat!: add ITSELF self-reference marker for element-level $elemMatch and $all - #770
Conversation
…and $all
The reserved marker ITSELF ('$this') addresses the array element itself
inside an elemMatch interior, closing the core IR gap behind two mongo
constructs:
- parser-mongo parses element-level $elemMatch ({ $elemMatch: { $gt: 5 } })
onto ITSELF and desugars $all to an AND of independently scoped
elemMatch(f, eq(ITSELF, v)) conditions; negated forms throw typed.
- memory evaluates ITSELF leaves against the bound element; only real
array elements match (missing/scalar/to-one/empty array never do).
- the expression dialect gains an elemMatch(field, expr) production with
a dedicated $this token, and the URL codec encodes/decodes it.
- sql/typeorm throw a typed featureUnsupported for ITSELF leaves (a
joined relation row is not a scalar column).
- the build layer exports ITSELF, accepts element-level $elemMatch
operator objects and rejects marker misuse with typed BuildErrors.
BREAKING CHANGE: every elemMatch node now opens its own quantifier
scope in @rapiq/memory - two elemMatches on one field (or an elemMatch
beside a dotted sibling) bind independent elements (mongo semantics)
instead of sharing one binding. $-prefixed keys are no longer accepted
as field names in the build-layer filters object grammar. In the
expression filter dialect, elemMatch is now a reserved keyword and
$-prefixed words are reserved markers, so fields with these names no
longer tokenize.
closes #768
|
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe change introduces the exported ChangesITSELF filter construction
Parser support
Encoding and evaluation
Adapter boundary
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Query
participant MongoFiltersParser
participant CoreFilters
participant MemoryCompiler
participant MemoryBinding
Query->>MongoFiltersParser: Parse $elemMatch or $all
MongoFiltersParser->>CoreFilters: Build ITSELF-scoped conditions
CoreFilters->>MemoryCompiler: Compile elemMatch filter
MemoryCompiler->>MemoryBinding: Create independent element scope
MemoryBinding-->>MemoryCompiler: Bind array element
MemoryCompiler-->>Query: Evaluate element predicate
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/parameter/filters/constants.ts`:
- Around line 11-21: Update the ITSELF documentation in the constants module to
explicitly allow nested elemMatch(ITSELF, ...) expressions as a supported form.
Broaden the restriction currently limiting ITSELF to leaf conditions, while
preserving the existing rejection behavior for other invalid contexts and
unsupported backend adapters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5af10c23-8ade-472f-beb0-6e1b566347e5
📒 Files selected for processing (30)
.agents/references/ucast.mdpackages/codec-url/src/expression/encoder/filters.tspackages/codec-url/test/unit/expression-roundtrip.spec.tspackages/codec-url/test/unit/simple-roundtrip.spec.tspackages/core/src/build/parameter/filters/module.tspackages/core/src/parameter/filters/constants.tspackages/core/src/parameter/filters/index.tspackages/core/test/unit/build/module.spec.tspackages/docs/guide/building-queries.mdpackages/docs/guide/filters.mdpackages/docs/guide/wire.mdpackages/docs/packages/codec-url.mdpackages/docs/packages/memory.mdpackages/docs/packages/parser-expression.mdpackages/docs/packages/parser-mongo.mdpackages/docs/packages/sql.mdpackages/memory/src/parameter/filters/binding.tspackages/memory/src/parameter/filters/compiler.tspackages/memory/src/parameter/filters/constants.tspackages/memory/test/unit/filters/binding.spec.tspackages/parser-expression/src/parameter/filters/constants.tspackages/parser-expression/src/parameter/filters/module.tspackages/parser-expression/test/unit/parser/filters.spec.tspackages/parser-mongo/src/parameter/filters/constants.tspackages/parser-mongo/src/parameter/filters/module.tspackages/parser-mongo/src/parameter/filters/types.tspackages/parser-mongo/test/unit/parser/filters.spec.tspackages/sql/src/adapter/filters/base.tspackages/sql/test/unit/interpreters/elem-match.spec.tspackages/typeorm/test/unit/filters.spec.ts
|
@coderabbitai pause |
✅ Action performedReviews paused. |
Closes #768.
The reserved marker
ITSELF(wire spelling$this) addresses the array element itself in the field position of a leaf condition inside anelemMatchinterior — the core IR gap behind the two remaining mongo constructs@rapiq/parser-mongohad to reject. Design decisions (marker spelling, binding scope) are recorded in.agents/plans/016-itself-marker.md.Changes
ITSELF = '$this';defineFiltersvalidates the marker contract on condition-node input, accepts mongo-style element-level$elemMatchoperator objects ({ $elemMatch: { $gt: 5 } }), and now rejects$-prefixed keys as field names (matching the mongo-dialect reservation).$elemMatchparses ontoITSELF(ucasthasOperatorssplit, including$notand nested$elemMatchinteriors);$allmoves out of the unsupported set and desugars to an AND of independently scopedelemMatch(f, eq(ITSELF, v))conditions with$in-style value validation; negated$all/$elemMatchthrowOPERATOR_UNSUPPORTED.elemMatchnode opens its own quantifier scope (discriminated binding paths) — interiors share one element, sibling elemMatches bind independently (exact Mongo semantics, required for the$alldesugar).ITSELFleaves evaluate the bound element and only match real array elements: missing fields, scalars, to-one objects and empty arrays never match (deliberately no mongo scalar fallback for$all).FiltersBaseAdapter.buildFieldthrows typedAdapterError.featureUnsupported('filters:itself')— a joined relation row is not a scalar column; dialect JSON-array support (json_each/unnest) stays a follow-up.elemMatch(field, expr)grammar production (interior fields resolve against the related schema viadescend, unbound fallback for non-relation fields) and a dedicated$thistoken;not(elemMatch(...))throws. The expression encoder serializesELEM_MATCHwith full round-trip coverage — wire formfilter=elemMatch(scores,gt($this,'5')); the simple codec keeps throwing typed under the subset law.$allrow), memory (quantifier semantics + ITSELF section, divergence table), sql (ITSELF limitation), parser-expression (grammar), codec-url and the filters / building-queries / wire guides.Breaking changes
elemMatchnodes on one field (or anelemMatchbeside a dotted sibling) no longer share an element binding in@rapiq/memory— the two plan-014 parity-fallout pinning specs were flipped. SQL keeps single-join-alias composition, so multi-elemMatch trees diverge between sql and memory (documented).$-prefixed keys are rejected as field names in the build-layer filters object grammar.elemMatchis a reserved keyword and$-words are reserved markers — fields with these names no longer tokenize/encode.Verification
All 8 packages build; full test run passes (~60 new specs across core, parser-mongo, parser-expression, memory, sql, typeorm, codec-url); lint clean. End-to-end smoke: a mongo document with
$all+ element-level$elemMatchparses, evaluates with mongo semantics viacompileFilters, and round-trips the URL codec.Summary by CodeRabbit
New Features
elemMatchsupport to expression and Mongo-style query formats.$allsupport for independently matching array elements.$thiselement references for conditions insideelemMatch.Bug Fixes
elemMatchconditions evaluate independently.Documentation
$this, dialect support, and adapter limitations.