Check if every element in array matches condition

Wex picture Wex · May 11, 2014 · Viewed 27.3k times · Source

I have a collection of documents:

date: Date
users: [
  { user: 1, group: 1 }
  { user: 5, group: 2 }
]

date: Date
users: [
  { user: 1, group: 1 }
  { user: 3, group: 2 }
]

I would like to query against this collection to find all documents where every user id in my array of users is in another array, [1, 5, 7]. In this example, only the first document matches.

The best solution I've been able to find is to do:

$where: function() { 
  var ids = [1, 5, 7];
  return this.users.every(function(u) { 
    return ids.indexOf(u.user) !== -1;
  });
}

Unfortunately, this seems to hurt performance is stated in the $where docs:

$where evaluates JavaScript and cannot take advantage of indexes.

How can I improve this query?

Answer

Asya Kamsky picture Asya Kamsky · May 13, 2014

The query you want is this:

db.collection.find({"users":{"$not":{"$elemMatch":{"user":{$nin:[1,5,7]}}}}})

This says find me all documents that don't have elements that are outside of the list 1,5,7.