关于 meteor-mongo-minimongo
面向 Meteor 开发者的 MongoDB 与 Minimongo 使用指南,说明服务端和客户端的读写差异、索引及响应式数据驱动选择。
主要功能
- 01区分服务端与客户端读写 API
- 02使用异步 Mongo 操作
- 03为分页查询创建索引
- 04选择变更流、oplog 或轮询
- 05支持排序与查询的排序规则
适用场景
- 01编写跨服务端和客户端的查询
- 02为大型查询添加分页与索引
- 03排查响应式更新的驱动选择
- 04实现大小写不敏感的查询
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ meteor/ agent-skills --skill meteor-mongo-minimongo安装后在 Claude Code 中输入/meteor-mongo-minimongo确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/eval-cases.mdselectors-modifiers.mdserver-vs-client.md详细介绍
Mongo 和 Minimongo
Meteor 在同一代码库中提供了两种 Mongo API 实现。服务器通过异步驱动程序与 MongoDB 通信。客户端运行 Minimongo,这是一个内存中的同步 Mongo 模拟器,用于保存订阅发送过来的文档。
决策流程
- 这段代码在哪里运行?
- 仅在服务器端:使用
await Collection.*Async(...)。 - 仅在客户端:同步使用
Collection.*(...)。 - 同构代码(在共享代码中
import):使用await Collection.*Async(...)。 在客户端,操作在本地执行,但仍基于 Promise;在服务器端则会与 Mongo 通信。
- 仅在服务器端:使用
- 查询是否会选出超过一页的文档?添加
{ limit, skip },并添加与选择器匹配的索引。 - 是否在客户端从发布中读取数据?使用
find().fetch()(同步),不要使用await。数据已经在本地。
服务器端读取
const doc = await Posts.findOneAsync(id);
const list = await Posts.find({ ownerId }, {
fields: { title: 1 }, sort: { createdAt: -1 }, limit: 50,
}).fetchAsync();
const count = await Posts.find({ ownerId }).countAsync();
服务器端写入
const _id = await Posts.insertAsync({ title, ownerId });
await Posts.updateAsync({ _id }, { $set: { title } });
await Posts.removeAsync({ _id });
客户端读取(Minimongo)
异步 API 可用于同构代码。优先在共享代码中使用它,以便同一行代码在服务器端也能正常运行。
const doc = await Posts.findOneAsync(id); // works in shared/client/server
const list = await Posts.find({ ownerId }).fetchAsync();
在客户端,操作会读取内存中的 Minimongo,但异步 API 仍会返回真正的 Promise。await 之后的代码会在稍后的微任务中恢复执行。同步 API 在客户端也可用,但仅限客户端:
const doc = Posts.findOne(id); // client-only
const list = Posts.find({ ownerId }).fetch(); // client-only
如果调用作用域本身适合保持同步,并且强制使用 await 会导致异步逻辑扩散到渲染路径中,请选择同步 API。常见情况包括:
- 消费响应式数据的 React 渲染函数和钩子。
- Blaze 模板助手。
- Tracker 自动运行函数。
如果文件也可能在服务器端运行,或所在函数已经是 async,请选择异步 API(findOneAsync、fetchAsync)。
索引
索引仅在服务器端使用。在应用启动时创建索引:
import { Meteor } from "meteor/meteor";
import { Posts } from "/imports/api/posts";
Meteor.startup(async () => {
await Posts.createIndexAsync({ ownerId: 1, createdAt: -1 });
await Posts.createIndexAsync({ slug: 1 }, { unique: true });
});
根据等值筛选条件、排序字段、范围筛选条件以及可用的索引前缀,选择复合索引的键顺序。等值选择器中的 JavaScript 属性顺序不必与索引顺序一致。使用 Mongo shell(meteor mongo)中的 db.posts.find(...).explain("executionStats") 验证所选的查询计划。
响应式数据源:oplog 或变更流
核心驱动边界因版本而异:
| Meteor | 响应式行为 |
|---|---|
| 3.0 至 3.4 | 配置了 MONGO_OPLOG_URL 时使用 oplog;否则使用轮询。核心变更流和响应式顺序设置不可用。 |
| 3.5+ | 按下方默认顺序为每个查询选择驱动。 |
Meteor 3.5+ 的默认设置为:
changeStreams -> oplog -> polling
变更流要求 MongoDB 6+ 部署在副本集或分片集群上,观察器未排序,不使用
skip 或 limit,且选择器可由 Minimongo 编译。不符合条件的查询会转而使用
下一个已配置的驱动。只有配置了 MONGO_OPLOG_URL 时,oplog 才可用。
Meteor 3.6(已在 beta.3 的 [email protected] 上验证)也会拒绝用于
Change Streams 的 $where/$near 选择器(RC 版本解决为
[email protected]):Minimongo 无法依据 MongoDB 语义重新匹配实时事件。
Oplog 也会拒绝这些选择器,因此请确认有可用的轮询回退方案。Minimongo 不兼容的
投影也需要回退。MongoDB 支持、但被 Minimongo 拒绝的投影属于驱动边界,不代表
需要重写发布或架构。提出这种更广泛的更改前,请先验证回退方案。测试初始结果和
实时结果;不要仅为强制使用 Change Streams 而移除选择器、投影或分页。
较早的 3.5/beta.1 软件包可能会为 $where/$near 选择错误的驱动;对于受限版本,
请使用受支持的轮询配置,或经授权的兼容升级,然后重新运行完全相同的查询。
在 Meteor 3.5+ 上,可通过以下方式覆盖应用级顺序:
METEOR_REACTIVITY_ORDER=oplog,polling,或使用:
{
"packages": {
"mongo": {
"reactivity": ["oplog", "polling"]
}
}
}
在 Meteor 3.5+ 上,disable-oplog 软件包只会移除 oplog 步骤,不会禁用变更流。
使用 reactivity: ["polling"] 强制启用轮询。在 Meteor 3.0 至 3.4 上,请勿添加
这些设置;请先升级。
如果核心变更流导致观察器事件重复或登录间歇性断开,请先检查解析到的 mongo
版本,再考虑重写查询或禁用响应式功能。[email protected](Meteor 3.5.2)修复了
对因果主节点快照已涵盖事件的重放问题。较早的 3.5 软件包不包含此修复;
请升级并重新运行失败的操作序列。这不会改变驱动适用条件,也不能成为按时间戳
随意丢弃应用事件的理由。如果问题仍然存在,请捕获观察器的顺序;原因不明时请使用
meteor-debugging。
排序规则(Meteor 3.5+)
使用 collation,在 Mongo 和 Minimongo 中进行区域设置感知或不区分大小写的
选择和排序。请为服务器查询创建使用相同排序规则的索引:
const collation = { locale: "en", strength: 2 };
const users = await Users.find(
{ email: "[email protected]" },
{ collation },
).fetchAsync();
await Users.createIndexAsync({ email: 1 }, { collation });
Minimongo 支持 locale、1 至 3 的 strength、caseLevel、
numericOrdering 和 caseFirst。其他 Mongo 排序规则选项仅由服务器支持,
并会被 Minimongo 忽略。
反模式
- 在服务器上使用同步 Mongo。该功能已在 Meteor 3 中移除。
- 在共享代码中使用同步 Mongo(
findOne、insert、update、remove)。 文件一旦在服务器上导入,就会出问题。 - 在服务器上执行无界
find。始终使用limit。 - 发布时忘记使用
fields投影。始终指定投影。 - 假设异步 Minimongo API 会在当前调用中恢复执行。即使底层读取发生在本地,它仍会返回 Promise。
另请参阅
references/server-vs-client.mdreferences/selectors-modifiers.mdreferences/eval-cases.md