MongoDB: $addToSet/$push document only if it doesn't already exist
I have a lists
collection where each document has an array of members
. Each element of the members
array is a document with an email
property, creation date
property, and some other meta. I have a unique index on members.email
to prevent the same email being entered into the same list twice, but I would like to retain the original date
value. Unfortunately, neither $addToSet
nor $push
seem to do this.
Example using $push:
$lists->update(array('_id' => $list['_id'], 'members.email' => array('$ne' => $email)), array('$push' => array('members' => array(
'email' => $email,
'date' => new MongoDate(),
// etc.
))));
And with $addToSet:
$lists-&开发者_开发百科gt;update(array('_id' => $list['_id']), array('$addToSet' => array('members' => array(
'email' => $email,
'date' => new MongoDate(),
// etc.
))));
Both examples replace the entire embedded document with the new one due to (I assume) the unique date
value. Is it possible to only $push
the "member" document if members.email
doesn't already exist or will I need to do this in two commands?
Alternatively, would it be better scalability-wise to put the members
in their own collection with a parent_list
-like property?
In my experience the reason that you cannot "$push" or "$addToSet" is because your target "members" is probably an embedded object (or converted to one after its creation). You can only use $push, $pushAll, $addToSet on embedded arrays...
Unfortunately for us php developers a cursor query always returns the data as arrays, the best way to check to see if something like "members" is an array or object, is to run a find() in the mongo shell and look for the curly/square brackets ( "{}" or "[]" ) in the result.
Hope this helps.
Why not have a collection where you store list_id, email, added_date You would then create a unique index on 2 keys list_id and email. That would prevent insertion of a record with the same list_id and email
There will not be embedded documents. You can still use find() by list_id
use $ne
in query condiction to filter documents with members of same email:
$lists->update(array('_id' => $list['_id'],
'members.email' => array('$ne' => $email)),
array('$addToSet' => array('members' => array(
'email' => $email,
'date' => new MongoDate(),
// etc.
))));
Yes it is possible. Check out the $exists operator in the mongoDB advanced queries section. Code an $exists condition into a where statement to only update when the members email doesn't exist.
IIUC, try using Upsert to either update the embedded doc (if such one) or insert it (if none). See this too.
As to you second question regarding scalability, its really case dependent... embedding the doc in the parent require harder updates and bigger data fetch, but reduce the fetch queries count.
精彩评论