17xande
17xande

Reputation: 2803

MongoDB find all not in this array

I'm trying to find all users except for a few, like this:

// get special user IDs
var special = db.special.find({}, { _id: 1 }).toArray();

// get all users except for the special ones
var users = db.users.find({_id: {$nin: special}});

This doesn't work because the array that I'm passing to $nin is not and array of ObjectId but an array of { _id: ObjectId() }

Variable special looks like this after the first query:

[ { _id: ObjectId(###) }, { _id: ObjectId(###) } ]

But $nin in the second query needs this:

[ ObjectId(###), ObjectId(###) ]

How can I get just the ObjectId() in an array from the first query so that I can use them in the second query?

Or, is there a better way of achieving what I'm trying to do?

Upvotes: 1

Views: 1332

Answers (1)

chridam
chridam

Reputation: 103375

Use the cursor.map() method returned by the find() function to transform the list of { _id: ObjectId(###) } documents to an array of ObjectId's as in the following

var special = db.special.find({}, { _id: 1 }).map(function(doc){
    return doc._id;
});

Another approach you can consider is using the $lookup operator in the aggregation framework to do a "left outer join" on the special collection and filtering the documents on the new "joined" array field. The filter should match on documents whose array field is empty.

The following example demonstrates this:

db.users.aggregate([
    {
        "$lookup": {
            "from": "special",
            "localField": "_id",
            "foreignField": "_id",
            "as": "specialUsers" // <-- this will produce an arry of "joined" docs
        }
    },
    { "$match": { "specialUsers.0": { "$exists": false } } } // <-- match on empty array
])

Upvotes: 3

Related Questions