Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

php

mongodb

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->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?

like image 924
Kevin Avatar asked Dec 05 '10 01:12

Kevin


3 Answers

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.

like image 102
bm_i Avatar answered Nov 03 '22 00:11

bm_i


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

like image 29
Dmitri Avatar answered Nov 02 '22 22:11

Dmitri


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.
))));
like image 28
kelvinhust Avatar answered Nov 03 '22 00:11

kelvinhust