python – arrayFilters doesn't work with pymongo but it works in MongoDB console

Question:

I have this code in Python

from pymongo import MongoClient

db       = MongoClient('localhost', 27017)['myDB']['myCol']
params   = { "model": "ILX ACURA" }
asset    = { "newAsset": ["Foo", "Bar", "Poo"] }
document = { "$push": { "models.$[mod].years": asset } }
filters  = { "arrayFilters" : [ { "mod.model": params['model'] } ] }
updated  = db.update( {}, document, False, False, None, filters )

if updated.matched_count > 0:
    print 'DONE!'

When I run the script I get this error

WriteError: No array filter found for identifier 'mod' in path models.$[mod].years

I've been struggling with python for a long time and tried better in MongoDB console

db.myCol.updateOne({}, { $push: { "models.$[mod].years": { "newAsset": ["Foo", "Bar","Poo"] } } }, { arrayFilters: [ { "mod.model": "ILX ACURA" } ] } )

Surprise!

{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

What will be happening?

I use python 2.7 pymongo 3.8 mongoDB 4.0

Answer:

As @JackNavaRow points out in his opportune comment, the filters you apply must be passed in a list and not in a document as you have it, however that is not the only problem.

According to the PyMongo documentation the update method is deprecated , and it is suggested to use one of the methods: replace_one , update_one or update_many .

We will use update_one and you will be able to perform the task, I also suggest that whenever you try to perform operations on the database you use try except blocks

Your code could look like this:

from pymongo import MongoClient

db       = MongoClient('localhost', 27017)['myDB']['myCol']
params   = { "model": "ILX ACURA" }
asset    = { "newAsset": ["Foo", "Bar", "Poo"] }
# llamamos update al documento que representa la actualización, así no hay confusión
# con el documento que se está actualizando
update = { "$push": { "models.$[mod].years": asset } }
# filters es una lista, no un documento, cada elemento de la lista define un filtro
filters  = [ { "mod.model": params['model'] } ]

#usamos un bloque try except
try:
    updated  = db.update_one( {}, update, False, False, None, filters )
    if updated.matched_count > 0:
        print 'DONE!'
except Exception, e:
    print str(e)

This should do what you're trying to do (for 1 document).

Edition

Since you seem to be trying to update multiple documents, you should use update_many .

Your code changes, since the order of the arguments passed to the method is different. You can consult the documentation .

from pymongo import MongoClient

db       = MongoClient('localhost', 27017)['myDB']['myCol']
params   = { "model": "ILX ACURA" }
asset    = { "newAsset": ["Foo", "Bar", "Poo"] }
# llamamos update al documento que representa la actualización, así no hay confusión
# con el documento que se está actualizando
update = { "$push": { "models.$[mod].years": asset } }
# filters es una lista, no un documento, cada elemento de la lista define un filtro
filters  = [ { "mod.model": params['model'] } ]

#usamos un bloque try except
try:
    # usamos update_many
    updated  = db.update_many( {}, update, False, filters )
    if updated.matched_count > 0:
        #si deseamos ver la cantidad de documentos afectados:
        print updated.matched_count
        print 'DONE!'
except Exception, e:
    print str(e)

I hope this is what you were looking for.

Scroll to Top