I’ve implemented a (Python) method for adding/updating data on (sports) matches. It relies on bulkWrite() with multiple UpdateOne() calls, each with the upsert flag set to true. When I use $all in my filter, to make sure I’m matching all the submitted players in a given array, I get this:
def add_or_update_matches_2(matches):
db = get_db()
requests = []
for match in matches:
request = UpdateOne(
{"players": {"$all": match["players"]}},
{"$set": {"test_name": "test_value"}},
upsert=True
)
requests.append(request)
if len(requests) == 0:
return None
result = db.matches.bulk_write(requests)
return result
If I remove the $all, it works as I want, except that I need to be able to have players in any order. The error doesn’t seem to come from pymongo, so I guess it’s internal to MongoDB. Also, I don’t understand what the error means and what I’m supposed to do instead. Could it be a bug?
There are many reasons this might be failing. Can you provide a few sample documents so I can understand what the structure is like? One possibility I can think of is that if players is an array of documents you need to use $elemMatch with $all as explained here.
If I comment out the line containing insertOne, so that the document is created rather than updated, the “cannot infer query fields to set” error occurs. Note that the same filter works when updating an existing document (and with deleteMany), but when inserting a document using updateOne, it fails.
As you can see, this is a string array, not an array of objects, so the $elemMatch syntax shouldn’t be necessary.
Would love to see a explanation for this behavior.
Hi @Gustaf_Liljegren. Thanks for the repro code! The reason this doesn’t work is due to the upsert: true clause in the updateOne operation. You see, when an update operation with upsert: true doesn’t find any matching documents and the update parameter contains operations using update operator expressions (https://docs.mongodb.com/manual/reference/operator/update/#id1) the server first builds a base document using the query parameter itself and then applies the update operations to it.
In your case, the server is unable to look at "players" : {$all: [ "Ada", "Karl"]} and figure out that the base document is a 2-member array which results in this error.
It is possible that update eventually becomes smart enough to handle this but in the meantime you will need to workaround this limitation in your code.