常见问题


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。 我对 virtualmiddlewaregetter/settermethod 使用箭头函数,而 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);
});

Atype 属性在 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:编译后无法覆盖..模型"?

Amongoose.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 上点击 访问它 并使用 编辑 按钮发送拉取请求。