tests/spec: record Tier 2's spec -- the stages that rewrite a document

24 cases for `$addFields`/`$set`, `$unset`, `$replaceRoot`, `$unwind` and
`$project`'s computed fields, recorded from mongod 8.3.7 before any of them is
written. The file starts at 0 pass / 24 fail, which is the honest number for
five stages this server does not have.

Eight things the recording settled, none of them guessable from the manual:

    $addFields whose expression is missing   the field is not added at all
    $addFields: {"n.z": 1}                   sets the nested path, keeps siblings
    $replaceRoot of missing or non-document  error 40228
    $unwind of an empty array or missing     the document is dropped
    $unwind of a non-array                   the document is kept whole
    $unwind path without a $                 error 28818
    includeArrayIndex                        0-based
    $project: {n: {x: 1}}                    narrows it; a document without
                                             `n` keeps only `_id`

That last one is the `query.project` gap recorded during M2 as broken rather
than unimplemented -- a nested inclusion currently reads as falsy and returns
the whole document minus the field. It now has a measured expectation to be
fixed against, in the corpus, rather than a note in a commit message.

Worth stating before the implementation: the design review expected Tier 2 to
need a per-stage iterator, on the grounds that `$unwind` is 1->N and "there is
no way to express that in a window over the input". That was true of the window
as it stood, and stopped being true when `$project` was made to rebuild the
stream -- a stage that rebuilds can emit any number of documents it likes. So
Tier 2 looks like five stages rather than a rewrite. The corpus is what will
say whether that holds.
This commit is contained in:
A.Shakhmatov
2026-08-09 22:30:36 +03:00
parent 37cfa863ee
commit 88ac010c3c
3 changed files with 1379 additions and 1 deletions

View File

@@ -64,6 +64,7 @@ accumulators in:
```
group-accumulators.json 19 pass 0 fail 0 skip
expressions.json 27 pass 0 fail 0 skip
document-stages.json 0 pass 24 fail 0 skip
```
`group-accumulators` found its first real disagreement on the way to 18:
@@ -72,7 +73,27 @@ that counted documents rather than numbers would have passed every test
anybody would think to write by hand.
`expressions.json` was recorded before the evaluator was written and read
1 pass / 26 fail against it; it is green now. Expressions are exercised through
1 pass / 26 fail against it; it is green now. `document-stages.json` is the
next tier's spec, recorded and not implemented -- `$addFields`/`$set`,
`$unset`, `$replaceRoot`, `$unwind` and `$project`'s computed fields, none of
which this server has, so it starts at zero.
What recording *that* settled:
| | mongod |
|---|---|
| `$addFields` whose expression is missing | the field is not added at all |
| `$addFields: {"n.z": 1}` | sets the nested path, keeps its siblings |
| `$replaceRoot` of a missing path or a non-document | error 40228 |
| `$unwind` of an empty array or a missing field | the document is dropped |
| `$unwind` of a non-array | the document is kept whole |
| `$unwind` path without a `$` | error 28818 |
| `includeArrayIndex` | 0-based |
| `$project: {n: {x: 1}}` | narrows the subdocument, and a document without `n` keeps only `_id` |
That last row is the `query.project` gap recorded during M2 -- a nested
inclusion currently reads as falsy and returns the whole document minus the
field. It now has a measured expectation to be fixed against. Expressions are exercised through
`$group`, because `_id` and the accumulator arguments are the only expression
positions that exist until `$addFields` and `$project`'s computed fields land.