【问题标题】:Mongoose overwrite the document rather that `$set` fieldsMongoose 覆盖文档而不是 `$set` 字段
【发布时间】:2018-03-31 01:52:06
【问题描述】:

说,我有一个文件:

{
  _id: 'some_mongodb_id',
  name: 'john doe',
  phone: '+12345678901',
}

我想更新这个文档:

.findOneAndUpdate({_id: 'some_mongodb_id'}, {name: 'Dan smith'})

结果应该是这样的:

{
  _id: 'some_mongodb_id',
  name: 'Dan smith',
}

应删除未指定的属性。

我该怎么做?

【问题讨论】:

  • 您是否尝试使用更新功能(而不是 findOneAndUpdate)?
  • @israel.zinc 我有,没区别
  • @israel.zinc 这不会有什么不同。 “猫鼬”通过将$set 添加到“更新对象”中来执行此操作,其中标准驱动程序将“按原样”传递该对象。因此,对于 mongoose,需要“关闭”这种行为,以使其像您实际要求的那样工作。

标签: node.js mongodb mongoose


【解决方案1】:

实际上,但事实上 mongoose 实际上是在“搞乱”更新,这实际上是您提交给常规 MongoDB 函数的默认操作。

所以 mongoose 认为它是“明智的”作为“假定”您打算在这里发出 $set 指令的便捷方法。由于在这种情况下您实际上不想这样做,因此您可以在传递给任何 .update() 方法的选项中通过 { overwrite: true } 关闭该行为:

作为一个完整的例子:

const mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

const uri = 'mongodb://localhost/test',
      options = { useMongoClient: true };

const testSchema = new Schema({
  name: String,
  phone: String
});

const Test = mongoose.model('Test', testSchema);

function log(data) {
  console.log(JSON.stringify(data,undefined,2))
}

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    // Clean data
    await Promise.all(
      Object.keys(conn.models).map( m => conn.models[m].remove({}) )
    );

    // Create a document
    let test = await Test.create({
      name: 'john doe',
      phone: '+12345678901'
    });
    log(test);

    // This update will apply using $set for the name
    let notover = await Test.findOneAndUpdate(
      { _id: test._id },
      { name: 'Bill S. Preston' },
      { new: true }
    );
    log(notover);

    // This update will just use the supplied object, and overwrite
    let updated = await Test.findOneAndUpdate(
      { _id: test._id },
      { name: 'Dan Smith' },
      { new: true, overwrite: true }
    );
    log(updated);


  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

生产:

Mongoose: tests.remove({}, {})
Mongoose: tests.insert({ name: 'john doe', phone: '+12345678901', _id: ObjectId("596efb0ec941ff0ec319ac1e"), __v: 0 })
{
  "__v": 0,
  "name": "john doe",
  "phone": "+12345678901",
  "_id": "596efb0ec941ff0ec319ac1e"
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { '$set': { name: 'Bill S. Preston' } }, { new: true, upsert: false, remove: false, fields: {} })
{
  "_id": "596efb0ec941ff0ec319ac1e",
  "name": "Bill S. Preston",
  "phone": "+12345678901",
  "__v": 0
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { name: 'Dan Smith' }, { new: true, overwrite: true, upsert: false, remove: false, fields: {} })
{
  "_id": "596efb0ec941ff0ec319ac1e",
  "name": "Dan Smith"
}

显示文档被“覆盖”,因为我们抑制了 $set 操作,否则会被插值。这两个示例首先显示没有 overwrite 选项,它应用了 $set 修饰符,然后显示了“使用”overwrite 选项,其中尊重您为“更新”传入的对象,并且没有这样的 $set修饰符被应用。

注意,这是 MongoDB 节点驱动程序“默认”执行此操作的方式。所以添加“隐式”$set 的行为是由猫鼬完成的,除非你告诉它不要这样做。


注意真正的“替换”方法实际上是使用replaceOne,或者作为replaceOne() 的API 方法,或者通过bulkWrite()overwrite 是 mongoose 希望如何应用 $set 的遗产,如上所述和演示,但是 MongoDB 官方 API 引入 replaceOne 作为update() 操作的“特殊”之王不允许在语句中使用 $set 等原子运算符,如果尝试会出错。

这在语义上更加清晰,因为 replace 非常清楚地了解该方法的实际用途。在对update() 变体的标准API 调用中,当然仍然允许您省略原子操作符,并且无论如何都会替换 内容。但应该会收到警告。

【讨论】:

  • 非常感谢,这个覆盖功能完全没有记录:-(
  • 我收到了这个错误:没有重载匹配这个调用。最后一个重载给出了以下错误。 '{ upsert: true; 类型的参数覆盖:布尔值; }' 不可分配给“QueryFindOneAndUpdateOptions”类型的参数。对象字面量只能指定已知属性,而 'QueryFindOneAndUpdateOptions'.ts(2769) 类型中不存在 'overwrite'
【解决方案2】:

你可以通过upsert选项,它将替换文档:

var collection = db.collection('test');
collection.findOneAndUpdate(
  {'_id': 'some_mongodb_id'},
  {name: 'Dan smith Only'},
  {upsert: true},
  function (err, doc) {
    console.log(doc);
  }
);

但这里的问题是 - 回调中的 doc 已找到文档但未更新。 因此,您需要执行以下操作:

var collection = db.collection('test');
collection.update(
  {'_id': 'some_mongodb_id'},
  {name: 'Dan smith Only'},
  {upsert: true},
  function (err, doc) {
    collection.findOne({'_id': 'some_mongodb_id'}, function (err, doc) {
        console.log(doc);
    });
  }
);

【讨论】:

  • 您可以使用选项 {new:true} 返回更新后的文档。
猜你喜欢
  • 2021-07-31
  • 1970-01-01
  • 1970-01-01
  • 2012-10-04
  • 2014-04-17
  • 1970-01-01
  • 2021-10-04
相关资源
最近更新 更多