I have a collection that has objects of various random structures/depths. Within them, there is one object that I want to get as my results, which has a specific key/value pair that I can use to find them.
An example from my collection:
{
"_id": ObjectId("123"),
"someKey": "blue",
"children": [
{
"foo": "bar",
"anotherKey": "anotherValue",
"whateverA": 111
}
]
}
{
"_id": ObjectId("456"),
"blahKey": "dog",
"children": [
{
"anotherRandom": "randomValue",
"children": [
{
"moreRandom": "stuffValue",
"children": [
{
"foo": "bar",
"animalKey": "legsValue",
"whateverB": 222
}
]
}
]
}
]
}
I would like to search for subdocuments that contain "foo: bar", and get a result that looks like the following:
{
"foo": "bar",
"anotherKey": "anotherValue",
"whateverA": 111
}
{
"foo": "bar",
"animalKey": "legsValue",
"whateverB": 222
}
Then I can paginate the results. Is this even possible in MongoDB 5?
Thank you.
CodePudding user response:
If we use the solution of @rickhg12hs
You can do something like:
db.collection.aggregate([
{
$addFields: {
res: {
$function: {
body: "function drill(t, n) {if (n.length > 0){for (let elem of n){if(elem['foo'] && elem['foo'] === 'bar'){t.push(elem);}else {drill(t, elem.children)}}}return t}",
args: [
[],
"$children"
],
lang: "js"
}
}
}
},
{
$project: {
res: {$arrayElemAt: ["$res", 0]},
_id: 0
}
},
{
$replaceRoot: {newRoot: "$res"}
}
])
As you can see on this playground example.
We can use $function
to recursively look for this key named foo
and return the object that contains it.
Edit according to a question in the comment:
You can use your code to manipulate it according to your needs: for example in js:
const key = 'foo';
const val = 'bar';
const body = `function drill(t, n) {if (n.length > 0){for (let elem of n){if(elem[${key}] && elem[${key}] === ${val}){t.push(elem);}else {drill(t, elem.children)}}}return t}`;
db.collection.aggregate([
{
$addFields: {res: {$function: {body, args: [[], "$children"], lang: "js"}}}
},
{
$project: {
res: {$arrayElemAt: ["$res", 0]}, _id: 0}
},
{
$replaceRoot: {newRoot: "$res"}
}
])