raging_subs
raging_subs

Reputation: 879

MongoDB Aggregate query: see if value is in an array.

I have an array of ObjectIds, someOtherArray and need to compare them against the current objectId in the aggregate query. I believe I most of the query done, but I'm not sure how to see if my current objectId is in the array, someOtherArray. I comment on the code below on where I am stuck.

db.Collection.aggregate([
{
    $project: { 
        aField : {
            $map : {
                input: "$anArray",
                as : "arrayValue",
                //this is where I'm not sure on what to do.  I need something to return 
                //true or false on whether that item is in someOtherArray
                in: { $cond : {if: { $isInArray: [ $$arrayValue, someOtherArray] }, then: arrayValue, else: '' }}
            }
        } 
    }
}
]);

Upvotes: 1

Views: 2738

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151180

Not sure what your overall purpose here is, but basically if you just want to test if the contents of one array matches the content of another supplied array ( or even another array field in the document ) then you can just test this with the $setIntersection operator:

db.Collection.aggregate([
    { "$project": {
        "present": { "$gt": [
            { "$size": { "$setIntersection": [ "$anArray", someOtherArray ] } },
            0
        ]}
    }}
])

What is returned from $setIntersection is just the elements that are the "same" and as a "set" so removing duplicates. Maybe that is even what you want by it's own, without doing a $size test on the result to see if anything was there.

If you are after some kind of "match mask" where the elements are returned from the original in their original positions where they matched something in the comparison array, then you can do something comparing both arrays against each other with a $map operation on each:

db.Collection.aggregate([
    { "$project": {
        "altered": {
            "$map": {
                "input": "$anArray",
                "as": "orig",
                "in": {
                    "$cond": [
                        { "$anyElementTrue": {
                           "$map": {
                               "input": anotherArray
                               "as": "compare",
                               "in": { "$eq": [ "$$orig", "$$compare" ] }
                           }
                        }},
                        "$$orig",
                        false
                    ]
                }
            }
        }
    }}
])

And that would return an array of the same length as the original in the document but with only the "matched" elements in their original positions and all other elements represented as false. So the "truth" test here is by each element of the array to compare, then distilled to a single result using $anyElementTrue.

Keep in mind that these operations all act on a concept of "truth" or are otherwise a means of determining the "combination" of results in some way. So unless you are expecting false results in your output, then you probably want to filter first to remove those documents that will never contain a match. This is just an $in operator for the query:

db.Collection.aggregate([
    { "$match": {
        "anArray": { "$in": someOtherArray }
    }}
])

All assumptions here are that someOtherArray is an externally declared variable which is just going to "interpolate" with the BSON representation here, which it will. For using another field in the document you can just notate as "$someOtherArray" in all cases, with of course the exception of the $match pipeline which cannot compare fields, and would need you to $project the "truthiness" of that evaluation before you could "filter" on that result.

Upvotes: 2

Related Questions