Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find Documents in MongoDB whose with an array field is a subset of a query array

Tags:

mongodb

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?

like image 316
duckworthd Avatar asked Apr 19 '13 16:04

duckworthd


3 Answers

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"
like image 65
diyism Avatar answered Nov 14 '22 13:11

diyism


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.

like image 42
Asya Kamsky Avatar answered Nov 14 '22 12:11

Asya Kamsky


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.

like image 44
JohnnyHK Avatar answered Nov 14 '22 11:11

JohnnyHK