我正在使用MongoDB为我的应用程序创建类似功能。我最近偶然发现一个问题。

我喜欢的代码:

async function like(articleId, userId) {
const db = await makeDb();
return new Promise((resolve, reject) => {
  const result = db.collection(collectionName).findOneAndUpdate(
    { articleId: ObjectID(articleId) },
    {
      $setOnInsert: {
        articleId: ObjectID(articleId),
        creationDate: new Date()
      },
      $addToSet: { likes: ObjectID(userId) }
    },
    { upsert: true }
  );
  resolve(result);
});


它能做什么:


如果集合中没有文档,则创建一个并填写所有字段
如果存在文档,则仅使用新的用户ID更新likes数组


这可以按预期工作,并且没有问题,这要感谢$addToSe t我没有任何重复等。

但是,在执行了上面的代码并返回了result之后,我需要做一些其他的事情,并且要使其正常工作,我需要知道Mongos $addToSet是否确实进行了任何更改。

简而言之:


如果新的userId已添加到likes数组中,我想做点什么
如果userId已经在likes数组中,并且$addToSet没有更改任何内容,我不想采取任何措施。


有没有办法区分$addToSet是否做了什么?

结果的当前console.log()如下所示:

  {
    lastErrorObject: { n: 1, updatedExisting: true },
    value: { _id: 5e2c47c57bb5183d80dce14f,
             articleId: 5da4d1365217baf52fbcd76a,
             creationDate: 2020-01-25T13:51:01.928Z,
             likes: [ 5d750b677d8edfc08af8a527 ]
    },
    ok: 1
  }


我想到的一件事(但是在性能方面非常差)是如果更新likes数组包含带有javascript includes方法的userID,则在更新之前将其进行比较。

还有更好的主意吗?

最佳答案

我找到了解决方案!

99%的功劳归给Neil Lunn这个答案:
https://stackoverflow.com/a/44994382/2175228

它使我走上了正确的道路。我放弃了findOneAndUpdate方法,转而使用updateOne方法。查询参数等都保持不变,只是方法名称发生了变化,显然该方法的结果类型也是如此。

updateOne方法的问题在于它不返回已更新的对象(或旧的对象)。但是它确实返回upsertedId,这是我所需要的唯一内容。

因此,最终您得到的是一个很长的CommandResult对象,该对象以:

 CommandResult {
  result: { n: 1, nModified: 0, ok: 1 },
  (...)
 }


但是当我仔细观察时,我发现该对象正是我一直需要的那些字段:

  modifiedCount: 0,
  upsertedId: null, // if object was inserted, this field will contain it's ID
  upsertedCount: 0,
  matchedCount: 1


因此,您需要做的就是从CommandResult对象中获取所需的部分,然后继续;)

我的代码更改后:

async function like(articleId, userId) {
    const db = await makeDb();
    return new Promise((resolve, reject) => {
      db.collection(collectionName)
        .updateOne(
          { articleId: ObjectID(articleId) },
          {
            $setOnInsert: {
              articleId: ObjectID(articleId),
              creationDate: new Date()
            },
            $addToSet: { likes: ObjectID(userId) }
          },
          { upsert: true }
        )
        .then(data => {
          // wrap the content that You need
          const result = {
            upsertedId: data.upsertedId,
            upsertedCount: data.upsertedCount,
            modifiedCount: data.modifiedCount
          };

          resolve(result);
        });
    });
  }


希望这个答案将来可以帮助有类似问题的人:)

10-07 21:36