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?
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"
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With