9

Suppose I have a insert a set of documents each with an array field. I would like to find all documents such that their array field is a subset of a query array. For example, if I have the following documents,

collection.insert([
  {
     'name': 'one',
     'array': ['a', 'b', 'c']
  },
  {
     'name': 'two',
     'array': ['b', 'c', 'd']
  },
  {
     'name': 'three',
     'array': ['b', 'c']
  }
])

and I query collection.find({'array': {'$superset': ['a', 'b', 'c']}), I would expect to see documents one and three as ['a', 'b', 'c'] and ['b', 'c'] are both subsets of ['a', 'b', 'c']. In other words, I'd like to do the inverse of Mongo's $all query, which selects all documents such that the query array is a subset of the document's array field. Is this possible? and if so, how?

1
  • You'd need to do some aggregation IMHO. Commented Apr 19, 2013 at 16:45

3 Answers 3

11

In MongoDb, for array field:

"$in:[...]" means "intersection" or "any element in",
"$all:[...]" means "subset" or "contain",
"$elemMatch:{...}" means "any element match"
"$not:{$elemMatch:{$nin:[...]}}" means "superset" or "in"
Sign up to request clarification or add additional context in comments.

Comments

10

There is a simple way to do this with aggregation framework or with a find query.

Find query is simple, but you have to use $elemMatch operator:

> db.collection.find({array:{$not:{$elemMatch:{$nin:['a','b','c']}}}}, {_id:0,name:1})

Note that this indicates that we want to not match an array which has an element which is (at the same time) not equal to 'a', 'b' or 'c'. I added a projection which only returns the name field of the resultant document which is optional.

Comments

4

To do this within the context of aggregation, you can use $setIsSubset:

db.collection.aggregate([
    // Project the original doc and a new field that indicates if array
    // is a subset of ['a', 'b', 'c']
    {$project: {
        doc: '$$ROOT',
        isSubset: {$setIsSubset: ['$array', ['a', 'b', 'c']]}
    }},
    // Filter on isSubset
    {$match: {isSubset: true}},
    // Project just the original docs
    {$project: {_id: 0, doc: 1}}
])

Note that $setIsSubset was added in MongoDB 2.6.

1 Comment

nice one. I removed the old inefficient aggregation from my answer as this is much more elegant and it's now been available for a while.

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.