【问题标题】:Upsert a document with value from filter使用过滤器中的值更新文档
【发布时间】:2020-06-03 11:17:20
【问题描述】:

我在 MongoDB 中有一个具有以下结构的集合:

{
  "userId": String,
  "refs": Set<String>
}

我需要使用这些文档更新集合。我想为过滤器$in 中的用户添加一个新字符串到refs。 但是,如果用户不存在,我需要“更新”他。

在代码(golang)中是这样的:

filter := bson.M{
    "userId": bson.M{
        "$in:": tokens // tokens is []string
    }
}

update := bson.M{
    "$addToSet": bson.M{
        "refs": newReference
    }
}

ctx, _ := newDbOperationContext()
_, err := driver.UpdateMany(ctx, filter, update)

因此,对于现有用户来说,它工作正常,添加了参考。但是,对于不存在的用户,什么都不会发生。

我在driver.UpdateMany(bson, bson, opts...) 中设置了options.UpdateOptions.SetUpsert(true)",但结果我得到了一个没有userId 的文档:

{
  "_id": ObjectId("..."),
  "refs": ["new_reference"]
}

所以,我的问题是,如何使用 userId 字段插入新值。

规模就像 2*10^6 用户更新,所以我想使用批处理请求来做到这一点。我认为,使用“一个一个”创建并更新他不是一个选项。

感谢您的支持!

【问题讨论】:

    标签: database mongodb go


    【解决方案1】:

    如果文档不存在,则只有更新器部分会插入到数据库中。所以这就是为什么你的输出是这样的。可以看here.

    【讨论】:

      【解决方案2】:

      看看您的用例,我认为最好的解决方案如下:

      由于您的规模很大并且希望进行批量请求,因此最好使用 BulkWrite :db.collection.bulkWrite() 方法提供了执行批量插入、更新和删除操作的能力。 示例:https://godoc.org/go.mongodb.org/mongo-driver/mongo#example-Collection-BulkWrite

      这使用 UpdateOne 模型,但它也支持 UpdateMany 模型。它也是 SetUpsert(true) 的一个函数 现在对于 _id 字段:您更新/更新的文档应该具有 _id 字段,以便新文档具有该 _id 字段,否则如果您的 upsert 文档没有 _id 字段,则 mongoDb 会在插入文档时自动生成一个 _id 字段

      我认为,在您的文档中包含 _id 字段不会很痛苦,这样您的问题就解决了。

      关于规模,我建议将 BulkWrite 与 UpdateOne 或 UpdateMany 模型一起使用。

      希望这会有所帮助。

      【讨论】:

      • 我的问题是那些文件必须有userId字段。它可能保存在_id字段中,但它必须是其他地方生成的userId。
      • 更新文档中有 userId 吗?如果您将 bulkWrite 与 UpdateOne 一起使用,这仍然是一个批处理请求。而如果每分钟有 2*10^6 个请求,我认为这不会造成太大影响。
      【解决方案3】:

      根据之前的问题,如this one 和this other one,似乎不可能仅使用$in 运算符执行多个upserts,因为它只会插入a single document(匹配过滤器的那个) :

      如果没有文档符合查询条件,则 db.collection.update() 插入单个文档。

      正如@Kartavya 所说,最好的办法是使用BulkWrite 执行多个写入操作。

      为此,您需要为 tokens 中的每个用户附加一个 upsert 操作 (=WriteModel) 作为过滤器,并且您可以使用相同的 $addToSet 更新操作:

      tokens := [...]string{"userId1", "userId3"}
      newRef := "refXXXXXXX"
      
      // all docs can use the same $addToSet update operation
      updateOp := bson.D{{"$addToSet", bson.D{{"refs", newRef}}}}
      
      // we'll append one update for each userId in tokens as a filter
      upserts := []mongo.WriteModel{}
      for _, t := range tokens {
          upserts = append(
              upserts,
              mongo.NewUpdateOneModel().SetFilter(bson.D{{"userId", t}}).SetUpdate(updateOp).SetUpsert(true))
      }
      
      opts := options.BulkWrite().SetOrdered(false)
      res, err := col.BulkWrite(context.TODO(), upserts, opts)
      if err != nil {
          log.Fatal(err)
      }
      
      fmt.Println(res)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        相关资源
        最近更新 更多