Aktualisierung 2017
Eine so gut gestellte Frage verdient eine moderne Antwort. Die Art der angeforderten Array-Filterung kann tatsächlich in modernen MongoDB-Versionen nach 3.2 einfach durch $match
durchgeführt werden und $project
Pipeline-Stufen, ähnlich wie es die ursprüngliche einfache Abfrageoperation beabsichtigt.
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 ] }
]
}
}
}
}}
])
Dabei wird der $filter
verwendet und $map
Operatoren, nur die Elemente aus den Arrays zurückzugeben, die die Bedingungen erfüllen würden, und ist für die Leistung weitaus besser als die Verwendung von $unwind
. Da die Pipeline-Stufen effektiv die Struktur von „query“ und „project“ aus einem .find()
widerspiegeln Betrieb ist die Leistung hier im Wesentlichen auf Augenhöhe mit einem solchen und Betrieb.
Beachten Sie, dass dort, wo die Absicht ist, tatsächlich "dokumentenübergreifend" zu arbeiten um Details aus "mehreren" Dokumenten statt aus "einem" zusammenzuführen, würde dies normalerweise eine Art von $unwind
erfordern Operation, um dies zu ermöglichen, damit die Array-Elemente für "Gruppierung" zugänglich sind.
Dies ist im Grunde der Ansatz:
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"
}}
}}
])
Dies ist eine "Array-Filterung" bei mehr als einer einzelnen Übereinstimmung mit den grundlegenden Projektionsfunktionen von .find()
kann nicht.
Sie haben "verschachtelte" Arrays, daher müssen Sie $unwind
verarbeiten zweimal. Zusammen mit den anderen Operationen.