2017-opdatering
Sådan et godt stillet spørgsmål fortjener et moderne svar. Den slags array-filtrering, der anmodes om, kan faktisk udføres i moderne MongoDB-udgivelser efter 3.2 via blot $match
og $project
pipeline-stadier, ligesom den oprindelige almindelige forespørgselsoperation har til hensigt.
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 ] }
]
}
}
}
}}
])
Dette gør brug af $filter
og $map
operatører til kun at returnere elementerne fra arrays, som ville opfylde betingelserne, og det er langt bedre for ydeevnen end at bruge $unwind
. Da pipelinestadierne effektivt afspejler strukturen af "forespørgsel" og "projekt" fra en .find()
drift, er ydelsen her stort set på niveau med sådan og drift.
Bemærk, at hvor hensigten er faktisk at arbejde "på tværs af dokumenter" for at samle detaljer ud af "flere" dokumenter i stedet for "én", så ville dette normalt kræve en form for $unwind
operation for at gøre det, som sådan gør det muligt for array-elementerne at være tilgængelige for "gruppering".
Dette er grundlæggende tilgangen:
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"
}}
}}
])
Dette er "array-filtrering" på mere end et enkelt match, som de grundlæggende projektionsfunktioner i .find()
ikke kan gøre.
Du har "indlejrede" arrays, derfor skal du behandle $unwind
to gange. Sammen med de andre operationer.