Skip to content

drivers(memory, mongodb): the $contains family still folds case — the last two backends left on the wrong side of #4706 Q2 = A #6682

Description

@os-zhuang

Finding, filed unassigned from the #6518 implementation. Not a request to unfreeze anything — it exists so the two FILTER_TEXT_CASES DEBT rows that survive #6518 have a successor to point at, instead of pointing at a closed issue.

What #6518 left behind

#4706 Q2 = A rules the $contains family ($contains / $notContains / $startsWith / $endsWith) case-SENSITIVE on every backend. After #6518 the scoreboard is:

face { name: { $contains: 'acme' } } against ACME Corp verdict
driver-sql / -sqlite-wasm / -turso (local + remote) does NOT match ✅ ruled behaviour (#6518)
@objectstack/formula matchesFilterCondition does NOT match ✅ already compliant (String.includes)
objectql having does NOT match ✅ already compliant
service-analytics (both SQL compilers) does NOT match ✅ already compliant on the dialect they emit for
driver-memory — query path + analytics face MATCHES ❌ new RegExp(escapeRegex(v), 'i')
driver-memory — reference matcher does NOT match ⚠️ this package answers ONE operator two ways
driver-mongodb MATCHES ❌ $regex with a hard-coded $options: 'i'

Both defects are over-matching — rows the filter excludes are returned — and both fold the whole Unicode range, so they also overshoot the #4706 Q1 = A ASCII-only boundary that $icontains is held to. On an RLS read scope a wider predicate is over-reach, not a loose filter (#3948).

The driver-memory row is the sharper one: its two faces disagree with each other, which is the divergence class #5374 closed between the other two faces of that package.

Why #6518 did not do it

The #5499 freeze (maintainer, 2026-08-05) suspends implementation investment in driver-memory and driver-mongodb. #6518 was dispatched with that freeze restated as a ruling, so both packages were explicitly out of scope; their FILTER_TEXT_CASES cells stay DEBT rather than being cleared or quietly widened.

Why this is separate from #6520

#6520 is the $icontains programme (requirement 1: implement a new operator, gated on taking $icontains into the spec FILTER_OPERATORS allowlist). This is requirement 2: an operator both packages already implement returns the wrong rows. Different requirement, different risk — #6520's cost is the vocabulary change rippling through allowlist consumers, while this one is a one-line flag on each side plus the row-set pins that flip with it.

They are, however, natural PR-mates: both touch the same two translateFieldOperators / normalizeFieldOperators sites, and clearing a FILTER_TEXT_CASES cell needs BOTH requirements answered before the case-set can be imported (scripts/check-driver-conformance.mjs judges a cell by that import, so a half-answered cell must not import it).

Suggested scope

  1. driver-memory: drop the 'i' flag from the $contains-family lowering on the query path and the analytics face, and align the reference matcher — one answer per operator, per driver-memory analytics 面的 $notContains 编译成裸 mingo {$not: 'x'},该谓词不约束任何行 —— 结果被放大到全表 #5374.
  2. driver-mongodb: drop the hard-coded $options: 'i' in mongodb-filter.ts's translateFieldOperators. escapeRegex already makes the comparand literal, so the literal-comparand cases keep holding.
  3. Flip the row-set pins that currently encode the folded answer, asserting the NEW substance rather than deleting the assertion.
  4. Re-grade the two FILTER_TEXT_CASES DEBT rows in scripts/check-driver-conformance.mjs — they can only be deleted once requirement 1 (JS 求值面全体拒收 $icontains(driver-memory 两面 / driver-mongodb / objectql having / formula)—— SQL 族已实现,同一 filter 在内存 double 上抛错 #6520) is answered too, since coverage is judged by importing the whole case-set.

Anchors verified on origin/main @ d13f627: driver-memory/src/memory-driver.ts (normalizeFieldOperators, the RegExp-'i' query path), driver-memory/src/memory-matcher.ts (match(), String.includes), driver-mongodb/src/mongodb-filter.ts (translateFieldOperators).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions