4

Similar to Find document with array that contains a specific value, but i'm trying to pull it.

db.getCollection('users').find({'favorites':{$elemMatch:{0:5719}}}, {"favorites.$": 1})

returns this:

{
    "_id" : "FfEj5chmviLdqWh52",
    "favorites" : [ 
        [ 
            5719, 
            "2016-03-21T17:46:01.441Z", 
            "a"
        ]
    ]
}

even after this returned 1:

Meteor.users.update(this.userId, {$pull: {'favorites':{$elemMatch:{0:movieid}}}})
0

2 Answers 2

2

It doesn't work because $pull is trying to remove a matching element from the "favorites" array. What you want to do is remove from the "array inside the array" of favorites.

For this you need a positional match to point to the nth inner element, then a very careful $pull expression to actually remove that element:

Meteor.users.update(
  { "favorites": { "$elemMatch": { "$elemMatch": { "$eq": 5719 }  } } },
  { "$pull": { "favorites.$": 5719 } }
)

The "double" $elemMatch with the $eq operator is a bit more expressive than { 0: 5719 } since it is not "locked" into the first position only and is actually looking at the matching value. But you can write it that way if you must, or if you "really mean" to match that value in the first position only.

Note that the "index" returned from the match in the positional $ argument is actually that of the "outer" array. So to pull from the

Of course if there is only ever one nested array element within, the you might as well just write:

  { "$pull": { "favorites.0": 5719 } }

Using the direct "first index" position, since you know the inner array will always be there.

In either case, your object updates correctly:

{
        "_id" : "FfEj5chmviLdqWh52",
        "favorites" : [
                [
                        "2016-03-21T17:46:01.441Z",
                        "a"
                ]
        ]
}

If you are trying to $pull the entire array entry from favorites, then the $eleMatch just needs to be dialed back one element:

Meteor.users.update(
    { "_id": this.userId },
    { "$pull": { "favorites": { "$elemMatch": { "$eq": 5719 } } } }
)

Or even:

Meteor.users.update(
    { "_id": this.userId },
    { "$pull": { "favorites": { "$elemMatch": { "0": 5719 } } } }
)

Noting that:

    { "_id": this.userId },

Is the long form that we generally use as a "query" selector, and especially when we want criteria "other than" the _id of the document. MiniMongo statements require at "least" the _id of the document though.

The rest of the statement has one "less" $elemMatch because the $pull already applies to the array.

That removes the whole matched element from the outer array:

{
        "_id" : "FfEj5chmviLdqWh52",
        "favorites" : []
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, but i want to remove all (ideally one) favorites with the specified ID (first element of the favorite).
@CeesTimmerman So how is that not what this answers? And how is that not what you asked? The first element has been removed.
The ID i'm looking for is the first element of the favorite, and that entire favorite should be removed from the favorites array. Your short code didn't remove the favorite (nor the ID within the favorite) and your long code gives me this: MongoError: The dollar ($) prefixed field '$elemMatch' in 'favorites.$elemMatch' is not valid for storage.
The error was from including this.userId,- is that safe to leave out?
@CeesTimmerman Undertstand what you are going for now. Added the correct update and explanation.
|
0

This is the first code i found that actually works:

Meteor.users.update(Meteor.userId(), {$pull: {favorites: {$in: [i]}}})

Apparently $in does partial matching. It seems safer than the working code from this answer:

Meteor.users.update(
    { "_id": this.userId },
    { "$pull": { "favorites": { "$elemMatch": { "$eq": i } } } }
)

5 Comments

Why would it be safer? It's actually a pet peeve, as far too many people use $in because they "think" it must be used when matching on an array. The use case is in fact the reverse, where you supply an "array of arguments" that are possible matches for the property. You are after removing an element based on matching a single condition. I cannot see why an "array" makes this clear or safe. To be the code seems obfiscated.
It seemed that position might be taken into account, but what i understood from the docs it acts the same as your code.
So why submit an "answer" stating you think it is safer or better in any way? You can't say "I knew this all along" when your question makes no such statement. Your question in fact states, "I don't know how to do this", and then someone was nice enough to show you. Misleading title and all, taken into account.
You initially only answered the title and the last bit of code is the same as the non-functional code in my question, taking away my accept. My answer clearly stated the working code. Only after that code did i discover in your long answer that the $eq you suggested also works. You still got my upvote for that.
It is not the same and I clearly state the difference ( i.e don't need two $elemMatch in a $pull since it already acts on an array), and of course it does actually remove the element as described. Anyhow this does seem to be wandering. $eq looks for equality of a single value, $in is meant for a list of values to test for equality of "either" supplied in the list. So it's just a copy of the code interposing one operator for another. Both work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.