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

时间:2015-07-18 00:35:40

标签: node.js mongodb

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

示例代码和文档:

    /*
=============================
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本身的问题(或者如果它是有意的),或者如果它是驱动程序的问题,所以我希望有人至少可以为我澄清这一点。

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

1 个答案:

答案 0 :(得分:1)

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

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: [] } ]

这里的改进基本上是使用Bulk Operations API并立即将所有更新发送到服务器并获得单个响应。另请注意在更新和查询选择中使用运算符。

简单地说,你已经知道&#34;用户&#34;以及&#34;联系&#34;他们正在接受。接受的联系是&#34;待定&#34;并且联系人自己让用户进入&#34;发送&#34;。

这些实际上只是对任一阵列的简单$push$pull操作。查询条件不是在此处使用$addToSet,而是确保在执行更新时存在期望值。这也保留了&#34; order&#34; $addToSet基本上无法保证,因为它是&#34; set&#34;,这是无序的。

一个发送到服务器和一个回调响应,让两个用户都正确更新。更有意义的是发送多个更新并等待每个更新的回调响应。

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

当我说&#34;完成并且自包含&#34;它意味着启动一个新项目并简单地运行代码。这是完整的说明:

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

然后使用该文件夹中的代码列表创建一个文件,例如test.js,然后运行:

node test.js