常见问题
¥FAQ
Q。连接到 localhost
时出现错误 connect ECONNREFUSED ::1:27017
。为什么?
¥Q. I get an error connect ECONNREFUSED ::1:27017
when connecting to localhost
. Why?
简单的解决方案是将 localhost
替换为 127.0.0.1
。
¥The easy solution is to replace localhost
with 127.0.0.1
.
发生此错误的原因是 Node.js 18 及更高版本默认情况下更喜欢 IPv6 地址而不是 IPv4。并且,大多数 Linux 和 OSX 机器默认在 /etc/hosts
中有一个 ::1 localhost
条目。这意味着 Node.js 18 将假定 localhost
表示 IPv6 ::1
地址。并且 MongoDB 默认不接受 IPv6 连接。
¥The reason why this error happens is that Node.js 18 and up prefer IPv6 addresses over IPv4 by default.
And, most Linux and OSX machines have a ::1 localhost
entry in /etc/hosts
by default.
That means that Node.js 18 will assume that localhost
means the IPv6 ::1
address.
And MongoDB doesn't accept IPv6 connections by default.
你还可以通过 在 MongoDB 服务器上启用 IPv6 支持 修复此错误。
¥You can also fix this error by enabling IPv6 support on your MongoDB server.
Q。操作 ...
在 10000 毫秒后超时。是什么赋予了?
¥Q. Operation ...
timed out after 10000 ms. What gives?
答:这个问题的核心是没有连接到 MongoDB。你可以在连接 MongoDB 之前使用 Mongoose,但必须在某个时刻进行连接。例如:
¥A. At its core, this issue stems from not connecting to MongoDB. You can use Mongoose before connecting to MongoDB, but you must connect at some point. For example:
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 时,出现此错误。是什么赋予了?
¥ Q. I am able to connect locally but when I try to connect to MongoDB Atlas I get this error. What gives?
你必须确保已将 mongodb 上的 IP 列入白名单,以允许 Mongoose 连接。你可以允许所有具有 0.0.0.0/0
的 ip 进行访问。
¥You must ensure that you have whitelisted your ip on mongodb to allow Mongoose to connect.
You can allow access from all ips with 0.0.0.0/0
.
Q。x.$__y 不是函数。是什么赋予了?
¥Q. x.$__y is not a function. What gives?
答:此问题是由于安装了多个版本的 mongoose 且彼此不兼容造成的。运行 npm list | grep "mongoose"
来查找并解决问题。如果你将结构或模型存储在单独的 npm 包中,请在单独的包中将 Mongoose 列出在 peerDependencies
而不是 dependencies
中。
¥A. This issue is a result of having multiple versions of mongoose installed that are incompatible with each other.
Run npm list | grep "mongoose"
to find and remedy the problem.
If you're storing schemas or models in a separate npm package, please list Mongoose in peerDependencies
rather than dependencies
in your separate package.
Q。我将结构属性声明为 unique
,但我仍然可以保存重复项。是什么赋予了?
¥Q. I declared a schema property as unique
but I can still save duplicates. What gives?
A. Mongoose 本身不处理 unique
:{ name: { type: String, unique: true } }
只是创建 name
上的 MongoDB 唯一索引 的简写。例如,如果 MongoDB 在 name
上还没有唯一索引,则尽管 unique
为 true,下面的代码也不会出错。
¥A. Mongoose doesn't handle unique
on its own: { name: { type: String, unique: true } }
is just a shorthand for creating a MongoDB unique index on name
.
For example, if MongoDB doesn't already have a unique index on name
, the below code will not error despite the fact that unique
is 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')
事件构建索引,则尝试保存重复项将正确出错。
¥However, if you wait for the index to build using the Model.on('index')
event, attempts to save duplicates will correctly error.
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 不是索引管理解决方案。
¥MongoDB persists indexes, so you only need to rebuild indexes if you're starting
with a fresh database or you ran db.dropDatabase()
. In a production environment,
you should create your indexes using the MongoDB shell
rather than relying on mongoose to do it for you. The unique
option for schemas is
convenient for development and documentation, but mongoose is not an index management solution.
Q。当我在结构中有嵌套属性时,Mongoose 默认添加空对象。为什么?
¥Q. When I have a nested property in a schema, mongoose adds empty objects by default. Why?
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
选项。
¥A. This is a performance optimization. These empty objects are not saved
to the database, nor are they in the result toObject()
, nor do they show
up in JSON.stringify()
output unless you turn off the minimize
option.
出现此行为的原因是 Mongoose 的更改检测和 getter/setter 是基于 Object.defineProperty()
的。为了支持嵌套属性的更改检测,而又不会产生每次创建文档时运行 Object.defineProperty()
的开销,mongoose 在编译模型时在 Model
原型上定义属性。因为 mongoose 需要为 nested.prop
定义 getter 和 setter,所以 nested
必须始终定义为 mongoose 文档上的对象,即使 nested
在底层 POJO 上未定义。
¥The reason for this behavior is that Mongoose's change detection
and getters/setters are based on Object.defineProperty()
.
In order to support change detection on nested properties without incurring
the overhead of running Object.defineProperty()
every time a document is created,
mongoose defines properties on the Model
prototype when the model is compiled.
Because mongoose needs to define getters and setters for nested.prop
, nested
must always be defined as an object on a mongoose document, even if nested
is undefined on the underlying POJO.
Q。我对 virtual、中间件、getter/setter 或 method 使用箭头函数,而 this
的值是错误的。
¥Q. I'm using an arrow function for a virtual, middleware, getter/setter, or method and the value of this
is wrong.
A. 箭头函数 处理 this
关键字的方式与传统函数不同。Mongoose getter/setter 依赖 this
来让你访问正在写入的文档,但此功能不适用于箭头函数。不要对 mongoose getter/setter 使用箭头函数,除非不打算访问 getter/setter 中的文档。
¥A. Arrow functions handle the this
keyword differently than conventional functions.
Mongoose getters/setters depend on this
to give you access to the document that you're writing to, but this functionality does not work with arrow functions. Do not use arrow functions for mongoose getters/setters unless do not intend to access the document in the 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
的嵌入属性,如下所示:
¥Q. I have an embedded property named type
like this:
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
时,它无法将对象转换为字符串。为什么是这样?
¥But mongoose gives me a CastError telling me that it can't cast an object
to a string when I try to save a Holding
with an asset
object. Why
is this?
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
是一个字符串,而不是一个对象。改为这样做:
¥A. The type
property is special in mongoose, so when you say
type: String
, mongoose interprets it as a type declaration. In the
above schema, mongoose thinks asset
is a string, not an object. Do
this instead:
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。我正在数组下填充嵌套属性,如下代码所示:
¥Q. I'm populating a nested property under an array like the below code:
new Schema({
arr: [{
child: { ref: 'OtherModel', type: Schema.Types.ObjectId }
}]
});
.populate({ path: 'arr.child', options: { sort: 'name' } })
不会按 arr.child.name
排序?
¥.populate({ path: 'arr.child', options: { sort: 'name' } })
won't sort by arr.child.name
?
答:参见 这个 GitHub 问题。这是一个已知问题,但修复起来非常困难。
¥A. See this GitHub issue. It's a known issue but one that's exceptionally difficult to fix.
Q。我的模型上的所有函数调用都挂起,我做错了什么?
¥Q. All function calls on my models hang, what am I doing wrong?
答:默认情况下,mongoose 将缓冲你的函数调用,直到它可以连接到 MongoDB。阅读 连接文档的缓冲部分 了解更多信息。
¥A. By default, mongoose will buffer your function calls until it can connect to MongoDB. Read the buffering section of the connection docs for more information.
Q。如何启用调试?
¥Q. How can I enable debugging?
A. 设置 debug
选项:
¥A. Set the debug
option:
// 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' 选项。
¥For more debugging options (streams, callbacks), see the 'debug' option under .set()
.
Q。我的 save()
回调永远不会执行。我究竟做错了什么?
¥Q. My save()
callback never executes. What am I doing wrong?
A. 所有 collection
操作(插入、删除、查询等)都会排队,直到 Mongoose 成功连接到 MongoDB。你可能还没有调用 Mongoose 的 connect()
或 createConnection()
函数。
¥A. All collection
actions (insert, remove, queries, etc.) are queued
until Mongoose successfully connects to MongoDB. It is likely you haven't called Mongoose's
connect()
or createConnection()
function yet.
在 Mongoose 5.11 中,有一个 bufferTimeoutMS
选项(默认设置为 10000),用于配置 Mongoose 在抛出错误之前允许操作保持缓冲的时间。
¥In Mongoose 5.11, there is a bufferTimeoutMS
option (set to 10000 by default) that configures how long
Mongoose will allow an operation to stay buffered before throwing an error.
如果你想在整个应用中选择退出 Mongoose 的缓冲机制,请将全局 bufferCommands
选项设置为 false:
¥If you want to opt out of Mongoose's buffering mechanism across your entire
application, set the global bufferCommands
option to false:
mongoose.set('bufferCommands', false);
你可能不想选择退出 Mongoose 的缓冲机制,而是希望减少 bufferTimeoutMS
以使 Mongoose 仅缓冲一小段时间。
¥Instead of opting out of Mongoose's buffering mechanism, you may want to instead reduce bufferTimeoutMS
to make Mongoose only buffer for a short time.
// If an operation is buffered for more than 500ms, throw an error.
mongoose.set('bufferTimeoutMS', 500);
Q。我应该为每个数据库操作创建/销毁一个新连接吗?
¥Q. Should I create/destroy a new connection for each database operation?
答:没有。当应用启动时打开连接,并保持打开状态直到应用关闭。
¥A. No. Open your connection when your application starts up and leave it open until the application shuts down.
Q。为什么我收到“OverwriteModelError:无法覆盖..当我使用 nodemon /测试框架时模型一次编译”?
¥Q. Why do I get "OverwriteModelError: Cannot overwrite .. model once compiled" when I use nodemon / a testing framework?
A. mongoose.model('ModelName', schema)
要求 'ModelName' 是唯一的,因此你可以使用 mongoose.model('ModelName')
访问模型。如果将 mongoose.model('ModelName', schema);
放入 Mocha beforeEach()
钩子 中,则此代码将在每次测试之前尝试创建一个名为 'ModelName' 的新模型,因此你将收到错误。确保仅使用给定名称创建一次新模型。如果需要创建多个同名模型,请创建一个新连接并将模型绑定到该连接。
¥A. mongoose.model('ModelName', schema)
requires 'ModelName' to be
unique, so you can access the model by using mongoose.model('ModelName')
.
If you put mongoose.model('ModelName', schema);
in a
mocha beforeEach()
hook, this code will
attempt to create a new model named 'ModelName' before every test,
and so you will get an error. Make sure you only create a new model with
a given name once. If you need to create multiple models with the
same name, create a new connection and bind the model to the connection.
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 将数组路径初始化为空数组的默认行为,以便我可以在创建文档时需要真实数据?
¥Q. How can I change mongoose's default behavior of initializing an array path to an empty array so that I can require real data on document creation?
A. 可以将数组的默认值设置为 undefined
。
¥A. You can set the default of the array to undefined
.
const CollectionSchema = new Schema({
field1: {
type: [String],
default: void 0
}
});
Q。如何初始化 null
的数组路径?
¥Q. How can I initialize an array path to null
?
A. 可以将数组的默认值设置为 null
。
¥A. You can set the default of the array to null
.
const CollectionSchema = new Schema({
field1: {
type: [String],
default: null
}
});
Q。为什么我的聚合 $match 在处理日期时无法返回查找查询返回的文档?
¥Q. Why does my aggregate $match fail to return the document that my find query returns when working with dates?
答:Mongoose 不会转换聚合管道阶段,因为对于 $project、$group 等,属性的类型可能会在聚合期间发生变化。如果你想使用聚合框架按日期进行查询,你有责任确保传入有效的日期。
¥A. Mongoose does not cast aggregation pipeline stages because with $project, $group, etc. the type of a property may change during the aggregation. If you want to query by date using the aggregation framework, you're responsible for ensuring that you're passing in a valid date.
Q。为什么不保存对日期对象(例如 date.setMonth(1);
)的就地修改?
¥Q. Why don't in-place modifications to date objects
(e.g. date.setMonth(1);
) get saved?
doc.createdAt.setDate(2011, 5, 1);
doc.save(); // createdAt changes won't get saved!
答:Mongoose 目前不监视日期对象的就地更新。如果你有此功能的需求,欢迎在 这个 GitHub 问题 上讨论。有几种解决方法:
¥A. Mongoose currently doesn't watch for in-place updates to date objects. If you have need for this feature, feel free to discuss on this GitHub issue. There are several workarounds:
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?
¥Q. Why does calling save()
multiple times on the same document in parallel only let
the first save call succeed and return ParallelSaveErrors for the rest?
答:由于验证和中间件一般具有异步性质,对同一个文档多次并行调用 save()
可能会导致冲突。例如,验证同一路径,然后使其无效。
¥A. Due to the asynchronous nature of validation and middleware in general, calling
save()
multiple times in parallel on the same doc could result in conflicts. For example,
validating, and then subsequently invalidating the same path.
Q。为什么任何 12 个字符的字符串都能成功转换为 ObjectId?
¥Q. Why is any 12 character string successfully cast to an ObjectId?
A. 从技术上讲,任何 12 个字符的字符串都是有效的 ObjectId。考虑使用像 /^[a-f0-9]{24}$/
这样的正则表达式来测试字符串是否正好是 24 个十六进制字符。
¥A. Technically, any 12 character string is a valid ObjectId.
Consider using a regex like /^[a-f0-9]{24}$/
to test whether a string is exactly 24 hex characters.
Q。为什么 Mongoose Maps 中的键必须是字符串?
¥Q. Why do keys in Mongoose Maps have to be strings?
答:因为 Map 最终存储在 MongoDB 中,其中键必须是字符串。
¥A. Because the Map eventually gets stored in MongoDB where the keys must be strings.
Q。我将 Model.find(...).populate(...)
与 limit
选项一起使用,但得到的结果少于限制。是什么赋予了?
¥Q. I am using Model.find(...).populate(...)
with the limit
option, but getting fewer results than the limit. What gives?
答:为了避免对 find
查询返回的每个文档执行单独的查询,Mongoose 改为使用 (numDocuments * limit) 作为限制进行查询。如果你需要正确的限制,你应该使用 perDocumentLimit 选项(Mongoose 5.9.0 中的新增功能)。请记住,populate() 将为每个文档执行单独的查询。
¥A. In order to avoid executing a separate query for each document returned from the find
query, Mongoose
instead queries using (numDocuments * limit) as the limit. If you need the correct limit, you should use the
perDocumentLimit option (new in Mongoose 5.9.0). Just keep in
mind that populate() will execute a separate query for each document.
Q。我的查询/更新似乎执行了两次。为什么会发生这种情况?
¥Q. My query/update seems to execute twice. Why is this happening?
答:重复查询的最常见原因是将回调和 Promise 与查询混合在一起。这是因为将回调传递给查询函数(例如 find()
或 updateOne()
)会立即执行查询,而调用 then()
会再次执行查询。
¥A. The most common cause of duplicate queries is mixing callbacks and promises with queries.
That's because passing a callback to a query function, like find()
or updateOne()
,
immediately executes the query, and calling then()
executes the query again.
混合 promise 和回调可能会导致数组中出现重复的条目。例如,下面的代码将 2 个条目插入到 tags
数组中,*不仅仅是 1 个。
¥Mixing promises and callbacks can lead to duplicate entries in arrays.
For example, the below code inserts 2 entries into the tags
array, *not just 1.
const BlogPost = mongoose.model('BlogPost', new Schema({
title: String,
tags: [String]
}));
// Because there's both `await` **and** a callback, this `updateOne()` executes twice
// and thus pushes the same string into `tags` twice.
const update = { $push: { tags: ['javascript'] } };
await BlogPost.updateOne({ title: 'Introduction to Promises' }, update, (err, res) => {
console.log(res);
});
有什么要补充的吗?
¥Something to add?
如果你想为此页面做出贡献,请在 github 上点击 访问它 并使用 编辑 按钮发送拉取请求。
¥If you'd like to contribute to this page, please visit it on github and use the Edit button to send a pull request.