常见问题
Q。 连接到 localhost
时出现错误 connect ECONNREFUSED ::1:27017
。 为什么?
简单的解决方案是将 localhost
替换为 127.0.0.1
。
发生此错误的原因是 Node.js 18 及更高版本默认情况下更喜欢 IPv6 地址而不是 IPv4。
并且,大多数 Linux 和 OSX 机器默认在 /etc/hosts
中有一个 ::1 localhost
条目。
这意味着 Node.js 18 将假定 localhost
表示 IPv6 ::1
地址。
并且 MongoDB 默认不接受 IPv6 连接。
你还可以通过 在 MongoDB 服务器上启用 IPv6 支持 修复此错误。
Q。 操作 ...
在 10000 毫秒后超时。 是什么赋予了?
A。 从本质上讲,这个问题源于未连接到 MongoDB。 你可以在连接 MongoDB 之前使用 Mongoose,但必须在某个时刻进行连接。 例如:
await mongoose.createConnection(mongodbUri).asPromise();
const Test = mongoose.model('Test', schema);
await Test.findOne(); // Will throw "Operation timed out" error because didn't call `mongoose.connect()`
await mongoose.connect(mongodbUri);
const db = mongoose.createConnection();
const Test = db.model('Test', schema);
await Test.findOne(); // Will throw "Operation timed out" error because `db` isn't connected to MongoDB
Q。 我能够在本地连接,但是当我尝试连接到 MongoDB Atlas 时,出现此错误。 是什么赋予了?
你必须确保已将 mongodb 上的 IP 列入白名单,以允许 Mongoose 连接。
你可以允许所有具有 0.0.0.0/0
的 ip 进行访问。
Q。 x.$y 不是函数。 是什么赋予了?
A。 此问题是由于安装了多个彼此不兼容的 mongoose 版本而导致的。
运行 npm list | grep "mongoose"
来查找并解决问题。
如果你将结构或模型存储在单独的 npm 包中,请在单独的包中将 Mongoose 列出在 peerDependencies
而不是 dependencies
中。
Q。 我将结构属性声明为 unique
,但我仍然可以保存重复项。 是什么赋予了?
A。 Mongoose 本身不处理 unique
: { name: { type: String, unique: true } }
只是创建 name
上的 MongoDB 唯一索引 的简写。
例如,如果 MongoDB 在 name
上还没有唯一索引,则尽管 unique
为 true,下面的代码也不会出错。
const schema = new mongoose.Schema({
name: { type: String, unique: true }
});
const Model = db.model('Test', schema);
// No error, unless index was already built
await Model.create([{ name: 'Val' }, { name: 'Val' }]);
但是,如果你等待使用 Model.on('index')
事件构建索引,则尝试保存重复项将正确出错。
const schema = new mongoose.Schema({
name: { type: String, unique: true }
});
const Model = db.model('Test', schema);
// Wait for model's indexes to finish. The `init()`
// function is idempotent, so don't worry about triggering an index rebuild.
await Model.init();
// Throws a duplicate key error
await Model.create([{ name: 'Val' }, { name: 'Val' }]);
MongoDB 保留索引,因此如果你从新数据库开始或运行 db.dropDatabase()
,则只需重建索引。 在生产环境中,你应该 使用 MongoDB shell 创建索引 而不是依赖 mongoose 来为你做这件事。 结构的 unique
选项方便开发和文档编制,但 mongoose 不是索引管理解决方案。
Q。 当我在结构中有嵌套属性时,Mongoose 默认添加空对象。 为什么?
const schema = new mongoose.Schema({
nested: {
prop: String
}
});
const Model = db.model('Test', schema);
// The below prints `{ _id: /* ... */, nested: {} }`, mongoose assigns
// `nested` to an empty object `{}` by default.
console.log(new Model());
A。 这是一个性能优化。 这些空对象不会保存到数据库中,也不会出现在结果 toObject()
中,也不会出现在 JSON.stringify()
输出中,除非你关闭 minimize
选项。
出现此行为的原因是 Mongoose 的更改检测和 getter/setter 是基于 Object.defineProperty()
的。
为了支持嵌套属性的更改检测,而又不会产生每次创建文档时运行 Object.defineProperty()
的开销,mongoose 在编译模型时在 Model
原型上定义属性。
因为 mongoose 需要为 nested.prop
定义 getter 和 setter,所以 nested
必须始终定义为 mongoose 文档上的对象,即使 nested
在底层 POJO 上未定义。
Q。 我对 virtual、middleware、getter/setter 或 method 使用箭头函数,而 this
的值是错误的。
A。 箭头函数 处理 this
关键字的方式与传统函数不同。
Mongoose getter/setter 依赖 this
来让你访问正在写入的文档,但此功能不适用于箭头函数。 not 是否对 mongoose getter/setter 使用箭头函数,除非不打算访问 getter/setter 中的文档。
// Do **NOT** use arrow functions as shown below unless you're certain
// that's what you want. If you're reading this FAQ, odds are you should
// just be using a conventional function.
const schema = new mongoose.Schema({
propWithGetter: {
type: String,
get: v => {
// Will **not** be the doc, do **not** use arrow functions for getters/setters
console.log(this);
return v;
}
}
});
// `this` will **not** be the doc, do **not** use arrow functions for methods
schema.method.arrowMethod = () => this;
schema.virtual('virtualWithArrow').get(() => {
// `this` will **not** be the doc, do **not** use arrow functions for virtuals
console.log(this);
});
Q。 我有一个名为 type
的嵌入属性,如下所示:
const holdingSchema = new Schema({
// You might expect `asset` to be an object that has 2 properties,
// but unfortunately `type` is special in mongoose so mongoose
// interprets this schema to mean that `asset` is a string
asset: {
type: String,
ticker: String
}
});
但是 mongoose 给了我一个 CastError 告诉我,当我尝试用 asset
对象保存 Holding
时,它无法将对象转换为字符串。 为什么是这样?
Holding.create({ asset: { type: 'stock', ticker: 'MDB' } }).catch(error => {
// Cast to String failed for value "{ type: 'stock', ticker: 'MDB' }" at path "asset"
console.error(error);
});
A。 type
属性在 mongoose 中比较特殊,因此当你说 type: String
时,mongoose 会将其解释为类型声明。 在上面的结构中,mongoose 认为 asset
是一个字符串,而不是一个对象。 改为这样做:
const holdingSchema = new Schema({
// This is how you tell mongoose you mean `asset` is an object with
// a string property `type`, as opposed to telling mongoose that `asset`
// is a string.
asset: {
type: { type: String },
ticker: String
}
});
Q。 我正在数组下填充嵌套属性,如下代码所示:
new Schema({
arr: [{
child: { ref: 'OtherModel', type: Schema.Types.ObjectId }
}]
});
.populate({ path: 'arr.child', options: { sort: 'name' } })
不会按 arr.child.name
排序?
A。 参见 这个 GitHub 问题。 这是一个已知问题,但修复起来非常困难。
Q。 我的模型上的所有函数调用都挂起,我做错了什么?
A。 默认情况下,mongoose 将缓冲你的函数调用,直到它可以连接到 MongoDB。 阅读 连接文档的缓冲部分 了解更多信息。
Q。 如何启用调试?
A。 设置 debug
选项:
// all executed methods log output to console
mongoose.set('debug', true);
// disable colors in debug mode
mongoose.set('debug', { color: false });
// get mongodb-shell friendly output (ISODate)
mongoose.set('debug', { shell: true });
有关更多调试选项(流、回调),请参阅 .set()
下的 'debug' 选项。
Q。 我的 save()
回调永远不会执行。 我究竟做错了什么?
A。 所有 collection
操作(插入、删除、查询等)都会排队,直到 Mongoose 成功连接到 MongoDB。 你可能还没有调用 Mongoose 的 connect()
或 createConnection()
函数。
在 Mongoose 5.11 中,有一个 bufferTimeoutMS
选项(默认设置为 10000),用于配置 Mongoose 在抛出错误之前允许操作保持缓冲的时间。
如果你想在整个应用中选择退出 Mongoose 的缓冲机制,请将全局 bufferCommands
选项设置为 false:
mongoose.set('bufferCommands', false);
你可能不想选择退出 Mongoose 的缓冲机制,而是希望减少 bufferTimeoutMS
以使 Mongoose 仅缓冲一小段时间。
// If an operation is buffered for more than 500ms, throw an error.
mongoose.set('bufferTimeoutMS', 500);
Q。 我应该为每个数据库操作创建/销毁一个新连接吗?
A。 不。 当应用启动时打开连接,并保持打开状态直到应用关闭。
Q。 为什么我使用 nodemon/测试框架时得到 "OverwriteModelError:编译后无法覆盖..模型"?
A。 mongoose.model('ModelName', schema)
要求 'ModelName' 是唯一的,因此你可以使用 mongoose.model('ModelName')
访问模型。
如果将 mongoose.model('ModelName', schema);
放入 Mocha beforeEach()
钩子 中,此代码将在 every 测试之前尝试创建名为 'ModelName' 的新模型,因此你将收到错误。 确保你只创建一个具有给定名称 once 的新模型。 如果需要创建多个同名模型,请创建一个新连接并将模型绑定到该连接。
const mongoose = require('mongoose');
const connection = mongoose.createConnection(/* ... */);
// use mongoose.Schema
const kittySchema = mongoose.Schema({ name: String });
// use connection.model
const Kitten = connection.model('Kitten', kittySchema);
Q。 如何更改 mongoose 将数组路径初始化为空数组的默认行为,以便我可以在创建文档时需要真实数据?
A。 你可以将数组的默认值设置为返回 undefined
的函数。
const CollectionSchema = new Schema({
field1: {
type: [String],
default: void 0
}
});
Q。 如何初始化 null
的数组路径?
A。 你可以将数组的默认值设置为返回 null
的函数。
const CollectionSchema = new Schema({
field1: {
type: [String],
default: () => { return null; }
}
});
Q。 为什么我的聚合 $match 在处理日期时无法返回查找查询返回的文档?
A。 Mongoose 不会强制聚合管道阶段,因为对于 $project、$group 等,属性的类型可能会在聚合期间发生变化。 如果你想使用聚合框架按日期进行查询,你有责任确保传入有效的日期。
Q。 为什么不保存对日期对象(例如 date.setMonth(1);
)的就地修改?
doc.createdAt.setDate(2011, 5, 1);
doc.save(); // createdAt changes won't get saved!
A。 Mongoose 目前不监视日期对象的就地更新。 如果你有此功能的需求,欢迎在 这个 GitHub 问题 上讨论。 有几种解决方法:
doc.createdAt.setDate(2011, 5, 1);
doc.markModified('createdAt');
doc.save(); // Works
doc.createdAt = new Date(2011, 5, 1).setHours(4);
doc.save(); // Works
Q。 为什么在同一个文档上并行调用 save()
多次只会让第一个保存调用成功,而其余的则返回 ParallelSaveErrors?
A。 由于验证和中间件通常具有异步性质,因此在同一个文档上并行多次调用 save()
可能会导致冲突。 例如,验证同一路径,然后使其无效。
Q。 为什么 any 12 个字符串成功转换为 ObjectId?
A。 从技术上讲,任何 12 个字符的字符串都是有效的 ObjectId。
考虑使用像 /^[a-f0-9]{24}$/
这样的正则表达式来测试字符串是否正好是 24 个十六进制字符。
Q。 为什么 Mongoose Maps 中的键必须是字符串?
A。 因为 Map 最终存储在 MongoDB 中,其中键必须是字符串。
Q。 我将 Model.find(...).populate(...)
与 limit
选项一起使用,但得到的结果少于限制。 是什么赋予了?
A。 为了避免对从 find
查询返回的每个文档执行单独的查询,Mongoose 改为使用 (numDocuments * limit) 作为限制进行查询。 如果你需要正确的限制,你应该使用 perDocumentLimit 选项(Mongoose 5.9.0 中的新增功能)。 请记住,populate() 将为每个文档执行单独的查询。
Q。 我的查询/更新似乎执行了两次。 为什么会发生这种情况?
A。 重复查询的最常见原因是 将回调和 promise 与查询混合在一起。
这是因为将回调传递给查询函数(例如 find()
或 updateOne()
)会立即执行查询,而调用 then()
会再次执行查询。
混合 promise 和回调可能会导致数组中出现重复的条目。
例如,下面的代码将 2 个条目插入到 tags
数组中,**不只是 1.!!!IG18!!!
Something to add? 如果你想为此页面做出贡献,请在 github 上点击 访问它 并使用 编辑 按钮发送拉取请求。