开发者

MongoDB: $addToSet/$push document only if it doesn't already exist

开发者 https://www.devze.com 2023-01-28 17:54 出处:网络
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 uni

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号