You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
$in / $nin are z.array(z.any()), so a reference is admitted there too.
No backend resolves it. The in-memory evaluator — the one implementation the repo has for $field — does not, and this is structural rather than an oversight. matches-filter.tsresolveValue returns early for an array:
functionresolveValue(raw: unknown,record){if(raw&&typeofraw==='object'&&!Array.isArray(raw)&&'$field'inraw){…}returnraw;// ← an ARRAY comes back unchanged, members unresolved}
evalOp resolves the whole comparand, never the members. So for $in/$nin the raw reference OBJECT is compared with looseEq against a stored value (never equal), and for $between the raw object becomes an ordering bound.
Why it matters
This is the ADR-0049 enforce-or-remove shape at a declared position: the spec promises a comparand form that nothing implements, and the failure is SILENT on the memory path — { amount: { $in: [{ $field: 'budget' }] } } matches nothing and reports nothing. On an RLS check that is a denied write with no diagnostic; the $nin direction loses an exclusion the author wrote.
driver-sql / driver-sqlite-wasm refuse these positions loudly (INVALID_FILTER / 400) — #5041 installed that and #5222 deliberately kept it, precisely because there is no correct in-memory semantics for SQL to be equivalent to. So today the declaration is honoured by nobody and refused by two backends.
The decision this needs
Either:
RemoveFieldReferenceSchema from the $between endpoint unions (and rule $in/$nin members out explicitly), making declared = enforced; or
Implement member resolution in matches-filter.ts and then decide whether the SQL faces can compile it conformance-equivalently (per-member OR-expansion, whose NULL and type-affinity behaviour is the open question [spec] SqlDriver 将 $field 编译为列对列比较(cross-field comparison push-down) #5222 declined to guess at).
Not decided here — it is a contract-face call, same class as the #5222 rulings.
Measured while implementing #5222 (cross-field push-down). Filing unassigned — recording, not claiming.
The fact
packages/spec/src/data/filter.zod.tsdeclaresFieldReferenceSchemain both$betweenendpoints, in the documentation copy and in the ENFORCED copy:$in/$ninarez.array(z.any()), so a reference is admitted there too.No backend resolves it. The in-memory evaluator — the one implementation the repo has for
$field— does not, and this is structural rather than an oversight.matches-filter.tsresolveValuereturns early for an array:evalOpresolves the whole comparand, never the members. So for$in/$ninthe raw reference OBJECT is compared withlooseEqagainst a stored value (never equal), and for$betweenthe raw object becomes an ordering bound.Why it matters
This is the ADR-0049 enforce-or-remove shape at a declared position: the spec promises a comparand form that nothing implements, and the failure is SILENT on the memory path —
{ amount: { $in: [{ $field: 'budget' }] } }matches nothing and reports nothing. On an RLScheckthat is a denied write with no diagnostic; the$nindirection loses an exclusion the author wrote.driver-sql/driver-sqlite-wasmrefuse these positions loudly (INVALID_FILTER/ 400) — #5041 installed that and #5222 deliberately kept it, precisely because there is no correct in-memory semantics for SQL to be equivalent to. So today the declaration is honoured by nobody and refused by two backends.The decision this needs
Either:
FieldReferenceSchemafrom the$betweenendpoint unions (and rule$in/$ninmembers out explicitly), making declared = enforced; ormatches-filter.tsand then decide whether the SQL faces can compile it conformance-equivalently (per-member OR-expansion, whose NULL and type-affinity behaviour is the open question [spec] SqlDriver 将$field编译为列对列比较(cross-field comparison push-down) #5222 declined to guess at).Not decided here — it is a contract-face call, same class as the #5222 rulings.
Refs
$field编译为列对列比较(cross-field comparison push-down) #5222 (cross-field push-down; refuses these positions, with the reasoning recorded incross-field-conformance-cases.ts)$field跨字段比较:spec 声明 + cel-to-filter 产出,但无任何 SQL 执行层实现 —— enforce-or-remove 裁决位 #5041 (the original refusal + probe evidence)