【问题标题】:MongoDB - check if $addToSet was a duplicate or notMongoDB - 检查 $addToSet 是否重复
【发布时间】:2020-01-25 14:19:04
【问题描述】:

我正在使用 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);
});

它的作用:

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

这按预期工作并且没有问题,感谢$addToSet 我没有任何重复等。

但是,在执行上述代码并返回 result 之后,我需要做一些额外的事情,为了让它工作,我需要知道 Mongos $addToSet 是否确实改变了任何东西。

简而言之:

  1. 如果新的 userId 已添加到 likes 数组中,我想做点什么
  2. 如果 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 数组是否包含用户 ID 和 javascript includes 方法。

有更好的想法吗?

【问题讨论】:

  • 所以你想确保addToSet 没有删除/更改数组中的任何其他元素,除了添加新的唯一用户ID 或者如果它是重复的则不添加它?所以它不会影响数组中现有的重复元素/元素..
  • @srinivasy 我认为以上都不是:P 我只想知道addToSet 是否做了任何事情。 addToSet 工作正常,问题是有两个选项: 1. 我想添加用户 X,用户 X 不在 likes 数组中,addToSet 通过添加用户 X 更新对象,返回更新后的对象。 2.我要添加用户X,用户X在likes数组中,addToSet没有效果,返回更新的(本例与旧的相同)对象。我想区分这两种情况,并对它们做出不同的反应。所以如果(1.)发生了我想做点什么,但是如果(2.)发生了我什么都不想做。
  • 所以从查询的输出中你会得到`n: 1, updatedExisting: true`通过它你可以知道它是否更新了,所以addToSet推了一个新元素!!跨度>
  • 是的......我虽然完全一样,但遗憾的是由于一些奇怪的原因并非如此,即使addToSet什么也没做,它总是返回{ n:1 and updatedExisting: true } :(
  • 也许它反映到 nMatched 而不是 nUpdated

标签: javascript node.js mongodb mongodb-query


【解决方案1】:

我找到了解决办法!

这个答案的 99% 归功于 Neil Lunnhttps://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);
        });
    });
  }

希望这个答案能帮助将来遇到类似问题的人:)

【讨论】:

    猜你喜欢
    • 2017-05-30
    • 2013-10-23
    • 2016-02-14
    • 2015-02-10
    • 2017-06-14
    • 1970-01-01
    • 2020-08-16
    • 2017-03-16
    • 2014-03-24
    相关资源
    最近更新 更多