fix(driver-memory,driver-mongodb): one storage form per temporal field type (#4047) - #4060
Merged
Merged
Conversation
…d type (#4047) The non-SQL counterpart of ADR-0053 D-B (#3912). Both drivers let the writer decide a datetime value's runtime type, and both compare across types by type bracket rather than by value — so a string comparand never matched a Date value, in either direction, for EVERY operator including $gte. SQLite's affinity rules at least left one half reachable; here a window silently answers with whichever half matches the comparand type. Both columns really were mixed: each driver's own created_at/updated_at default writes one form (new Date() on mongo, ISO text in memory) while REST/JSON writes, relative-date tokens and initialData fixtures supply the other. On MongoDB the dashboard's default window — string bounds against a BSON-Date created_at — returned NOTHING, worse than the final-day loss #3777 fixed. One canon per driver, chosen by what the store natively has, applied on write and to every filter comparand: - driver-mongodb: BSON Date (the dialect's native instant, its timestamptz) — create/update/updateMany/bulkCreate/bulkUpdate convert, and translateFilter takes a temporal-kind resolver so find(), deleteMany and the aggregation $match all coerce comparands. - driver-memory: canonical UTC ISO text (sorts chronologically under the string comparison mingo performs; survives JSON persistence) — create/ update/updateMany convert, and both filter lowerings coerce. Both learn their temporal fields from syncSchema; an undeclared object is left exactly as written — schemaless stores, so guessing a type from a value would coerce data the platform never claimed. driver-memory also converges rows already in the table when the schema arrives, catching initialData and persistence restores (the in-memory analogue of backfillCanonicalDatetimes, idempotent like it). Field.date stays timezone-naive text on both — an instant would invent a midnight and re-couple it to a zone. Ordering where this meets #3777/ #4042 is load-bearing: the calendar-day rewrite runs on the bare-day STRING first, and only the resulting bound is converted to storage form. Tests: row-result probes on both, red before the fix — mixed-form windows, Date-object comparands, bound semantics on top of the converged storage, $between + array spelling, date-column non-widening, undeclared passthrough; mongodb runs against a real MongoDB (mongodb-memory-server). ADR-0053 gains the D-E addendum. Closes #4047 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EkL3RGJrzjLEGURsLxfS2f
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 30, 2026 07:55
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4047。ADR-0053 D-B(#3912,SQL 侧「每方言单一存储形态」)的非 SQL 对应物;续 #4041 / #4048(边界语义)。
验收门(先红后绿)
两个驱动各一份行结果探针,在未动实现前先跑红:
{$gte:'2026-04-29', $lte:'2026-07-28'}['s_evening','s_morning'](Date 行全不可见)['d_midnight','d_yesterday'](字符串行全不可见)mongodb 探针跑在真实 MongoDB 上(
mongodb-memory-server,本环境可用),不是形状断言。根因
MongoDB 按 BSON 类型桶比较,mingo 对 JS 类型照搬同一规则:字符串比较值与
Date值互不匹配,所有操作符都如此($gte也不例外)。SQLite 的 affinity 至少还留下一半可达;这里窗口是「谁的类型对上就答谁」。而两个 datetime 列确实是混的:驱动自己的
created_at/updated_at默认值写一种形态(mongo 是new Date(),memory 是 ISO 文本),REST/JSON 写入、相对日期 token、initialData夹具供另一种。mongo 上 dashboard 默认窗口(字符串边界 × BSON Date 的created_at)返回空集——比 #3777 的「丢一天」更糟。每驱动一个 canon,按该存储原生有什么来选
datetimedriver-mongodbDatetimestamptzdriver-memoryField.date两边都保持时区无关文本——转成瞬时等于凭空造一个午夜并把它重新绑到某个时区。写路径与过滤路径共用同一规则:mongodb 的
create/update/updateMany/bulkCreate/bulkUpdate转换,translateFilter接一个 temporal-kind resolver(find/deleteMany/聚合$match全覆盖);memory 两条 lowering 与三个写路径同理。三个承重细节
syncSchema(驱动唯一被交付对象定义的地方)。从值猜类型会去强转平台从未声明拥有的数据——有专门测试钉住。initialData夹具与持久化恢复(两者都早于任何 schema 声明)。是backfillCanonicalDatetimes的内存版,同样幂等。mongo 不做自动 backfill——重写真实 collection 是迁移不是启动步骤,PR 描述里明说了这条边界。datetime列上丢失当天数据 —— 默认配置即命中 #3777/driver-memory / driver-mongodb:裸日期$lte上界在 datetime 值上同样丢当天数据(#3777 的非 SQL 驱动对齐) #4042 的顺序是承重的:日历日上界改写是日历操作,先在裸日期字符串上跑,只有产出的边界才转存储形态。反过来会把Date交给nextUtcCalendarDay(它会正确地拒绝放宽),整天窗口就悄悄缩回一个瞬时。覆盖
每驱动一套:混合形态窗口、Date 比较值、收敛后的边界语义(
$gte/$lt/完整 ISO 不放宽)、$between+ 数组拼写、date列不被放宽、未声明对象透传;memory 另有initialData追认与update()形态两条。测试
driver-memory194 ✅(含新 9)/driver-mongodb107 ✅(含新 7);两包turbo build(带 dts)✅;改动文件 eslint 干净。既有断言零改动。关联
@objectstack/formula.matchesFilterCondition(RLS 写侧check,第五个求值面)因层级约束(不依赖 core)未对齐 D-D/D-E 两条规则,归属待定(私有拷贝 vs 工具下沉 spec/types),已记入 ADR D-E4。🤖 Generated with Claude Code
https://claude.ai/code/session_01EkL3RGJrzjLEGURsLxfS2f
Generated by Claude Code