2

I'm pretty new in mongodb so here's my question, what I want is check if the main document exists and then if it does, check if the nested document exist then update it and if it doesn't insert a new one. But with this code I can only update an existent nested document but I cannot create a new one. I don't even know if it is a proper use of this method, is it efficient?

collection.find_one_and_update(
 {"_id:{"field1":val1,"field2":val2,"field3":val3,"field4":val4}},
 {"$set":{"elements.$[element].prop2":valToUpdate}},
 return_document=ReturnDocument.AFTER, 
 array_filters=[{"element.prop1":valToCheck}],
 upsert=True
)

In mongo syntax I think is something like this:

db.collection.findOneAndUpdate(
   {"_id:{"field1":val1,"field2":val2,"field3":val3,"field4":val4}},
   {"$set":{"elements.$[element].prop2":valToUpdate}},
   { arrayFilters: [ {"element.prop1":valToCheck} ] }
)

Can I use a single function like this one for do the whole process (check if the main and the nested document exist and if it does update, otherwise create a new one)?

A sample of how I want to alter the document:

before execute the method

{
   "_id":{"name":"Storename","location":"somewhere","phone":46284723},
   "books":
      [
         {"name":"somebook","price":{"$numberDouble":"34"}}
      ]
}

After the execution if there's the nested document

{
   "_id":{"name":"Storename","location":"somewhere","phone":46284723},
   "books":
      [
         {"name":"somebook","price":{"$numberDouble":"55"}},
      ]
}

After the execution if there's not the nested document

{
   "_id":{"name":"Storename","location":"somewhere","phone":46284723},
   "books":
      [
         {"name":"somebook","price":{"$numberDouble":"34"}},

         {"name":"someOTHERbook","price":{"$numberDouble":"45"}}
      ]
}
0

1 Answer 1

2

That's because upsert seems to not work along with $ operator, Also in general upsert is on document level but should not be used to insert missing elements of an array like this scenario. As the actual document exists in DB it will through an error of duplicate _id.

If you wanted to do this in one DB call, then the below query should work for your scenario, as at any given case only one updateOne should update the array :

Mongo Query :

db.yourCollectionName.bulkWrite([
    {
        updateOne: {
            filter: {
                "_id": {
                    "name": "Storename",
                    "location": "somewhere",
                    "phone": 46284723
                }, 'books.name': 'somebook'
            }, update: {
                $set: { "books.$.price": 100 }
            }
        }
    },
    {
        updateOne: {
            filter: {
                "_id": {
                    "name": "Storename",
                    "location": "somewhere",
                    "phone": 46284723,
                }
            }, update: {
                $addToSet: { 'books': { 'name': 'somebook', "price": 100 } }
            }
       }
 }])

Python Code :

import pymongo
from pymongo import UpdateOne

bulkArr = [
    UpdateOne({
        "_id": {
            "name": "Storename",
            "location": "somewhere",
            "phone": 46284723
        }, 'books.name': 'somebook'
    }, { '$set': { "books.$.price": 100 } }),
    UpdateOne({
        "_id": {
            "name": "Storename",
            "location": "somewhere",
            "phone": 46284723,
        }
    }, {
        $addToSet: { 'books': { 'name': 'somebook', "price": 100 } }
       })
   ]

db.yourCollectionName.bulk_write(bulkArr)

PyMongo Ref : Please do refer this link to convert this query. PyMongo-bulk_write

Sign up to request clarification or add additional context in comments.

12 Comments

Is this bulk_write method efficient enough?
@esteban21 : should be
does it allows the insertion of the main documents if doesn't exist? I mean, I put the upsert parameter in the second "UpdateOne" and it works, but is it a good practice?
@esteban21 : If it doesn’t find any document with filter ‘_id :{ name : Storename, location: somewhere, phone : 4628723 }’ then it would work but if it does find any then if would throw an error !! Since this question is not about upsert a document( it’s all about upsert on array ) then I’ve not mentioned anything here, maybe if you need it add a third updateOne and add $ne for the filter criteria..
My bad, I know that the question was about the nested documents. Actually it works, but at the first try it threw that error when I put the upsert in the first updateOne, and then I changed it to the second and it works even if there´s the match with the _id filter, is that normal?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.