I have a collection with embedded array of objects. I need a query to select only those documents where each of embedded objects
prop equals to some value:
{
{
_id: 1,
objects: [{prop_1: ..., foo: true}, {prop_1: ..., foo: false}, {prop_1: ...}]
},
{
_id: 2,
objects: [{prop_1: ..., foo: false}, {prop_1: ..., foo: false}, {prop_1: ...}]
},
{
_id: 3,
objects: [
{prop_1: ..., foo: true}, {prop_1: ..., foo: true}, {prop_1: ..., foo: true}
]
}
}
I need a query to get all the documents where each element of objects
has foo === true
. If one of objects.foo
is not exists or not equals true
do not return this document in result. In this case only record with _id : 3
meets this condition.
1. you can use $allElementsTrue
directly if checking explicitly true
edit
as pointed out by @aneroid this one's shorter if directly checking true
db.collection.find({ $expr: { $allElementsTrue: "$objects.foo" } })
db.collection.aggregate([
{
$match: {
$expr: {
$allElementsTrue: {
$map: {
input: "$objects",
in: "$$this.foo"
}
}
}
}
}
])
or with a small addition if you want to check other values too
db.collection.aggregate([
{
$match: {
$expr: {
$allElementsTrue: {
$map: {
input: "$objects",
in: { $eq: [ "$$this.foo", yourValue ] }
}
}
}
}
}
])
2. or by comparing the filtered objects array size with the actual object array size
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
{
$size: "$objects"
},
{
$size: {
$filter: {
input: "$objects",
cond: { $eq: [ "$$this.foo", yourValue ] }
}
}
}
]
}
}
}
])
3. or using $reduce
by keeping a boolean accumulator and by checking if it finally ends up being true
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
true,
{
$reduce: {
input: "$objects",
initialValue: true,
in: { $and: ["$$value", { $eq: ["$$this.foo", yourValue ] }] }
}
}
]
}
}
}
]);
4. or using $setIsSubset
since now the first array (without dupes) is a subset(equal) of the second
db.collection.aggregate([
{
$match: {
$expr: {
$setIsSubset: [
{ $map: { input: "$objects", in: "$$this.foo" } },
[ yourValue ]
]
}
}
}
])
5. or with a .find
by getting the negation of all the docs that do not have foo
as yourValue
db.collection.find({
objects: {
$not: {
$elemMatch: { foo: { $ne: yourValue } }
}
}
})