I performed an update in sharded environment like :
db.collection.update({$or:[{a:2,b:3},{a:3,b:2}]},{$set:{x:5}})
But i got this error message :
update { q: { $or: [ {a:2,b:3},{a:3,b:2} ] }, u: {$set:{x:5}}, multi: false, upsert: false } does not contain _id or shard key for pattern { a: 1.0, b: 1.0 }
How can i perform this kind of update with the $or predicate on the shard key ?
Thanks
The main problem here is that your $or
condition does not really make much sense without the "multi" parameter of the update statement. At least the sharding conditional logic thinks so, even if your intention was to only match a singular document.
In the "mind" of the sharding manager that lives withing the mongos
router, the expectation is that you either target a singular shard or range of keys, or you are asking to access a possible variety or shards.
Here is the actual code handling this for reference:
// Validate that single (non-multi) sharded updates are targeted by shard key or _id
if (!updateDoc.getMulti() && shardKey.isEmpty() && !isExactIdQuery(updateDoc.getQuery())) {
return Status(ErrorCodes::ShardKeyNotFound,
stream() << "update " << updateDoc.toBSON()
<< " does not contain _id or shard key for pattern "
<< _manager->getShardKeyPattern().toString());
}
So as you should clearly see in the "if" condition, the expectation here is that there is either a definition of the "shard key" within the query, or at least an exact _id
to facilitate an exact match.
Therefore your two provisions on making this valid for an update over shards is to either:
Include an "range" over possible values in the shard key with the query criteria. I don't know your shard key so I cannot really give a sample. But basically:
{
"shardKey": { "$gt": minShardKey, "$lt": maxShardKey },
"$or": [
{ "a": 2, "b": 3 },
{ "a": 3, "b": 2 }
]
}
As the query condition, where the minShardkey
and maxShardKey
refer to the minimum and maximum possible values on that key within the range ( on the also hypothetical "shardKey" field ) in order to make the manager consider that you really intend to search across all shards.
Include the "multi" option in the update like so:
db.collection.update(
{ "$or":[
{ "a": 2, "b": 3 },
{ "a": 3, "b": 2 }
]},
{ "$set": { "x":5 } },
{ "multi": true }
)
Which makes the selection "possibly" match more than one, and is therefore valid for searching over shards without needing a targeted shard key.
In either case, the logic is fulfilled in that you at least "intend" to search the conditions across the shards to find something or "things" that match the conditions you have given.
As an additional note, then also consider that "upsert" actions have similar restrictions, and that the general principle is that the shard key needs to be addressed, othewise the operation in invalid since there needs to be some indication of which shard to insert the new data.