Reputation: 12816
Given documents with the following structure:
{ 'id': 1, name: 'bob', type: 'foo', children: [{'id': 2}, {'id': 3}]}
{ 'id': 2, name: 'bob', type: 'foo' }
{ 'id': 3, name: 'bob', type: 'bar' }
{ 'id': 4, name: 'bob', type: 'foo', children: [{'id': 5}, {'id': 6}]}
{ 'id': 5, name: 'bob', type: 'foo' }
{ 'id': 6, name: 'bob', type: 'foo' }
How could I write an aggregate pipeline query to find all the documents where, if they have children, all the children are of type foo
(and the parent is of type 'foo')?
Additional notes:
children
is an array of objects with a property referencing other documents in the same collectionUpvotes: 0
Views: 2486
Reputation: 854
I have written a solution. please take a look the query. solution checkup link: https://mongoplayground.net/p/cu7Mf8XZHDI
this query is similar to the question: MongoDB (Mongoose) how to returning all document fields using $elemMatch
db.collection.find({},
{
students: {
$filter: {
input: "$students",
as: "student",
cond: {
$or: [
{
$eq: [
"$$student.age",
8
]
},
{
$eq: [
"$$student.age",
15
]
}
]
}
}
}
})
Upvotes: 0
Reputation: 12816
After some additional toying with the aggregation pipeline API, here is one potential solution.
The steps are:
$match
based on the type
criterion, to ensure only the parent documents with the appropriate type
are used subsequently in the pipeline.$lookup
on the child documents. Although this doesn't appear to be explicitly documented, $lookup
can use properties of nested objects in arrays with no difficulty.$elemMatch
and some negation to achieve the desired effect.Here's what that looks like using Robo3T (should be easily translated to other query clients):
Note: In this particular case, id
is just a placeholder for whatever the documents are being joined on, it is not the "official" _id
mongo field
db.getCollection('items').aggregate([
{ $match : { "type": "foo" } },
{
$lookup: {
from: "items",
localField: "children.id",
foreignField: "id",
as: "items"
}
},
{
$match : {
"items": {
$not: {
$elemMatch: {
"type": { $ne: "foo" }
}
}
}
}
}
])
This will exclude documents 1 and 3, since 3 has a type of "bar" and 1 includes it.
This may not be an optimal solution, I have not tested it on large datasets. Also, the final match using $elemMatch
is quite messy, so recommendations for improvements on that are welcome.
Upvotes: 1