Skip to content

fix(driver-memory): render the pipeline dump's RegExp operand instead of dropping it to {} (#7853) - #7869

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-7853-analytics-pipeline-dump-regexp
Aug 12, 2026
Merged

fix(driver-memory): render the pipeline dump's RegExp operand instead of dropping it to {} (#7853)#7869
huangyiirene merged 1 commit into
mainfrom
claude/issue-7853-analytics-pipeline-dump-regexp

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #7853

MemoryAnalyticsService.query() returns AnalyticsResult.sql from generateSqlFromPipeline() — a stage-by-stage dump of the mingo pipeline it actually executed, and the only thing an author debugging an in-memory chart is given. It dumped each stage with a bare JSON.stringify, and a RegExp has no own enumerable properties, so every pattern operand rendered as {}. The $match stage was reported as constraining name by an empty object: the one field the reader came for is the one the dump dropped.

Measured on origin/main @ 55635fc, executed

All twelve operators this face declares, before → after. Nine are byte-identical; the three that carry a pattern are the whole change:

operator before after
$contains {"name":{"$regex":{}}} {"name":{"$regex":"/et/"}}
$icontains {"name":{"$regex":{}}} {"name":{"$regex":"/[Bb][Ee][Tt]/"}}
$notContains {"name":{"$not":{"$regex":{}}}} {"name":{"$not":{"$regex":"/et/"}}}
$eq $ne $gt $gte $lt $lte $in $nin $exists unchanged, character for character

A metacharacter comparand now shows its escape too: {$contains: 'a.p'} dumps {"name":{"$regex":"/a\\.p/"}}, so an author can see that a.p ran as a literal and not as "any character between a and p" (#5567's direction). The dump is the only place that distinction is visible.

Nothing executed changes. This surface is explicitly not SQL — its own header says -- MongoDB Aggregation Pipeline on table: … — so it cannot render a predicate execution did not run. query()'s rows and generateSql()'s SQL are untouched; the diff is one replacer argument plus the function it names.

The rendering-form decision, and why it went against the lean

The two candidates were the pattern's own literal syntax (/et/) and the mongo-shaped {"$regex":"et","$options":""} that the rest of the dump speaks. The mongo form was the first choice for internal consistency, and measurement ruled it out on a structural ground rather than a cosmetic one: the RegExp sits at the $regex key, so a value replacer substituting the pair renders the doubled

{"name":{"$regex":{"$regex":"et","$options":""}}}

— a shape no mongo query has. Flattening it into the real mongo spelling means rewriting the parent object, which would make the dump disagree with the pipeline it claims to be dumping: what mingo executes at that key is a JS RegExp object, not a source/options pair. That trades a visibly degenerate rendering for a plausible-but-wrong one, which is the #5333 direction this card is explicitly graded away from.

The literal form is also the only one-token rendering that keeps the flags, and flags are not decoration on this axis: $icontains' fold lives in the pattern source (#6520) while $contains is case-exact (#7723 / #4706 Q2 = A), so a form that dropped them would recreate a smaller copy of this same information loss. No declared operator carries a flag today — measured, every builder folds into the source — so the segment renders empty; it is in the form because a fold that ever moved into a flag would otherwise vanish exactly the way the source did.

The check is strengthened, not duplicated

There was already a check on this dump — memory-driver-filter-logic-conformance.test.ts, "the emitted $match wraps the negation around a pattern instead of a bare scalar" — and it passed for the whole life of the defect. It asserted $not and $regex are present and even carried a comment conceding the loss ("JSON.stringify renders a RegExp as {}, so assert on the STRUCTURE"). That is precisely the blind spot the card names: {"$regex":{}} is non-empty and satisfies every presence-only assertion.

So it is strengthened in place rather than shadowed by a parallel file: four cases now assert the pattern text for each affected operator plus the metacharacter escape, and the structural assertion stays as the guard it was built to be.

Reverse-verified: with the replacer argument removed, all four new cases fail (expected '{"name":{"$regex":{}}}' not to contain '{}') and the pre-existing structural case stays green — measured confirmation that it was blind to this defect.

Assumptions the dispatch carried, measured

# assumption verdict
A contains / icontains / notContains reach the defect holds exactly — enumerated all twelve declared operators; those three and no others
B no other value type on this path is degenerate under JSON.stringify holds — a Date comparand is canonicalized to an ISO string by comparandsFor before it arrives (and toJSON runs before a replacer anyway). A BigInt comparand does throw, but out of mingo's own Query.compile during execution, before this dump is ever built, so no replacer here reaches it — stack captured
C the dump has no existing test asserting content falsified — the test above exists and passes today. Strengthened rather than duplicated, exactly as the falsification implies

Gates

gate result
@objectstack/driver-memory tests 712 passed / 23 files (was 708 / 23 — the four new cases)
@objectstack/service-analytics tests (the only consumer of this service outside the package) 1558 passed / 72 files
tsc --noEmit clean
pnpm lint clean
check:driver-conformance 40 covered / 0 DEBT / 0 exempt — unchanged
check:query-options-erasure holds — 67 unswept sites / 17 files, none new; baseline verified against 55635fc

Changeset: @objectstack/driver-memory patch — a user-visible debug string. content/docs/releases/ untouched. No adjacent card was folded in; nothing outside packages/drivers/driver-memory plus the changeset is touched.


Generated by Claude Code

… of dropping it to `{}` (#7853)

`AnalyticsResult.sql` from `generateSqlFromPipeline()` dumped each mingo stage
with a bare `JSON.stringify`. A `RegExp` has no own enumerable properties, so
every pattern operand rendered as `{}` — `{"name":{"$regex":{}}}` — and the one
field an author debugging an in-memory chart is looking for was the one the
dump dropped.

Measured across the twelve operators this face declares: exactly three carry a
pattern (`$contains`, `$icontains`, `$notContains` inside `$not`) and all three
were affected. They now render the pattern's own literal syntax, `/source/flags`
— chosen over the mongo-shaped `{$regex, $options}` because the `RegExp` sits AT
the `$regex` key, so a value replacer producing the pair renders the doubled
`{"$regex":{"$regex":"et","$options":""}}`, a shape no mongo query has, and
flattening it would make the dump disagree with the pipeline it dumps.

No executed behaviour changes: the dump is explicitly not SQL, and `query()`'s
rows, `generateSql()`'s SQL, and the other nine operators' dumps are unchanged.

The existing dump check asserted structure (`$not` wraps a `$regex`) and passed
throughout — it is strengthened here to assert the pattern TEXT for each
affected operator, since `{"$regex":{}}` is non-empty and satisfies any
presence-only assertion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzMdsWW2aZwGCEhfDLo1sT
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 12, 2026 12:43am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-memory.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-memory)
  • content/docs/deployment/vercel.mdx (via @objectstack/driver-memory)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-memory)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-memory)
  • content/docs/permissions/authentication.mdx (via @objectstack/driver-memory)
  • content/docs/plugins/index.mdx (via @objectstack/driver-memory)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-memory)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-memory)

1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-memory)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

driver-memory's AnalyticsResult.sql loses a RegExp operand to {}, so the pipeline dump shows no pattern at all

2 participants