mongo-query-normalizer 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +109 -441
  2. package/README.zh-CN.md +109 -430
  3. package/dist/compile/compile.js +13 -0
  4. package/dist/compile/compile.js.map +1 -1
  5. package/dist/normalize.d.ts.map +1 -1
  6. package/dist/normalize.js +3 -0
  7. package/dist/normalize.js.map +1 -1
  8. package/dist/passes/normalize-predicate.d.ts.map +1 -1
  9. package/dist/passes/normalize-predicate.js +38 -4
  10. package/dist/passes/normalize-predicate.js.map +1 -1
  11. package/dist/predicate/capabilities/eq/eq-eq.js +9 -11
  12. package/dist/predicate/capabilities/eq/eq-eq.js.map +1 -1
  13. package/dist/predicate/capabilities/eq/eq-in.d.ts.map +1 -1
  14. package/dist/predicate/capabilities/eq/eq-in.js +9 -50
  15. package/dist/predicate/capabilities/eq/eq-in.js.map +1 -1
  16. package/dist/predicate/capabilities/eq/eq-range.d.ts.map +1 -1
  17. package/dist/predicate/capabilities/eq/eq-range.js +11 -80
  18. package/dist/predicate/capabilities/eq/eq-range.js.map +1 -1
  19. package/dist/predicate/capabilities/range/range-range.d.ts.map +1 -1
  20. package/dist/predicate/capabilities/range/range-range.js +1 -10
  21. package/dist/predicate/capabilities/range/range-range.js.map +1 -1
  22. package/dist/predicate/planner/relation-planner.d.ts.map +1 -1
  23. package/dist/predicate/planner/relation-planner.js +0 -7
  24. package/dist/predicate/planner/relation-planner.js.map +1 -1
  25. package/dist/predicate/safety/predicate-safety-policy.d.ts +8 -0
  26. package/dist/predicate/safety/predicate-safety-policy.d.ts.map +1 -1
  27. package/dist/predicate/safety/predicate-safety-policy.js.map +1 -1
  28. package/dist/predicate/shared/cardinality-risk.d.ts +2 -0
  29. package/dist/predicate/shared/cardinality-risk.d.ts.map +1 -0
  30. package/dist/predicate/shared/cardinality-risk.js +7 -0
  31. package/dist/predicate/shared/cardinality-risk.js.map +1 -0
  32. package/dist/predicate/shared/field-cardinality-guards.d.ts +4 -0
  33. package/dist/predicate/shared/field-cardinality-guards.d.ts.map +1 -0
  34. package/dist/predicate/shared/field-cardinality-guards.js +11 -0
  35. package/dist/predicate/shared/field-cardinality-guards.js.map +1 -0
  36. package/dist/predicate/shared/field-cardinality-policy.d.ts +3 -0
  37. package/dist/predicate/shared/field-cardinality-policy.d.ts.map +1 -0
  38. package/dist/predicate/shared/field-cardinality-policy.js +5 -0
  39. package/dist/predicate/shared/field-cardinality-policy.js.map +1 -0
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -1,523 +1,191 @@
1
- # Mongo Query Normalizer
1
+ # mongo-query-normalizer
2
2
 
3
- **English** | [中文](README.zh-CN.md)
4
-
5
- An **observable, level-based** normalizer for MongoDB query objects. It stabilizes query **shape** at the conservative default, and adds **`predicate`** and **`scope`** levels with **documented, test-backed contracts** (see [SPEC.md](SPEC.md) and [docs/normalization-matrix.md](docs/normalization-matrix.md) / [中文](docs/normalization-matrix.zh-CN.md)). It returns **predictable** output plus **metadata**—not a MongoDB planner optimizer.
6
-
7
- > **Default posture:** **`shape`** is the smallest, structural-only pass and the recommended default for the widest production use. **`predicate`** and **`scope`** apply additional conservative rewrites under explicit contracts; adopt them when you need those transforms and accept their modeled-operator scope (opaque operators stay preserved).
8
- >
9
- > **As of `v0.2.0`:** predicate rewrites are intentionally narrowed to an explicitly validated surface (`eq.eq`, `eq.ne`, `eq.in`, `eq.range`, `range.range`). High-risk combinations (for example null-vs-missing, array-sensitive semantics, `$exists`/`$nin`, object-vs-dotted-path mixes, opaque mixes) remain conservative by design.
3
+ > A safe MongoDB query normalizer — **correctness over cleverness**
10
4
 
11
5
  ---
12
6
 
13
- ## Why it exists
14
-
15
- - Query **shape** diverges across builders and hand-written filters.
16
- - Outputs can be **hard to compare**, log, or diff without a stable pass.
17
- - You need a **low-risk normalization layer** that defaults to conservative behavior.
18
-
19
- This library does **not** promise to make queries faster or to pick optimal indexes.
20
-
21
- ---
7
+ ## What it does
22
8
 
23
- ## Features
9
+ **Turn messy Mongo queries into clean, stable, and predictable ones — safely.**
24
10
 
25
- - **Level-based** normalization (`shape` → `predicate` → `scope`)
26
- - **Conservative default**: `shape` only out of the box (lowest-risk structural pass)
27
- - **Observable** `meta`: changed flags, applied/skipped rules, warnings, hashes, optional stats
28
- - **Stable / idempotent** output when rules apply (same options)
29
- - **Opaque fallback** for unsupported operators (passthrough, not semantically rewritten)
30
-
31
- ---
32
-
33
- ## Install
11
+ ```js
12
+ // before
13
+ {
14
+ $and: [
15
+ { status: "open" },
16
+ { status: { $in: ["open", "closed"] } }
17
+ ]
18
+ }
34
19
 
35
- ```bash
36
- npm install mongo-query-normalizer
20
+ // after
21
+ { status: "open" }
37
22
  ```
38
23
 
39
24
  ---
40
25
 
41
- ## Quick start
42
-
43
- ```ts
44
- import { normalizeQuery } from "mongo-query-normalizer";
45
-
46
- const result = normalizeQuery({
47
- $and: [{ status: "open" }, { $and: [{ priority: { $gte: 1 } }] }],
48
- });
49
-
50
- console.log(result.query);
51
- console.log(result.meta);
52
- ```
53
-
54
- ---
55
-
56
- ## Complete usage guide
57
-
58
- ### 1) Minimal usage (recommended default)
59
-
60
- ```ts
61
- import { normalizeQuery } from "mongo-query-normalizer";
62
-
63
- const { query: normalizedQuery, meta } = normalizeQuery(inputQuery);
64
- ```
65
-
66
- - Without `options`, default behavior is `level: "shape"`.
67
- - Best for low-risk structural stabilization: logging, cache-key normalization, query diff alignment.
68
-
69
- ### 2) Pick a level explicitly
70
-
71
- ```ts
72
- normalizeQuery(inputQuery, { level: "shape" }); // structural only (default)
73
- normalizeQuery(inputQuery, { level: "predicate" }); // modeled predicate cleanup
74
- normalizeQuery(inputQuery, { level: "scope" }); // scope propagation / conservative pruning
75
- ```
76
-
77
- - `shape`: safest structural normalization.
78
- - `predicate`: dedupe / merge / contradiction collapse for modeled operators.
79
- - `scope`: adds inherited-constraint propagation and conservative branch decisions on top of `predicate`.
80
-
81
- ### 3) Full `options` example
82
-
83
- ```ts
84
- import { normalizeQuery } from "mongo-query-normalizer";
85
-
86
- const result = normalizeQuery(inputQuery, {
87
- level: "scope",
88
- rules: {
89
- // shape-related
90
- flattenLogical: true,
91
- removeEmptyLogical: true,
92
- collapseSingleChildLogical: true,
93
- dedupeLogicalChildren: true,
94
- // predicate-related
95
- dedupeSameFieldPredicates: true,
96
- mergeComparablePredicates: true,
97
- collapseContradictions: true,
98
- // ordering-related
99
- sortLogicalChildren: true,
100
- sortFieldPredicates: true,
101
- // scope observe-only rule (no structural hoist)
102
- detectCommonPredicatesInOr: true,
103
- },
104
- safety: {
105
- maxNormalizeDepth: 32,
106
- maxNodeGrowthRatio: 1.5,
107
- },
108
- observe: {
109
- collectWarnings: true,
110
- collectMetrics: false,
111
- collectPredicateTraces: false,
112
- collectScopeTraces: false,
113
- },
114
- predicate: {
115
- safetyPolicy: {
116
- // override only fields you care about
117
- },
118
- },
119
- scope: {
120
- safetyPolicy: {
121
- // override only fields you care about
122
- },
123
- },
124
- });
125
- ```
126
-
127
- ### 4) Inspect resolved runtime options
26
+ ## ⚠️ Why this matters
128
27
 
129
- ```ts
130
- import { resolveNormalizeOptions } from "mongo-query-normalizer";
28
+ If you build dynamic queries, you will eventually get:
131
29
 
132
- const resolvedOptions = resolveNormalizeOptions({
133
- level: "predicate",
134
- observe: { collectMetrics: true },
135
- });
30
+ * duplicated conditions
31
+ * inconsistent query shapes
32
+ * hard-to-debug filters
33
+ * subtle semantic bugs
136
34
 
137
- console.log(resolvedOptions);
138
- ```
35
+ Most tools try to “optimize” queries.
139
36
 
140
- - Useful for debugging why a rule is enabled/disabled.
141
- - Useful for logging a startup-time normalization config snapshot.
37
+ 👉 This library does something different:
142
38
 
143
- ### 5) Consume `query` and `meta`
39
+ > **It only applies transformations that are provably safe.**
144
40
 
145
- ```ts
146
- const { query: normalizedQuery, meta } = normalizeQuery(inputQuery, options);
41
+ ---
147
42
 
148
- if (meta.bailedOut) {
149
- logger.warn({ reason: meta.bailoutReason }, "normalization bailed out");
150
- }
43
+ ## 🛡️ Safe by design
151
44
 
152
- if (meta.changed) {
153
- logger.info(
154
- {
155
- level: meta.level,
156
- beforeHash: meta.beforeHash,
157
- afterHash: meta.afterHash,
158
- appliedRules: meta.appliedRules,
159
- },
160
- "query normalized"
161
- );
45
+ ```js
46
+ // NOT simplified (correctly)
47
+ {
48
+ $and: [
49
+ { uids: "1" },
50
+ { uids: "2" }
51
+ ]
162
52
  }
163
53
  ```
164
54
 
165
- - `query`: normalized query object.
166
- - `meta`: observability data (changed flag, rule traces, warnings, hashes, optional stats/traces).
55
+ Why?
167
56
 
168
- ### 6) Typical integration patterns
57
+ Because MongoDB arrays can match both:
169
58
 
170
- ```ts
171
- // A. Normalize centrally in data-access layer
172
- export function normalizeForFind(rawFilter) {
173
- return normalizeQuery(rawFilter, { level: "shape" }).query;
174
- }
175
-
176
- // B. Use stronger convergence in offline paths
177
- export function normalizeForBatch(rawFilter) {
178
- return normalizeQuery(rawFilter, { level: "predicate" }).query;
179
- }
59
+ ```js
60
+ { uids: ["1", "2"] }
180
61
  ```
181
62
 
182
- - Prefer `shape` for online request paths.
183
- - Enable `predicate` / `scope` when there is clear benefit plus test coverage.
184
-
185
- ### 7) Errors and boundaries
186
-
187
- - Invalid `level` throws an error (for example, typos).
188
- - Unsupported or unknown operators are generally preserved as opaque; semantic merge behavior is not guaranteed for them.
189
- - The library target is stability and observability, not query planning optimization.
190
-
191
- ---
192
-
193
- ## Default behavior
194
-
195
- - **Default `level` is `"shape"`** (see `resolveNormalizeOptions()`).
196
- - By default there is **no** predicate merge at `shape`. At **`scope`**, core work is inherited-constraint propagation and conservative branch decisions; **`detectCommonPredicatesInOr`** is an **optional, observe-only** rule (warnings / traces)—never a structural hoist.
197
- - The goal is **stability and observability**, not “smart optimization.”
198
-
199
63
  ---
200
64
 
201
- ## Choosing a level
202
-
203
- - Use **`shape`** when you only need structural stabilization (flatten, dedupe children, ordering, etc.).
204
- - Use **`predicate`** when you need same-field dedupe, modeled comparable merges, and contradiction collapse on **modeled** operators; opaque subtrees stay preserved.
205
- - Use **`scope`** when you need inherited-constraint propagation, conservative pruning, and narrow coverage elimination as described in the spec and matrix. **`detectCommonPredicatesInOr`** (when enabled) is **observe-only** and does not rewrite structure.
206
-
207
- Authoritative behavior boundaries are in **[SPEC.md](SPEC.md)**, **[docs/normalization-matrix.md](docs/normalization-matrix.md)**, and contract tests under **`test/contracts/`**—not informal README prose alone.
208
-
209
- ---
210
-
211
- ## Levels
212
-
213
- ### `shape` (default)
214
-
215
- **Recommended default** for the lowest-risk path. Safe structural normalization only, for example:
216
-
217
- - flatten compound (`$and` / `$or`) nodes
218
- - remove empty compound nodes
219
- - collapse single-child compound nodes
220
- - dedupe compound children
221
- - canonical ordering
222
-
223
- ### `predicate`
224
-
225
- On top of `shape`, conservative **predicate** cleanup on **modeled** operators:
226
-
227
- - dedupe same-field predicates
228
- - merge comparable predicates where modeled
229
- - collapse clear contradictions to an unsatisfiable filter
230
- - merge **direct** `$and` children that share the same field name before further predicate work (so contradictions like `{ $and: [{ a: 1 }, { a: 2 }] }` can be detected)
231
-
232
- ### `scope`
233
-
234
- On top of `predicate`:
235
-
236
- - **Inherited constraint propagation** (phase-1 allowlist) and **conservative branch pruning**; **coverage elimination** only in narrow, tested cases when policy allows
237
- - Optional **`detectCommonPredicatesInOr`**: observe-only (warnings / traces); **no** structural rewrite
238
-
239
- ---
240
-
241
- ## `meta` fields
242
-
243
- | Field | Meaning |
244
- |--------|---------|
245
- | `changed` | Structural/predicate output differs from input (hash-based) |
246
- | `level` | Resolved normalization level |
247
- | `appliedRules` / `skippedRules` | Rule tracing |
248
- | `warnings` | Non-fatal issues when `observe.collectWarnings` is enabled (rule notices, detection text, etc.) |
249
- | `bailedOut` | Safety stop; output reverts to pre-pass parse for that call |
250
- | `bailoutReason` | Why bailout happened, if any |
251
- | `beforeHash` / `afterHash` | Stable hashes for diffing |
252
- | `stats` | Optional before/after tree metrics (`observe.collectMetrics`) |
253
- | `predicateTraces` | When `observe.collectPredicateTraces`: per-field planner / skip / contradiction signals |
254
- | `scopeTrace` | When `observe.collectScopeTraces`: constraint extraction rejections + scope decision events |
255
-
256
- ---
257
-
258
- ## Unsupported / opaque behavior
259
-
260
- Structures such as **`$nor`**, **`$regex`**, **`$not`**, **`$elemMatch`**, **`$expr`**, geo/text queries, and **unknown** operators are generally treated as **opaque**: they pass through or are preserved without full semantic rewriting. They are **not** guaranteed to participate in merge or contradiction logic.
261
-
262
- ---
65
+ ## What this is NOT
263
66
 
264
- ## Stability policy
67
+ * Not a query optimizer
68
+ * Not an index advisor
69
+ * Not a performance tool
265
70
 
266
- The **public contract** is:
71
+ It will **never guess**:
267
72
 
268
- - `normalizeQuery`
269
- - `resolveNormalizeOptions`
270
- - the exported **types** listed in the package entry
73
+ * field cardinality
74
+ * schema constraints
75
+ * data distribution
271
76
 
272
- **Not** part of the public contract: internal AST, `parseQuery`, `compileQuery`, individual rules/passes, or utilities. They may change between versions.
77
+ If unsure **skip**
273
78
 
274
79
  ---
275
80
 
276
- ## Principles (explicit)
277
-
278
- 1. Default level is **`shape`**.
279
- 2. **`predicate`** / **`scope`** may change structure while aiming for **semantic equivalence** on **modeled** operators.
280
- 3. **Opaque** nodes are not rewritten semantically.
281
- 4. Output should be **idempotent** under the same options when no bailout occurs.
282
- 5. This library is **not** the MongoDB query planner or an optimizer.
283
-
284
- ---
285
-
286
- ## Example scenarios
287
-
288
- **Online main path** — use default (`shape`); this remains the most production-safe baseline in `v0.2.0`:
81
+ ## 🚀 Quick start
289
82
 
290
83
  ```ts
291
- normalizeQuery(query);
292
- ```
293
-
294
- **Predicate or scope** — pass `level` explicitly; review [SPEC.md](SPEC.md) and contract tests for supported vs preserved patterns:
84
+ import { normalizeQuery } from "mongo-query-normalizer";
295
85
 
296
- ```ts
297
- normalizeQuery(query, { level: "predicate" });
86
+ const { query } = normalizeQuery(inputQuery);
298
87
  ```
299
88
 
300
89
  ---
301
90
 
302
- ## Public API
91
+ ## 🧠 Where it fits
303
92
 
304
- ```ts
305
- normalizeQuery(query, options?) => { query, meta }
306
- resolveNormalizeOptions(options?) => ResolvedNormalizeOptions
93
+ ```text
94
+ Query Builder / ORM
95
+
96
+ normalizeQuery ← (this library)
97
+
98
+ MongoDB
307
99
  ```
308
100
 
309
- Types: `NormalizeLevel`, `NormalizeOptions`, `NormalizeRules`, `NormalizeSafety`, `NormalizeObserve`, `ResolvedNormalizeOptions`, `NormalizeResult`, `NormalizeStats`, `PredicateSafetyPolicy`, `ScopeSafetyPolicy`, trace-related types (see package exports).
101
+ You don’t replace your builder.
102
+ You **sanitize its output**.
310
103
 
311
104
  ---
312
105
 
313
- ## Testing
314
-
315
- ### Test layout
106
+ ## 🧩 When to use
316
107
 
317
- This repository organizes tests by **API surface**, **normalization level**, and **cross-level contracts**, while preserving deeper semantic and regression suites.
318
-
319
- ### Directory responsibilities
320
-
321
- #### `test/api/`
322
-
323
- Tests the public API and configuration surface.
324
-
325
- Put tests here when they verify:
326
-
327
- * `normalizeQuery` return shape and top-level behavior
328
- * `resolveNormalizeOptions`
329
- * package exports
330
-
331
- Do **not** put level-specific normalization behavior here.
108
+ * dynamic filters / search APIs
109
+ * BI / reporting systems
110
+ * user-generated queries
111
+ * multi-team codebases with inconsistent query styles
112
+ * logging / caching / diffing queries
332
113
 
333
114
  ---
334
115
 
335
- #### `test/levels/`
336
-
337
- Tests the behavior boundary of each `NormalizeLevel`.
338
-
339
- Current levels:
340
-
341
- * `shape`
342
- * `predicate`
343
- * `scope`
344
-
345
- Each level test file should focus on four things:
116
+ ## ⚙️ Levels
346
117
 
347
- 1. positive capabilities of that level
348
- 2. behavior explicitly not enabled at that level
349
- 3. contrast with the adjacent level(s)
350
- 4. a small number of representative contracts for that level
118
+ | Level | What it does | Safety |
119
+ | ----------- | ------------------------------ | --------- |
120
+ | `shape` | structural normalization | 🟢 safest |
121
+ | `predicate` | safe predicate simplification | 🟡 |
122
+ | `scope` | limited constraint propagation | 🟡 |
351
123
 
352
- Prefer asserting:
353
-
354
- * normalized query structure
355
- * observable cross-level differences
356
- * stable public metadata
357
-
358
- Avoid overfitting to:
359
-
360
- * exact warning text
361
- * exact internal rule IDs
362
- * fixed child ordering unless ordering itself is part of the contract
363
-
364
- ---
365
-
366
- #### `test/contracts/`
367
-
368
- Tests contracts that should hold across levels, or default behavior that is separate from any single level.
369
-
370
- Put tests here when they verify:
371
-
372
- * default level behavior
373
- * idempotency across all levels
374
- * output invariants across all levels
375
- * opaque subtree preservation across all levels
376
- * formal **`predicate` / `scope`** contracts (supported merges, opaque preservation, scope policy guards, rule toggles)—see `test/contracts/predicate-scope-stable-contract.test.js`
377
-
378
- Use `test/helpers/level-contract-runner.js` for all-level suites.
124
+ Default is `shape`.
379
125
 
380
126
  ---
381
127
 
382
- #### `test/semantic/`
128
+ ## 📦 Output
383
129
 
384
- Tests semantic equivalence against execution behavior.
385
- These tests validate that normalization preserves meaning.
386
-
387
- This directory is intentionally separate from `levels/` and `contracts/`.
388
-
389
- ---
390
-
391
- #### `test/property/`
392
-
393
- Tests property-based and metamorphic behavior.
394
-
395
- Use this directory for:
396
-
397
- * randomized semantic checks
398
- * metamorphic invariants
399
- * broad input-space validation
400
-
401
- Do not use it as the primary place to express level boundaries.
402
-
403
- ---
404
-
405
- #### `test/regression/`
406
-
407
- Tests known historical failures and hand-crafted regression cases.
408
-
409
- Add a regression test here when fixing a bug that should stay fixed.
410
-
411
- ---
412
-
413
- #### `test/performance/`
414
-
415
- Tests performance guards or complexity-sensitive behavior.
416
-
417
- These tests should stay focused on performance-related expectations, not general normalization structure.
418
-
419
- ---
420
-
421
- ### Helper files
422
-
423
- #### `test/helpers/level-runner.js`
424
-
425
- Shared helper for running a query at a specific level.
426
-
427
- #### `test/helpers/level-cases.js`
428
-
429
- Shared fixed inputs used across level tests.
430
- Prefer adding reusable representative cases here instead of duplicating inline fixtures.
431
-
432
- #### `test/helpers/level-contract-runner.js`
433
-
434
- Shared `LEVELS` list and helpers for all-level contract suites.
130
+ ```ts
131
+ {
132
+ query, // normalized query
133
+ meta // debug / trace info
134
+ }
135
+ ```
435
136
 
436
137
  ---
437
138
 
438
- ### Rules for adding new tests
139
+ ## 🎯 Design philosophy
439
140
 
440
- #### When adding a new normalization rule
141
+ > If a rewrite might be wrong, don’t do it.
441
142
 
442
- Ask first:
443
-
444
- * Is this a public API behavior?
445
-
446
- * Add to `test/api/`
447
- * Is this enabled only at a specific level?
448
-
449
- * Add to `test/levels/`
450
- * Should this hold for all levels?
451
-
452
- * Add to `test/contracts/`
453
- * Is this about semantic preservation or randomized validation?
454
-
455
- * Add to `test/semantic/` or `test/property/`
456
- * Is this a bug fix for a previously broken case?
457
-
458
- * Add to `test/regression/`
143
+ * no schema assumptions
144
+ * no array guessing
145
+ * no unsafe merges
146
+ * deterministic output
147
+ * idempotent results
459
148
 
460
149
  ---
461
150
 
462
- #### When adding a new level
151
+ ## 🔍 Example
463
152
 
464
- At minimum, update all of the following:
153
+ ```ts
154
+ const result = normalizeQuery({
155
+ $and: [
156
+ { status: "open" },
157
+ { status: { $in: ["open", "closed"] } }
158
+ ]
159
+ });
465
160
 
466
- 1. add a new `test/levels/<level>-level.test.js`
467
- 2. register the level in `test/helpers/level-contract-runner.js`
468
- 3. ensure all-level contract suites cover it
469
- 4. add at least one contrast case against the adjacent level
161
+ console.log(result.query);
162
+ // { status: "open" }
163
+ ```
470
164
 
471
165
  ---
472
166
 
473
- ### Testing style guidance
474
-
475
- Prefer:
476
-
477
- * example-based tests for level boundaries
478
- * query-shape assertions
479
- * contrast tests between adjacent levels
480
- * shared fixtures for representative cases
167
+ ## 📚 Docs
481
168
 
482
- Avoid:
169
+ * [`SPEC.md`](SPEC.md) — behavior spec
170
+ * [`docs/normalization-matrix.md`](docs/normalization-matrix.md) — rule coverage by operator and level
171
+ * [`docs/CANONICAL_FORM.md`](docs/CANONICAL_FORM.md) — canonical output shape and idempotency
172
+ * [`CHANGELOG.md`](CHANGELOG.md) — release notes
173
+ * [`test/REGRESSION.md`](test/REGRESSION.md) — reproducing property / semantic test failures
483
174
 
484
- * coupling level tests to unstable implementation details
485
- * repeating the same fixture with only superficial assertion changes
486
- * putting default-level behavior inside a specific level test
487
- * mixing exports/API tests with normalization behavior tests
175
+ **中文:** [`README.zh-CN.md`](README.zh-CN.md) · [`SPEC.zh-CN.md`](SPEC.zh-CN.md) · [`docs/normalization-matrix.zh-CN.md`](docs/normalization-matrix.zh-CN.md) · [`CHANGELOG.zh-CN.md`](CHANGELOG.zh-CN.md)
488
176
 
489
177
  ---
490
178
 
491
- ### Practical rule of thumb
179
+ ## 🧪 Testing
492
180
 
493
- * `api/` answers: **how the library is used**
494
- * `levels/` answers: **what each level does and does not do**
495
- * `contracts/` answers: **what must always remain true**
496
- * `semantic/property/regression/performance` answer: **whether the system remains correct, robust, and efficient**
181
+ * semantic equivalence tests (real MongoDB)
182
+ * property-based testing
183
+ * regression suites
497
184
 
498
185
  ---
499
186
 
500
- ### npm scripts and property-test tooling
501
-
502
- Randomized semantic tests use **`mongodb-memory-server`** + **`fast-check`** to compare **real** `find` results (same `sort` / `skip` / `limit`, projection `{ _id: 1 }`) before and after `normalizeQuery` on a **fixed document schema** and a **restricted operator set** (see `test/helpers/arbitraries.js`). They assert matching **`_id` order**, **idempotency** of the returned `query`, and (for opaque operators) **non-crash / stable second pass** only. **`FC_SEED` / `FC_RUNS` defaults are centralized in `test/helpers/fc-config.js`** (also re-exported from `arbitraries.js`).
503
-
504
- To **avoid downloading** a MongoDB binary, set one of **`MONGODB_BINARY`**, **`MONGOD_BINARY`**, or **`MONGOMS_SYSTEM_BINARY`** to your local `mongod` path before running semantic tests (see `test/helpers/mongo-fixture.js`).
505
-
506
- * **`npm run test`** — build, then `test:unit`, then `test:semantic`.
507
- * **`npm run test:api`** — `test/api/**/*.test.js` only.
508
- * **`npm run test:levels`** — `test/levels/**/*.test.js` and `test/contracts/*.test.js`.
509
- * **`npm run test:unit`** — all `test/**/*.test.js` except `test/semantic/**`, `test/regression/**`, and `test/property/**` (includes `test/api/**`, `test/levels/**`, `test/contracts/**`, `test/performance/**`, and other unit tests).
510
- * **`npm run test:semantic`** — semantic + regression + property folders (defaults when env unset: see `fc-config.js`).
511
- * **`npm run test:semantic:quick`** — lower **`FC_RUNS`** (script sets `45`) + **`FC_SEED=42`**, still runs `test/regression/**` and `test/property/**`.
512
- * **`npm run test:semantic:ci`** — CI-oriented env (`FC_RUNS=200`, `FC_SEED=42` in script).
513
-
514
- Override property-test parameters: **`FC_SEED`**, **`FC_RUNS`**, optional **`FC_QUICK=1`** (see `fc-config.js`). How to reproduce failures and when to add a fixed regression case: **`test/REGRESSION.md`**.
515
-
516
- Full-text, geo, heavy **`$expr`**, **`$where`**, aggregation, collation, etc. stay **out** of the main semantic equivalence generator; opaque contracts live in **`test/contracts/opaque-operators.all-levels.test.js`**.
517
-
518
- ---
187
+ ## Philosophy
519
188
 
520
- ## Contributor notes
189
+ Most query tools try to be smart.
521
190
 
522
- - [SPEC.md](SPEC.md) behavior-oriented specification.
523
- - [docs/CANONICAL_FORM.md](docs/CANONICAL_FORM.md) — idempotency and canonical shape notes.
191
+ This one tries to be **correct**.