3

Lets imagine a JSON like:

"user": {
  "id": "1234",
  ...some fields,
  "achievements": [
    {
      "scope": "life achievements",
      "list": [
        {"_id": 1, "title": "some text", "gotAt": "some date"},
        {"_id": 2, "title": "some other text", "gotAt": "some date"}
      ]
    },
    {
      "scope": "sport achievements",
      "list": [
        {"_id": 1, "title": "sport", "gotAt": "some date"},
        {"_id": 2, "title": "some other sport", "gotAt": "some date"}
      ]
    },
    {
      "scope": "academic achievements",
      "list": [
        {"_id": 1, "title": "some text", "gotAt": "some date"},
        {"_id": 2, "title": "some other text", "gotAt": "some date"}
      ]
    },
  ]
}

I need to UPDATE for example the first life achievement (with id 1) using mongoDb and Golang driver Mongo-go for it

The problem is in the nesting level. I can get some array element by $ mongoDb operator, but here i need to identify every user achievement by the activity scope (life, sport, academic etc) and _id field of achievement. So I need to make a search inside an array of objects inside other array of objects.

Thats what ive done (it doesnt work anyway)

doc := coll.FindOneAndUpdate(
        context.Background(),
        bson.D{
            {"_id", "1234"},
            {"achievements.scope", "life achievements"},
            {"achievements.list._id", 1},
        },
        bson.D{
            {"$set", bson.D{
                {"achievements.$.list.$[elem].title", "some new test"},
            }},
        },
        options.FindOneAndUpdate().SetArrayFilters(options.ArrayFilters{
            Filters: append(make([]interface{}, 0), bson.D{
                {"elem", bson.D{
                    {"achievements.list._id", 1},
                }},
            }),
        }).SetReturnDocument(1),
    )

I expect this query to update only one element specified by "id" and "scope" fields. May be I use arrayFilters incorretly, or may be I set param of FindOneAndUpdate function incorrectly. Now it doesnt throw any error nor updates the document. Please help me

Sergey Shopin
  • 63
  • 2
  • 8

2 Answers2

11

With the package go.mongodb.org/mongo-driver/mongo you can do it like this:

coll.FindOneAndUpdate(
    context.Background(),
    bson.D{
        {"id", "1234"},
        {"achievements.scope", "life achievements"},
        {"achievements.list._id", 1},
    },
    bson.M{"$set": bson.M{"achievements.$.list.$[elem].title": "some new test"}},
    options.FindOneAndUpdate().SetArrayFilters(options.ArrayFilters{
        Filters: []interface{}{bson.M{"elem._id": 1}},
    }),
)

which "translates" to shell:

db.user.findOneAndUpdate(
  {"id": "1234","achievements.scope": "life achievements","achievements.list._id": 1},
  {$set: {"achievements.$.list.$[elem].title": "some new test"}},
  {arrayFilters: [{"elem._id": 1}]}
)
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
owlwalks
  • 1,541
  • 13
  • 16
  • Thank you so match! Ive combined what I have done with what you have written (specifically my array filters were incorrect). So it helped! – Sergey Shopin May 26 '19 at 12:00
2

So, with help of @owlwalks, we have found the decision:

doc := coll.FindOneAndUpdate(
        context.Background(),
        bson.D{
            {"_id", "1234"},
            {"achievements.scope", "life achievements"},
        },
        bson.D{
            {"$set", bson.D{
                {"achievements.$.list.$[elem].title", "some new text"},
            }},
        },
        options.FindOneAndUpdate().SetArrayFilters(options.ArrayFilters{
            Filters: []interface{}{bson.D{
                    {"elem._id", 1},
                }},
        }).SetReturnDocument(1),
    )
Sergey Shopin
  • 63
  • 2
  • 8