Home > Blockchain >  MongoDB: Upsert with array filter
MongoDB: Upsert with array filter

Time:04-04

I have collection like this:

mc.db.collection.insert_many([
    {"key_array": [1], "another_array": ["a"]},
    {"key_array": [2, 3], "another_array": ["b"]},
    {"key_array": [4], "another_array": ["c", "d"]},    
])

And I'm using this kind of updates:

mc.db.collection.update_one(
    {"key_array": 5},
    {"$addToSet": {"another_array": "f"}},
    upsert=True
)

It works good with updates, but I have trouble when trying to upsert: It creates a document with a non-array key_array field, like this

{
    "_id": ObjectId(...)
    "key_array": 5,
    "another_array": ["f"]
}

while I want to have this one

{
    "_id": ObjectId(...)
    "key_array": [5],
    "another_array": ["f"]
}

Also, I cannot use the {"key_array": [5]} style query, because it won't match the existing array with length > 1.

So, is there any chance to save such behavior on updates, and receive the correct document structure on inserts?

Any help will be appreciated

CodePudding user response:

This should help. https://www.mongodb.com/docs/manual/reference/operator/update/setOnInsert/

mc.db.collection.update_one(
    {"key_array": 5},
    {
        "$addToSet": {"another_array": "f"},
        "$setOnInsert": {"key_array": [5], ...}
    },
    upsert=True
)

CodePudding user response:

how about this one.

db.collection.update({
  "key_array": 5
},
{
  "$addToSet": {
    "another_array": "f",
    
  },
  "$set": {
    "key_array": [
      5
    ]
  }
},
{
  "upsert": true
})

https://mongoplayground.net/p/4YdhKuzr2I6

CodePudding user response:

Ok, finally I had solved this issue with two consecutive updates, the first as specified in the question - upserts with non-array query field, and the second which converts the field to an array if it belongs to another type.

from pymongo import UpdateOne

bulk = [
    UpdateOne(
        {"key_array": 6},
        {"$addToSet": {"another_array": "e"}},
        upsert=True
    ),
    UpdateOne(
        {
            "$and": [{"key_array": 6},
                     {"key_array": {"$not": {"$type": "array"}}}]
        },
        [{"$set": { "key_array": ["$key_array"]}}]
    )
]

mc.db.collection.bulk_write(bulk)

But I'm still looking for a more elegant way to do such a thing.

  • Related