为什么MongoDB会忽略我的一些更新?

我一直在使用本地MongoDB驱动程序在Node.JS中构build应用程序 – 它包含联系人,当用户接受联系人时,应该从“待处理”和“已发送”联系人中删除,然后添加到“联系人”中。

示例代码和文档:

/* ============================= User "john" ============================= { username: "john", contacts: ["jim"], pending_contacts: ["bob"] } ============================= User "bob" ============================= { username: "bob", contacts: ["dave"], sent_contacts: ["john"] } ============================= What SHOULD happen ============================= { username: "bob", contacts: ["dave", "john"], sent_contacts: [] }, { username: "john", contacts: ["jim", "bob"], pending_contacts: [] } ============================= What ACTUALLY happens ============================= { username: "john", contacts: ["jim", "bob"], pending_contacts: ["bob"] }, { username: "bob", contacts: ["dave", "john"], sent_contacts: ["john"] } */ var col = this.db.collection('users'); var contact = "bob", username = "john"; var who = [contact, username]; var finishCount = 0; // finish will run 3 times before callback function finish(name) { console.log(name, ' has finished'); finishCount++; if(finishCount<3) return; callback(false, null); } // run if there's an error function failed(err) { callback(err, null) } console.log('removing %s and %s from pending and sent', username, contact) col.update( {username: { $in: who }}, { $pullAll: { sent_contacts: who, pending_contacts: who } }, {multi: 1}, function(err,data) { if(err) return failed(err); finish('REMOVE_CONTACTS'); } ); col.update( {username: username}, {$addToSet: {contacts: contact}}, function(err,res) { if(err) return failed(err); console.log('added 1'); finish('ADD_TO_USER'); } ); col.update( {username: contact}, {$addToSet: {contacts: username}}, function(err,res) { if(err) return failed(err); console.log('added 2'); finish('ADD_TO_CONTACT'); } ); 

第一次更新将联系人和所有者从其他待处理/已发送列表中删除,第二次和第三次更新将所有者添加到联系人的联系人列表,反之亦然。

问题是,最终的结果似乎是从未发生删除,虽然删除查询本身工作得很好。 我不知道这是否是MongoDB本身的一个问题(或者如果有意的话),还是与驱动程序有关,所以我希望至less有人能为我澄清一下。

注:是的,我知道他们asynchronous运行。 通过在之前的callback中放置每个更新来一个接一个地运行它们并没有什么不同。 在任何人抱怨这个代码看起来有多糟糕之前,我先前在Async.JS中设置了它,但是我从这个代码示例中删除了它,以确保Asyn.cJS不对这些问题负责。

使用节点本机驱动程序,每次都适用于我:

 var mongodb = require('mongodb'), async = require('async'), MongoClient = mongodb.MongoClient; var user = "john", contact = "bob"; var contactsList = [ { "username": "john", "contacts": [ "jim" ], "pending_contacts": [ "bob" ] }, { "username": "bob", "contacts": [ "dave" ], "sent_contacts": [ "john" ] } ]; MongoClient.connect('mongodb://localhost/test',function(err,db) { var coll = db.collection("contacts"); async.series( [ // Wipe clean function(callback) { coll.remove({},callback) }, // Init collection function(callback) { async.each(contactsList,function(contact,callback) { coll.insert(contact,callback); },callback); }, // Do updates function(callback) { // Init batch var bulk = coll.initializeOrderedBulkOp(); // Add to user and pull from pending bulk.find({ "username": user, "contacts": { "$ne": contact }, }).updateOne({ "$push": { "contacts": contact }, "$pull": { "pending_contacts": contact } }); // Add to contact and pull from sent bulk.find({ "username": contact, "contacts": { "$ne": user }, "sent_contacts": user }).updateOne({ "$push": { "contacts": user }, "$pull": { "sent_contacts": user } }); // Execute bulk.execute(function(err,response) { console.log( response.toJSON() ); callback(err); }); }, // List collection function(callback) { coll.find({}).toArray(function(err,results) { console.log(results); callback(err); }); } ], function(err) { if (err) throw err; db.close(); } ); }); 

而输出:

 { ok: 1, writeErrors: [], writeConcernErrors: [], insertedIds: [], nInserted: 0, nUpserted: 0, nMatched: 2, nModified: 2, nRemoved: 0, upserted: [] } [ { _id: 55b0c16934fadce812cdcf9d, username: 'john', contacts: [ 'jim', 'bob' ], pending_contacts: [] }, { _id: 55b0c16934fadce812cdcf9e, username: 'bob', contacts: [ 'dave', 'john' ], sent_contacts: [] } ] 

这里的改进基本上是使用批量操作API ,并将所有更新一次发送到服务器,并得到一个响应。 还要注意在更新和查询select中使用操作符。

简而言之,您已经知道“用户”以及他们正在接受的“联系”。 被接受的联系人是“正在等待”,联系人自己使用户处于“已发送”状态。

这些实际上只是简单的$push$pull操作在适当的数组。 查询条件确保在执行更新时存在期望值,而不是在这里使用$addToSet 。 这也保留了$addToSet基本上不能保证的“order”,因为它是一个“set”,它是无序的。

一个发送到服务器和一个callback响应,使两个用户正确更新。 更有意义,然后发送多个更新,并等待每个callback响应。

无论如何,这是一个完整的自包含列表,只有两个命名的依赖关系,因此您可以轻松地自行运行并确认结果。


当我说“完整和独立”时,意味着开始一个新的项目并简单地运行代码。 以下是完整的说明:

 mkdir sample cd sample npm init npm install mongodb --save npm install async --save 

然后用该文件夹中的代码清单创build一个文件,例如test.js ,然后运行:

 node test.js