4

I have collection of documents like following:

{
_id: ObjectId("55b164c65c1a8f360078c917"),
message_id: ["abc","def"],
message: [{message_id: "efgh", message_body: "somebody"}]
}

I want to find out mongodb query to find out those document whose message.message_id is not in message_id. Can anyone please help me with this.

3
  • are you using terminal to do this? Commented Feb 17, 2016 at 4:58
  • I am using compose.io Commented Feb 17, 2016 at 5:37
  • @GujaratSantana I am trying to write a query like: find({ "message.message_id" : <don't know how to provide {message_id array here}>}); something like we have "IN" in SQL Commented Feb 17, 2016 at 5:39

1 Answer 1

2

Probably the best approach with MongoDB 2.6 and greater uses .aggregate() with $redact:

db.collection.aggregate([
    { "$redact": {
        "$cond": {
            "if": { 
                "$gt": [
                    { "$size": {
                        "$setIntersection": [
                            { "$map": {
                                "input": "$message",
                                 "as": "msg",
                                 "in": "$$msg.message_id"
                            }},
                            "$message_id" 
                        ]
                    }},
                    0
                ]
            },
            "then": "$$PRUNE",
            "else": "$$KEEP"
        }
    }}
])

The "inside out" of the logic here is that $map is used to take out the keys of "message_id" from the "messages" array, and return that for comparison with the "message_id" array itself to see the result of $setIntersection. It's done this way to see "intersection" as there is nothing concrete here that says one is the "subset" of the other. So it's just the common elements, otherwise there is $setIsSubset.

Naturally if the $size of that intersection is greater than 0, then there was a match of elements. So that true condition would be "pruned", and anything else is "kept".

Or in prior versions use $where with this JavaScript eval instead, follwing exactly the same logic:

db.collection.find({
    "$where": function() {
        var self = this;
        return self.message.map(function(el) {
            return el.message_id;
        }).filter(function(el) {
            return self.mesage_id.indexOf(el) != -1
        }).length == 0;
    }
})

Another take on an aggregation pipeline is also possible in earlier versions, but it would take several pipeline stages and therefore several passes to complete. Therefore that would not be your best option pre MongoDB 2.6.

At any rate, standard query operations cannot normally "compare" one element of a document to another. So this leaves the aggregation framework or $where as the two tools that can do this comparison.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Blakes for explaining so well. works for me. +1
@VishalSharma stackoverflow.com/help/accepted-answer. Welcome to StackOverflow

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.