【问题标题】:How to avoid adding duplicate objects to an array in MongoDB如何避免向 MongoDB 中的数组添加重复对象
【发布时间】:2020-06-17 03:33:16
【问题描述】:

这是我的架构:

 new Schema({
    code: { type: String },
    toy_array: [
      {
        date:{
        type:Date(),
        default: new Date()
       }
        toy:{ type:String }
    ]
   }

这是我的数据库:

{
  "code": "Toystore A",
  "toy_array": [
    {
      _id:"xxxxx", // automatic
      "toy": "buzz"
    },
    {
      _id:"xxxxx", // automatic
      "toy": "pope"
    }
  ]
},
{
  "code": "Toystore B",
  "toy_array": [
    {
       _id:"xxxxx", // automatic
      "toy": "jessie"
    }
  ]
}

我正在尝试更新一个对象。在这种情况下,我想用code: 'ToystoreA' 更新文档,如果数组中不存在玩具,则将一个子文档数组添加到名为toy_array 的数组中。

例如,如果我尝试这样做:

db.mydb.findOneAndUpdate({
  code: 'ToystoreA,
  /*toy_array: {
    $not: {
      $elemMatch: {
        toy: [{"toy":'woddy'},{"toy":"buzz"}],
      },
    },
  },*/
},
{
  $addToSet: {
    toy_array: {
      $each: [{"toy":'woddy'},{"toy":"buzz"}],
    },
  },
},
{
  new: false,
}
})

它们被添加并且是我想要避免的。

我该怎么做?

[
  {
    "code": "Toystore A",
    "toy_array": [
      {
        "toy": "buzz"
      },
      {
        "toy": "pope"
      }
    ]
  },
  {
    "code": "Toystore B",
    "toy_array": [
      {
        "toy": "jessie"
      }
    ]
  }
]

在此示例中,[{"toy":'woddy'},{"toy":"buzz"}] 应仅添加 'woddy',因为 'buzz' 已在数组中。

注意:当我插入一个新的toy 时,除了一个_id 之外,还会插入一个插入日期(这对我来说很正常)。

【问题讨论】:

  • 在这个例子中 [{"toy":'woddy'},{"toy":"buzz"}] 它应该只添加 'pope' 因为 'buzz' 已经在数组。 --> 你确定这个说法吗?你的意思是 woddy 而不是 pope ?如果是,您的代码应该按预期工作..
  • @whoami 你是对的!对不起。
  • @whoami 它添加了这么多的对象元素,而无需进行验证。我会更新代码。每次插入时,都会为每个玩具自动生成一个唯一的 _id,因此我认为每个新元素似乎都是独一无二的。
  • 这能回答你的问题吗? Stop Mongoose from creating _id property for sub-document array items$addFields 失败了,因为每个对象都有唯一的 _id 我猜你可能正在使用猫鼬,如果是的话,那么你可以改变你的架构,就像那个链接中提到的那样!!
  • @whoami 实际上我确实希望生成它,而且在我的真实代码中也会自动生成一个日期。所以我一直在寻找一种方法来插入一个不存在的对象。在这种情况下,如果 toy 是唯一的,则输入一个玩具。

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

当您在对象上使用 $addToSet 时,由于某种原因,您的用例失败了:

假设您的文档如下所示:

    {
      _id: 123, // automatically generated
      "toy": "buzz"
    },
    {
      _id: 456, // automatically generated
      "toy": "pope"
    }

输入是:

[{_id: 789, "toy":'woddy'},{_id: 098, "toy":"buzz"}]

在比较两个对象 {_id: 098, "toy":"buzz"}{_id: 123, "toy":"buzz"} - $addToSet 时,请考虑它们是不同的,您不能在对象中的字段 (toy) 上使用 $addToSet。因此,请尝试以下有关 MongoDB 版本 >= 4.2 的查询。

查询:

db.collection.updateOne({"_id" : "Toystore A"},[{
    $addFields: {
      toy_array: {
        $reduce: {
          input: inputArrayOfObjects,
          initialValue: "$toy_array", // taking existing `toy_array` as initial value
          in: {
            $cond: [
              { $in: [ "$$this.toy", "$toy_array.toy" ] }, // check if each new toy exists in existing arrays of toys
              "$$value", // If yes, just return accumulator array
              { $concatArrays: [ [ "$$this" ], "$$value" ] } // If No, push new toy object into accumulator
            ]
          }
        }
      }
    }
  }])

测试:聚合管道测试网址:mongoplayground

参考: $reduce

注意:

您不需要提及{ new: false },因为.findOneAndUpdate() 默认返回旧文档,如果您需要新文档,则必须使用{ new: true }。此外,如果任何人都可以从数组对象的架构中删除 _id,那么您可以像 OP 之前所做的那样使用 $addToSet(假设 _id 只是唯一字段),请检查此 stop-mongoose-from-creating-id-property-for-sub-document-array-items

【讨论】:

  • 如果我想这样做但第一次添加,它会以同样的方式工作吗?
  • 你确定它适用于updateOne吗?使用此代码,它永远不会更新。
  • @yavg :在这种情况下(第一次),如果您没有 toy_array 本身,您需要在更新操作中添加一个先前的 $addFields 阶段,如下所示 :: (mongoplayground.net/p/k6wWVBWZU4W) , 这段代码永远不会更新 ?有什么错误吗?您是否将 db.collection 替换为您的收藏集名称?
  • 是的。这就是我所做的。我有一个怀疑。在我的实际示例中,每个数组位置都包含一个 country_id 属性,其值为属于另一个集合中的 id 的 ObjectId。比较是在 StringObjectId 之间。例如arrayfilter.id_country == db.country_id 完成。我不确定是否没有比较,因为db.country_id 是一个`ObjectId`
  • 在这个例子中,如果它只适用于.update,在我的真实代码中我不知道为什么它不起作用..我认为ObjectsId之间肯定存在一些比较问题。再次感谢
猜你喜欢
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
相关资源
最近更新 更多