Pull or pullAll? to delete Array of emails?

I have an array of emails to delete as

[‘tim@apple.com’, ‘gates@microsoft.com’,'elon@tesla.com]

my collection contains :

userid: objectID,
fullname : …
contacts : [ {email: gates@microsoft.com } ]

what I need to do is either a findAndUpdate or findModify with a $pull to delete all contacts with email that equals to array of emails for a given userid.

all the items in the array are surely in the contacts list.

so far I have :slight_smile:

collection(“users”).findOneAndUpdate({_id : ObjectId(userId)}, {$pull : {contacts : {email : deleteArray}}},{multi:true});

now if I run the above,it will not throw an error because I cant pass in a array as the value for email. it does work if I pass in a single email. I tried using pullAll, but that does not work either. it says expected array but passed in a object.

What ways can I delete all elements in a array without looping over the findoneandupdate for each element in the array.

thanks.

Hello : )

I think this will work.

var delete_emails = [{"email" :tim@apple.com"}, {"email" : "gates@microsoft.com"},{"email" : "elon@tesla.com"}];

collection(“users”).findOneAndUpdate({_id : ObjectId(userId)}, {$pullAll : {contacts : delete_emails }},{multi:true});

I haven’t tested it see examples also in docs.

You can also use pipeline updates that are more powerful,but i dont think you need it here.You can use something like the bellow(add the filter,bellow is only the update)
For more complicated updates you can use $filter with a pipeline update.With filter you could do it as.

[ {
  "$addFields" : {
    "contacts" : {
      "$filter" : {
        "input" : "$contacts",
        "as" : "contact",
        "cond" : {
          "$not" : [ {
            "$in" : [ "$$contact.email", ["tim@apple.com", "gates@microsoft.com","elon@tesla.com"]]
          } ]
        }
      }
    }
  }
} ]

We don’t have sets in mongodb we only have arrays,so we cant do the check in O(1)
Unless mongodb converts it to a set internally,i dont know if it does.

Hi Taki
thanks,

if delete_emails are only indexes with the email addresses and not nested objects what would i change so

var delete_emails = [tim@apple.com,gates@microsoft.com,elon@tesla.com];

that is how my data is currently set up.

Also does pullAll need multi? since pullAll already removes all data in the array?

Sadly the example did not work, no errors, just the email sub-document was not deleted.

Further reading:

pullall as per the docs accept an Array, the docs do not state if it can accept and array of objects , although i am sure its possible, but in your example how does it know to look in to delete_emails.email? as “.email” positional syntax was not specified?

also, when you mentioned {$pullAll : {contacts : delete_emails }}, don’t we need to use positional since contacts is itself array of objects? i have tried contacts.email or contacts.$.email and none of these work.

thanks.

Hello

I didnt had time before to make those queries,but i did now.
They are working unless i made a typo,i re-send them now.

Data in

{
  "userid": 1,
  "fullname": "john-don",
  "contacts": [
    {
      "email": "gates@microsoft.com"
    },
    {
      "email": "me@gmail.com"
    }
  ]
}

Query1(with update operator and $pullAll) (the same query like before)
(will not work if {“phone” : “000” , “email” “me@gmail.com”} dont use it if contact documents
have more than the email)

findOneAndUpdate(
{"userid" : {"$eq" : 1}},
 {"$pullAll" : {"contacts" : [{"email" "tim@apple.com"} 
                              {"email" "gates@microsoft.com"}
                              {"email" "elon@tesla.com"}]}},
 {"multi" true})

Result1
Screenshot from 2021-07-31 21-43-04

Query2 (pipeline update,the query that i sended before)

findOneAndUpdate({"userid" : {"$eq" : 1}},
     [
    {
      "$addFields": {
        "contacts": {
          "$filter": {
            "input": "$contacts",
            "as": "contact",
            "cond": {
              "$not": [
                {
                  "$in": [
                    "$$contact.email",
                    [
                      "tim@apple.com",
                      "gates@microsoft.com",
                      "elon@tesla.com"
                    ]
                  ]
                }
              ]
            }
          }
        }
      }
    }
  ],
   {"multi" true})

Result2(same)
Screenshot from 2021-07-31 21-48-25

Dont use the first query if your contacts have more than emails,
Use the second query that will work,even if contacts that more than email.

{“multi” true} means to update many documents if they fit the filter creteria.
If not, the update will just update one document max, in the collection(its not related with the array),you can use updateMany that has multi true by default

$pullAll checks all array members if they are also members in the second array.