使用不同的值更新 mongoDb 集合的每个文档的相同属性

Posted

技术标签:

【中文标题】使用不同的值更新 mongoDb 集合的每个文档的相同属性【英文标题】:Update the same property of every document of a mongoDb collection with different values 【发布时间】:2018-11-19 04:07:00 【问题描述】:

我在 mongoDb 中有一个集合,看起来像这样

 
"slno" : NumberInt(1),
"name" : "Item 1"
 
 
"slno" : NumberInt(2),
"name" : "Item 2"
 
 
"slno" : NumberInt(3),
"name" : "Item 3"
 

我收到来自 angularJs 前端的请求,要求将此集合更新为

 
"slno" : NumberInt(1),
"name" : "Item 3"
 
 
"slno" : NumberInt(2),
"name" : "Item 1"
 
 
"slno" : NumberInt(3),
"name" : "Item 2"
 

我正在使用 Mongoose 5.0 ORM 和 Node 6.11 并表达 4.15。请帮助我找到实现这一目标的最佳方法。

【问题讨论】:

【参考方案1】:

您基本上想要bulkWrite(),它可以获取对象的输入数组并使用它来发出“批量”请求以更新匹配的文档。

假设文档数组是在req.body.updates 中发送的,那么你会得到类似的东西

const Model = require('../models/model');

router.post('/update', (req,res) => 
  Model.bulkWrite(
    req.body.updates.map(( slno, name ) => 
      (
        updateOne: 
          filter:  slno ,
          update:  $set:  name  
        
      )
    )
  )
  .then(result => 
    // maybe do something with the WriteResult
    res.send("ok"); // or whatever response
  )
  .catch(e => 
    // do something with any error
  )
)

这会根据输入发送一个请求:

bulkWrite([
    updateOne:  filter:  slno: 1 , update:  '$set':  name: 'Item 3'    ,
    updateOne:  filter:  slno: 2 , update:  '$set':  name: 'Item 1'    ,
    updateOne:  filter:  slno: 3 , update:  '$set':  name: 'Item 2'     ]
)

在对服务器的单个请求中通过单个响应有效地执行所有更新。

另请参阅bulkWrite() 上的核心 MongoDB 文档。这是mongo shell 方法的文档,但所有选项和语法在大多数驱动程序中都完全相同,尤其是在所有基于 javascript 的驱动程序中。

作为与猫鼬一起使用的方法的完整工作演示:

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

const uri = 'mongodb://localhost/test';

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

const testSchema = new Schema(
  slno: Number,
  name: String
);

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

const log = data => console.log(JSON.stringify(data, undefined, 2));

const data = [1,2,3].map(n => ( slno: n, name: `Item $n` ));

const request = [[1,3],[2,1],[3,2]]
  .map(([slno, n]) => ( slno, name: `Item $n` ));

mongoose.connect(uri)
  .then(conn =>
    Promise.all(Object.keys(conn.models).map( k => conn.models[k].remove()))
  )
  .then(() => Test.insertMany(data))
  .then(() => Test.bulkWrite(
    request.map(( slno, name ) =>
      ( updateOne:  filter:  slno , update:  $set:  name    )
    )
  ))
  .then(result => log(result))
  .then(() => Test.find())
  .then(data => log(data))
  .catch(e => console.error(e))
  .then(() => mongoose.disconnect());

或者对于更现代的环境使用async/await

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

const uri = 'mongodb://localhost/test';

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

const testSchema = new Schema(
  slno: Number,
  name: String
);

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

const log = data => console.log(JSON.stringify(data, undefined, 2));

const data = [1,2,3].map(n => ( slno: n, name: `Item $n` ));

const request = [[1,3],[2,1],[3,2]]
  .map(([slno,n]) => ( slno, name: `Item $n` ));

(async function() 

  try 

    const conn = await mongoose.connect(uri)

    await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));

    await Test.insertMany(data);
    let result = await Test.bulkWrite(
      request.map(( slno, name ) =>
        ( updateOne:  filter:  slno , update:  $set:  name    )
      )
    );
    log(result);

    let current = await Test.find();
    log(current);

    mongoose.disconnect();

   catch(e) 
    console.error(e)
   finally 
    process.exit()
  

)()

加载初始数据然后更新,显示响应对象(序列化)和处理更新后集合中的结果项:

Mongoose: tests.remove(, )
Mongoose: tests.insertMany([  _id: 5b1b89348f3c9e1cdb500699, slno: 1, name: 'Item 1', __v: 0 ,  _id: 5b1b89348f3c9e1cdb50069a, slno: 2, name: 'Item 2', __v: 0 ,  _id: 5b1b89348f3c9e1cdb50069b, slno: 3, name: 'Item 3', __v: 0  ], )
Mongoose: tests.bulkWrite([  updateOne:  filter:  slno: 1 , update:  '$set':  name: 'Item 3'    ,  updateOne:  filter:  slno: 2 , update:  '$set':  name: 'Item 1'    ,  updateOne:  filter:  slno: 3 , update:  '$set':  name: 'Item 2'     ], )

  "ok": 1,
  "writeErrors": [],
  "writeConcernErrors": [],
  "insertedIds": [],
  "nInserted": 0,
  "nUpserted": 0,
  "nMatched": 3,
  "nModified": 3,
  "nRemoved": 0,
  "upserted": [],
  "lastOp": 
    "ts": "6564991738253934601",
    "t": 20
  

Mongoose: tests.find(,  fields:  )
[
  
    "_id": "5b1b89348f3c9e1cdb500699",
    "slno": 1,
    "name": "Item 3",
    "__v": 0
  ,
  
    "_id": "5b1b89348f3c9e1cdb50069a",
    "slno": 2,
    "name": "Item 1",
    "__v": 0
  ,
  
    "_id": "5b1b89348f3c9e1cdb50069b",
    "slno": 3,
    "name": "Item 2",
    "__v": 0
  
]

这是使用与 NodeJS v6.x 兼容的语法

【讨论】:

感谢@Neil Lunn 的帮助。【参考方案2】:

Neil Lunn 的回答稍作改动就完成了任务。

const Model = require('../models/model');

router.post('/update', (req,res) => 

var tempArray=[];

req.body.updates.map((slno,name) => 
    tempArray.push(
      updateOne: 
        filter: slno,
        update: $set: name
      
    );
 );

Model.bulkWrite(tempArray).then((result) => 
  //Send resposne
).catch((err) => 
   // Handle error
);

感谢尼尔·伦恩。

【讨论】:

只是小费。我认为至少有一个人不喜欢这个答案,因为Array.map() 已经返回了一个数组,所以不需要Array.push() 到另一个数组。我还为您提供了工作代码示例并显示了运行该代码的输出,因此应该很清楚没有必要“更改”任何东西以使其工作。如果您花时间了解这些方法,我可能会帮助您编写更好的代码。 我明白了。谢谢

以上是关于使用不同的值更新 mongoDb 集合的每个文档的相同属性的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB - 使用 javascript 更新子文档

MongoDB - 使用 javascript 更新子文档

MongoDb:如何根据每个字段的值更新多个文档中的字段值?

MongoDb:如何根据每个字段的值更新多个文档中的字段值?

无法更新 mongoDB 文档

向 MongoDB 集合中的每个文档添加新字段