plan/spec: hashed indexes are done, the implication test is next

`tests/spec/indexes/` is 42/42, so all four recorded corpora are green:
positional 51, operators 125, indexes 42, aggregate 70.

Records what implementing the row taught, including the three things the
design review got wrong. Two were already noted when the corpus was
recorded ($in is allowed; a differing filter is 86); the third is new and
cheaper than the review's version: hashed needs no flags bit, because it
belongs to a key *component* and the per-component direction byte was
already there holding 0 or 1.

Also fixes a corpus case that did not measure what it said. "equality
across numeric types" sent an int32, because a source is plain JSON and
`5.0` is `5` after JSON.parse -- it was a second copy of the case above
it. Reading sources as EJSON was tried and reverted, and the recorder now
says why: EJSON's wrapper namespace collides with the query operators
these sources are made of, so `{"$regex": "x"}` became a BSONRegExp,
`structuredClone` flattened it to `{pattern, options}`, and "a filter
using $regex is refused" silently became a filter mongod accepts. The
cross-type property is a unit test instead, which is where it belongs --
it is about how this server hashes, and mongod's hash is a different
function, so a corpus could only ever check the answer.

The case is renamed to what it does measure rather than deleted: two
documents sharing a value is still the read a hashed index exists for.

Verified: 256/256 unit tests in ReleaseFast and ReleaseSafe, 87/87 fuzz,
all four corpora 0 fail, pinned scorecard unchanged at 228/63/196, the
full e2e matrix and crash-fuzz green.
This commit was merged in pull request #13.
This commit is contained in:
A.Shakhmatov
2026-08-11 00:06:33 +03:00
parent 235ae19e30
commit bf685aa6de
6 changed files with 143 additions and 8 deletions

View File

@@ -152,6 +152,32 @@ sixth "this key is hashed" — old files never set them and read back
identically, so `catalog_version` stays 1. That is the same argument the free
list used for its own format change and it holds here for the same reason.
## Outcome
Steps 14 landed in that order. `tests/spec/indexes/` is 42/42.
Three things this review got wrong, kept here because the point of writing it
before the code was to find out which parts would not survive contact:
1. **`$in` in a partial filter is allowed.** §4 listed it with `$ne` and
`$regex`. Recording it said otherwise.
2. **The same key with a different filter is IndexKeySpecsConflict (86)**, not
the 67 §4 implied — the filter is part of *which documents* the index is
over, not of how it behaves, and mongod splits the two codes on exactly
that line.
3. **Hashed needs no flags bit.** §5 proposed a sixth one. Hashed belongs to a
key *component*, not to an index, and the per-component direction byte the
catalog has always written has only ever held 0 or 1 — so a third value
costs no format change and `catalog_version` stays 1, which is the same
conclusion by a better route.
And one thing the corpus found that this review had no reason to look for:
`find({a: null})` did not match a document with no `a`, index or no index. See
PLAN §6.
Step 5, the implication test, is still open. Until it exists a partial index
is maintained, enforces `unique`, and is never read from.
## 6. Not covered
Neither `$or` in a partial filter beyond accepting it, nor `2dsphere`, `text`,