plan/spec: the positional gate goes green
Scorecard 204 -> 218 pass, 87 -> 73 fail: the fourteen `arrayFilters` cases across five files, which is the whole of what the two implementation commits were expected to move and nothing else. Positional corpus 51/51. The three divergences measured on the way are written into PLAN §6 rather than left in commit messages: `$` with two predicates on one array that no element satisfies together, an array filter with a top-level `$and`/`$or`, and a literal index into a scalar element -- the last being the one place the positional walk and the plain indexed path now answer differently, which is worth a commit of its own and needs its own measurements first. The design review gets an outcome note, since two of its guesses were wrong and the corpus is where that was settled.
This commit is contained in:
33
PLAN.md
33
PLAN.md
@@ -467,7 +467,7 @@ answers, and the trade is only acceptable because the lie is removed first.
|
||||
| M1 | **Cursors + wire polish** | getMore / killCursors / batchSize; server-side cursor state with idle timeout; sessions plumbing (lsid accepted) as drivers send it; hello advertisement updates; **`moreToCome` on requests** (see the bug below); command-monitoring assertions in the spec runner | crud spec suite green; e2e green |
|
||||
| M2 | **The `aggregate` command surface** | `$out` and `$merge` (7 of the 13 failures), and refusing every pipeline construct the engine does not implement instead of answering `0` (amendment A6). The other 6 failures are blocked on M2.5, M4 and M8 — see `docs/M2_DESIGN_REVIEW.md` §7 | `aggregate-*.json`: 0 fail among the 7 reachable cases |
|
||||
| M2.5 | **The aggregation engine** | expression evaluator, per-stage document iterator, the accumulators, `$unwind`; `$lookup`/`$facet` explicitly out of the first cut (amendment A6) | a purpose-built stage corpus, every expectation measured against mongod |
|
||||
| M3 | **Update operators + index types** | `distinct` (**done**); positional paths refused rather than destructive (**done**); then `$`/`$[]`/`$[<ident>]` implemented, $setOnInsert, $addToSet, $mul, $min/$max, $pop, $pullAll, $currentDate, pipeline updates; partial + hashed indexes | `tests/spec/positional/` 0 fail (51 cases, recorded from mongod — the named gate could not see this work, see below); remaining crud coverage; e2e3/e2e4 green |
|
||||
| M3 | **Update operators + index types** | `distinct` (**done**); positional paths refused rather than destructive (**done**); `$`/`$[]`/`$[<ident>]` + `arrayFilters` implemented (**done**); then $setOnInsert, $addToSet, $mul, $min/$max, $pop, $pullAll, $currentDate, pipeline updates; partial + hashed indexes | `tests/spec/positional/` 0 fail (51 cases, recorded from mongod — **green**; the named gate could not see this work, see below); remaining crud coverage; e2e3/e2e4 green |
|
||||
| M4 | **Sessions + transactions** | logical sessions, snapshot isolation on the mmap engine, write concern at commit | sessions + transactions spec suites green |
|
||||
| M5 | **Change streams** | change feed + resume tokens (likely log-seq based), getMore integration | change-streams spec suite green |
|
||||
| M6 | **Admin/ops commands** | dbStats, collStats, serverStatus, ping, buildInfo, listDatabases filters, dropDatabase durability (log it) | mongosh UX smoke; e2e green |
|
||||
@@ -586,9 +586,10 @@ gaps (`bad update`, `update must be a document` — M3), unimplemented commands
|
||||
`bulkWrite`/`insertMany`. That list, not the total, is the milestone backlog.
|
||||
|
||||
All three named commands have since landed: `$out`/`$merge` in M2, `distinct`
|
||||
as M3's first commit. The backlog the same grouping gives today is
|
||||
`arrayFilters` (14 cases), the `findOneAndUpdate`/`findOneAndReplace` shapes
|
||||
(~10), pipeline-form updates (~10), and `create-null-ids` (6).
|
||||
as M3's first commit, and the `arrayFilters` cluster (14 cases) with M3's
|
||||
positional operators. The backlog the same grouping gives today is the
|
||||
`findOneAndUpdate`/`findOneAndReplace` shapes (~10), pipeline-form updates
|
||||
(~10), and `create-null-ids` (6).
|
||||
|
||||
---
|
||||
|
||||
@@ -1091,7 +1092,7 @@ has to be its own commit with its own re-recorded scorecard.
|
||||
`sources/`, every expectation recorded from mongod 8.3.7, run through the
|
||||
shared runner with `--suite-dir`.
|
||||
|
||||
It stands at 15 pass / 36 fail against the refusal, which is the intended
|
||||
It stood at 15 pass / 36 fail against the refusal, which was the intended
|
||||
shape — `expressions.json` was 1/26 before the evaluator and is green now.
|
||||
Recording it settled a dozen things, and the first contradicts what the
|
||||
design review assumed: `y.$[i].c.$[i].d` reusing one identifier at two
|
||||
@@ -1100,6 +1101,28 @@ has to be its own commit with its own re-recorded scorecard.
|
||||
is error 28 where every other path failure is 2; a positional segment never
|
||||
creates, so a missing or non-array path is an error where `$set: {'a.b': 1}`
|
||||
would construct; and an upsert gets no special case.
|
||||
|
||||
**Green as of the two implementation commits**, 51/51, and the pinned crud
|
||||
scorecard moved 204 → 218 with it. Three divergences from mongod were
|
||||
measured on the way and left in place rather than guessed at:
|
||||
- **`$` with two predicates on one array.** `{"y.b": 3, "y.c": 2}` matches
|
||||
`[{b: 3, c: 1}, {b: 1, c: 2}]` without either element satisfying both, and
|
||||
the document still matched. mongod writes element 1; this writes element 0.
|
||||
mongod's answer is an artefact of which predicate last wrote its match
|
||||
position — the same query with the two predicates *reversed* still gives 1
|
||||
— so there is no rule here to copy, only a behaviour to record.
|
||||
- **An array filter with a top-level `$and`/`$or`.** mongod accepts
|
||||
`[{$or: [{"i.b": 3}]}]` and finds the identifier inside it; this refuses
|
||||
with 9, the same answer it gives `[{}]`. The identifier is read off
|
||||
top-level field names, and an operator is not one.
|
||||
- **A literal index into a scalar element.** `$set: {"y.0.b": 1}` on
|
||||
`y: [null]` is PathNotViable (28) on mongod; here it replaces the null
|
||||
with `{b: 1}`. The positional walk refuses this — `Walk.element` is where
|
||||
the corpus measures it — but the plain indexed path still takes
|
||||
`set_path`'s creating branch. One rule, reached two ways, and only one way
|
||||
agrees. Worth one commit, and it needs its own measurements first: the
|
||||
padding case (`y.3.b` past the end) is a *legal* creation on mongod, so
|
||||
the fix is not "refuse a non-document element".
|
||||
- **M3 update operators** — open. `distinct` landed first because it was a
|
||||
whole missing command with no dependencies, and measuring it turned up three
|
||||
things worth keeping, none of which are `distinct`'s to fix:
|
||||
|
||||
Reference in New Issue
Block a user