Node.js 为什么MongoDB会忽略我的一些更新?
我一直在使用原生MongoDB驱动程序在Node.JS中构建一个应用程序——它包括联系人,当用户接受联系人时,它应该从“待定”和“已发送”联系人中删除,然后添加到“联系人” 示例代码和文档:Node.js 为什么MongoDB会忽略我的一些更新?,node.js,mongodb,Node.js,Mongodb,我一直在使用原生MongoDB驱动程序在Node.JS中构建一个应用程序——它包括联系人,当用户接受联系人时,它应该从“待定”和“已发送”联系人中删除,然后添加到“联系人” 示例代码和文档: /* ============================= User "john" ============================= { username: "john", contacts: ["jim"], pending_contacts: ["bob"] } =
/*
=============================
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');
}
);
/*
=============================
用户“john”
=============================
{
用户名:“john”,
联系人:[“jim”],
待定联系人:[“bob”]
}
=============================
用户“bob”
=============================
{
用户名:“鲍勃”,
联系人:[“dave”],
已发送联系人:[“john”]
}
=============================
该怎么办
=============================
{
用户名:“鲍勃”,
联系人:[“戴夫”、“约翰”],
已发送联系人:[]
},
{
用户名:“john”,
联系人:[“吉姆”、“鲍勃”],
待定联系人:[]
}
=============================
到底发生了什么
=============================
{
用户名:“john”,
联系人:[“吉姆”、“鲍勃”],
待定联系人:[“bob”]
},
{
用户名:“鲍勃”,
联系人:[“戴夫”、“约翰”],
已发送联系人:[“john”]
}
*/
var col=this.db.collection('users');
var contact=“bob”,username=“john”;
var who=[联系人,用户名];
var finishCount=0;
//在回调之前,finish将运行3次
函数完成(名称){
log(名称“has finished”);
finishCount++;
如果(finishCount使用节点本机驱动程序,则每次对我都有效:
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: [] } ]
这里的改进基本上是使用,将所有更新一次发送到服务器,并获得一个响应。还要注意更新和查询选择中运算符的使用
简单地说,您已经知道“用户”以及他们正在接受的“联系人”。要接受的联系人是“待定”的,联系人本身将用户置于“已发送”状态
这些操作非常简单,在任意一个数组上的操作都是适当的。查询条件不在此处使用,而是确保在执行更新时出现预期值。这还保留了$addToSet
基本上无法保证的“顺序”,因为它是一个“集”,没有顺序
一次发送到服务器,一次回调响应,让两个用户都得到正确更新。这比发送多个更新并等待每个用户的回调响应更有意义
无论如何,这是一个完整的自包含列表,只有两个命名的依赖项,因此您可以轻松地自己运行它并确认结果
当我说“完整且自包含”时,它意味着启动一个新项目并简单地运行代码。以下是完整的说明:
mkdir sample
cd sample
npm init
npm install mongodb --save
npm install async --save
然后在该文件夹中创建一个包含代码列表的文件,比如test.js
,然后运行:
node test.js
请在问题中包含您的相关代码。@DaveNewton这是要点,但我已将其放入实际问题中now@JohnnyHK如果我将它们一个接一个地放在前一个回调中运行,问题仍然会发生all@Someguy123您的代码生成了“应该发生什么”结果当我尝试它时。那是使用MongoDB驱动程序的v2.0.34。@JohnnyHK I刚从2.0.33更新到2.0.34,似乎没有什么不同。注意到一些奇怪的行为,它似乎从第一个用户的挂起的_联系人中删除,但如果它在sent_联系人中,它将不会删除(即使查询以两个键为目标)。注意:我在服务器上使用MongoLab感谢您的出色响应,但奇怪的是,尽管使用了您的确切代码,但sent_contacts和pending_contacts并没有被更改。这是执行的结果,这很奇怪,因为它看起来工作正常(但这两个contacts都未更改sent/pending):{ok:1,writeErrors:[]、writeConcernErrors:[]、insertedIds:[]、未插入的:0、未插入的:0、未匹配的:2、未修改的:2、未删除的:0、已插入的:[]、lastOp:{{U bsontype:'Timestamp',低:2、高:1437701814}
@Someguy123您可能没有运行此处的独立列表,或者您的安装有问题。尽管如此,您在此处引用的“WriterResponse”与您的“not change”声明不一致。nModified
中清楚地显示了2,这仅在实际发生更改时才会发生。请确保运行“whole”独立地在与现有集合不同的集合中列出。这就是数据“自包含”的原因。请参阅附加说明。我已经对它进行了100%独立测试,它似乎可以工作。我已经给了你奖金,因为我认为没有其他人会对此作出响应。我真的很困惑,为什么它在我的Express应用程序中不工作,因为我使用的是完全相同的Async、MongoClient等版本。我想我必须做很多调查由于我的应用程序对该代码的实现与独立代码之间的差异很小,除了它位于路由后面之外……如果您知道什么可能会干扰这一点,请告诉我,谢谢。@Someguy123有很多差异。1.操作是“批量”操作因此,此过程只需要向服务器发出一个请求。2.移动到大容量可避免异步回调混淆,而异步回调混淆可能是导致问题的原因之一。3.操作本身的形式比以前更简洁,只需要“两个”操作。4.您看到了“两个”项目已在您所说的未更新的位置进行了更新。您可能正在查看错误的项目,并且“用户名”有“多个”记录。请始终使用“唯一”标识符。没有重复的用户。此外,我指的是我从您的帖子中复制并在我的应用程序中实现的代码。奇怪的是,它做了一些毫无意义的事情:它将联系人添加到每侧的联系人,但是联系人将从挂起的联系人切换到已发送的联系人,反之亦然A.