2017-update
Zo'n goed gestelde vraag verdient een modern antwoord. Het soort array-filtering dat wordt gevraagd, kan feitelijk worden gedaan in moderne MongoDB-releases na 3.2 via eenvoudig $match
en $project
pijplijnfasen, net zoals de oorspronkelijke gewone query-operatie bedoeld is.
db.accounts.aggregate([
{ "$match": {
"email" : "[email protected]",
"groups": {
"$elemMatch": {
"name": "group1",
"contacts.localId": { "$in": [ "c1","c3", null ] }
}
}
}},
{ "$addFields": {
"groups": {
"$filter": {
"input": {
"$map": {
"input": "$groups",
"as": "g",
"in": {
"name": "$$g.name",
"contacts": {
"$filter": {
"input": "$$g.contacts",
"as": "c",
"cond": {
"$or": [
{ "$eq": [ "$$c.localId", "c1" ] },
{ "$eq": [ "$$c.localId", "c3" ] }
]
}
}
}
}
}
},
"as": "g",
"cond": {
"$and": [
{ "$eq": [ "$$g.name", "group1" ] },
{ "$gt": [ { "$size": "$$g.contacts" }, 0 ] }
]
}
}
}
}}
])
Dit maakt gebruik van de $filter
en $map
operators om alleen de elementen uit de arrays terug te geven die aan de voorwaarden zouden voldoen, en is veel beter voor de prestaties dan het gebruik van $unwind
. Aangezien de pijplijnfasen effectief de structuur van "query" en "project" weerspiegelen van een .find()
bediening, de prestaties hier zijn in principe vergelijkbaar met die en de bediening.
Merk op dat waar het de bedoeling is om daadwerkelijk "over documenten" te werken om details samen te brengen uit "meerdere" documenten in plaats van "één", dan zou dit meestal een soort $unwind
vereisen om dit te doen, zodat de array-items toegankelijk zijn voor "groeperen".
Dit is eigenlijk de aanpak:
db.accounts.aggregate([
// Match the documents by query
{ "$match": {
"email" : "[email protected]",
"groups.name": "group1",
"groups.contacts.localId": { "$in": [ "c1","c3", null ] },
}},
// De-normalize nested array
{ "$unwind": "$groups" },
{ "$unwind": "$groups.contacts" },
// Filter the actual array elements as desired
{ "$match": {
"groups.name": "group1",
"groups.contacts.localId": { "$in": [ "c1","c3", null ] },
}},
// Group the intermediate result.
{ "$group": {
"_id": { "email": "$email", "name": "$groups.name" },
"contacts": { "$push": "$groups.contacts" }
}},
// Group the final result
{ "$group": {
"_id": "$_id.email",
"groups": { "$push": {
"name": "$_id.name",
"contacts": "$contacts"
}}
}}
])
Dit is "arrayfiltering" op meer dan een enkele overeenkomst die de basisprojectiemogelijkheden van .find()
kan niet.
Je hebt "geneste" arrays, daarom moet je $unwind
. verwerken tweemaal. Samen met de andere bewerkingen.