$pull matching element in sub array

Hi,

In $pull documentation we find an exemple to pull an element from an array.

See https://docs.mongodb.com/manual/reference/operator/update/pull/

Source data :

    {
       _id: 1,
       results: [
          { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
          { item: "B", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 9 } ] }
       ]
    }
    {
       _id: 2,
       results: [
          { item: "C", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 7 } ] },
          { item: "B", score: 4, answers: [ { q: 1, a: 0 }, { q: 2, a: 8 } ] }
       ]
    }

The query :

    db.survey.update(
      { },
      { $pull: { results: { answers: { $elemMatch: { q: 2, a: { $gte: 8 } } } } } },
      { multi: true }
    )

This end with this result :

    {
       "_id" : 1,
       "results" : [
          { "item" : "A", "score" : 5, "answers" : [ { "q" : 1, "a" : 4 }, { "q" : 2, "a" : 6 } ] }
       ]
    }
    {
       "_id" : 2,
       "results" : [
          { "item" : "C", "score" : 8, "answers" : [ { "q" : 1, "a" : 8 }, { "q" : 2, "a" : 7 } ] }
       ]
    }

The query removed the entier result (probably as expected :slight_smile: ).

I am looking for a way to only remove the anwser that match and not the entirer result.

Expected result :

    {
        _id: 1,
            results: [
        { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
        { item: "B", score: 8, answers: [ { q: 1, a: 8 } ] }
    ]
    }
    {
        _id: 2,
            results: [
        { item: "C", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 7 } ] },
        { item: "B", score: 4, answers: [ { q: 1, a: 0 } ] }
    ]
    }

I tried things around $ and $ but not luck.

When I will figure out this then I will want to filter results too before looking into anwsers. For exemple only if restults.score is $get to 8 so final result should be :

    {
        _id: 1,
            results: [
        { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
        { item: "B", score: 8, answers: [ { q: 1, a: 8 } ] }
    ]
    }
    {
        _id: 2,
            results: [
        { item: "C", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 7 } ] },
        { item: "B", score: 4, answers: [ { q: 1, a: 0 }, { q: 2, a: 8 } ] }
    ]
    }

Hi @Maxence_Warzecha,

The below query uses array filters,

db.survey.update(
   { },
   {"$pull": {"results.$[elem].answers": { a: { $gte: 8 } } }  },
   { "arrayFilters": [ { "elem.answers.q": {"$eq": 2}} ], "multi": true }
);

*output*:
    MongoDB Enterprise mongos> db.survey.find().pretty();
    {
    	"_id" : 1,
    	"results" : [
    		{
    			"item" : "A",
    			"score" : 5,
    			"answers" : [
    				{
    					"q" : 1,
    					"a" : 4
    				},
    				{
    					"q" : 2,
    					"a" : 6
    				}
    			]
    		},
    		{
    			"item" : "B",
    			"score" : 8,
    			"answers" : [ ]
    		}
    	]
    }
    {
    	"_id" : 2,
    	"results" : [
    		{
    			"item" : "C",
    			"score" : 8,
    			"answers" : [
    				{
    					"q" : 2,
    					"a" : 7
    				}
    			]
    		},
    		{
    			"item" : "B",
    			"score" : 4,
    			"answers" : [
    				{
    					"q" : 1,
    					"a" : 0
    				}
    			]
    		}
    	]
    }

Reference: $[<identifier>] — MongoDB Manual

Hi,

Very interesting. However in a: { $gte: 8 } apply to the answer withing nested array and I am looking for this filter to apply on resulls.score (parent array).

Did not know about arrayFilters, I will look at it in more details.

I guess the following query anwser the first part of my question (eg. pull in sub array without filtering on results.score first). Pull properly all anwsers with q equals to `2``

db.survey.update(
    { },
    {"$pull": {"results.$[].answers": { q: { $eq: 2 } } }  },
    { "multi": true }
);

The question now (and not use if arrayFilters can help) is to know how to filter on results.score before looking into anwsers.

For exemple, I want to filter anwsers if first results.score = 8 and then if anwsers.q = 2, final result should be :

{
    _id: 1,
    results: [
        { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
        { item: "B", score: 8, answers: [ { q: 1, a: 8 } ] }
    ]
},
{
    _id: 2,
    results: [
        { item: "C", score: 8, answers: [ { q: 1, a: 8 } ] },
        { item: "B", score: 4, answers: [ { q: 1, a: 0 }, { q: 2, a: 8 } ] }
    ]
}

2 elements removed.

Hi @Maxence_Warzecha,

The condition with results.score =8 is added to array filters. Element to remove i.e., results.answers.q=4 is mentioned within $pull.

db.survey.update(
   {},
   {"$pull": {"results.$[elem].answers": { q:  2} }  },
   { "arrayFilters": [ {"elem.score" : 8 } ], "multi": true }
);
2 Likes

Hi @Laks,

That’s exactly what I was looking for! Thank you for this query :slight_smile:. I definitly learn something new with arrayFilters! In your first exemple I did not get it because you were filtering again using elem.answers.q and it confused me :sweat_smile: but it was already the way to go, just needed to filter elem.score

With my words, $[elem] is a kind of an “alias” for each elements in results array and is used in arrayFilters to filter these elements before trying to $pull.

For those that want a proper explaination of arrayFilters with <array>.$[<identifier>] you can look here : https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/

Thank you again @Laks

1 Like

Here is the result with @Laks query (my expected result) :

 [ {
    "_id": 1,
    "results": [{
        "item": "A",
        "score": 5,
        "answers": [{
            "q": 1,
            "a": 4
        }, {
            "q": 2,
            "a": 6
        }]
    }, {
        "item": "B",
        "score": 8,
        "answers": [{
            "q": 1,
            "a": 8
        }]
    }]
},{
    "_id": 2,
    "results": [{
        "item": "C",
        "score": 8,
        "answers": [{
            "q": 1,
            "a": 8
        }]
    }, {
        "item": "B",
        "score": 4,
        "answers": [{
            "q": 2,
            "a": 8
        }, {
            "q": 1,
            "a": 0
        }]
    }]
}]

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.