Annotatie voor wie op zoek is naar - Buitenlandse graaf
Een beetje beter dan oorspronkelijk werd beantwoord, is om de nieuwere vorm van $opzoeken
van MongoDB 3.6. Dit kan het "tellen" doen binnen de "sub-pipeline"-expressie in tegenstelling tot het retourneren van een "array" voor daaropvolgende filtering en telling of zelfs het gebruik van $unwind
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"let": { "id": "$_id" },
"pipeline": [
{ "$match": {
"originalLink": "",
"$expr": { "$eq": [ "$$id", "$_id" ] }
}},
{ "$count": "count" }
],
"as": "linkCount"
}},
{ "$addFields": {
"linkCount": { "$sum": "$linkCount.count" }
}}
])
Niet waar de oorspronkelijke vraag om vroeg, maar een deel van het onderstaande antwoord in de nu meest optimale vorm, zoals natuurlijk het resultaat van $lookup
wordt teruggebracht tot het "overeenkomende aantal" alleen in plaats van "alle overeenkomende documenten".
Origineel
De juiste manier om dit te doen is door de "linkCount"
. toe te voegen naar de $group
stage en een $first
op eventuele aanvullende velden van het bovenliggende document om de "enkelvoud" vorm te krijgen, net als de staat "voor" de $unwind
is verwerkt op de array die het resultaat was van $lookup
:
Alle details
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$unwind": "$link" },
{ "$match": { "link.originalLink": "" } },
{ "$group": {
"_id": "$_id",
"partId": { "$first": "$partId" },
"link": { "$push": "$link" },
"linkCount": {
"$sum": {
"$size": {
"$ifNull": [ "$link.linkHistory", [] ]
}
}
}
}}
])
Produceert:
{
"_id" : ObjectId("594a6c47f51e075db713ccb6"),
"partId" : "f56c7c71eb14a20e6129a667872f9c4f",
"link" : [
{
"_id" : ObjectId("594b96d6f51e075db67c44c9"),
"originalLink" : "",
"emailGroupId" : ObjectId("594a6c47f51e075db713ccb6"),
"linkHistory" : [
{
"_id" : ObjectId("594b96f5f51e075db713ccdf")
},
{
"_id" : ObjectId("594b971bf51e075db67c44ca")
}
]
}
],
"linkCount" : 2
}
Groep op partId
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$unwind": "$link" },
{ "$match": { "link.originalLink": "" } },
{ "$group": {
"_id": "$partId",
"linkCount": {
"$sum": {
"$size": {
"$ifNull": [ "$link.linkHistory", [] ]
}
}
}
}}
])
Produceert
{
"_id" : "f56c7c71eb14a20e6129a667872f9c4f",
"linkCount" : 2
}
De reden waarom je het op deze manier doet met een $unwind
en dan een $match
is vanwege de manier waarop MongoDB de pijplijn daadwerkelijk afhandelt wanneer deze in die volgorde wordt uitgegeven. Dit is wat er gebeurt met de $lookup
zoals wordt aangetoond de "explain"
output van de operatie:
{
"$lookup" : {
"from" : "link",
"as" : "link",
"localField" : "_id",
"foreignField" : "emailGroupId",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"originalLink" : {
"$eq" : ""
}
}
}
},
{
"$group" : {
Ik verlaat het deel met $group
in die uitvoer om aan te tonen dat de andere twee pijplijntrappen "verdwijnen". Dit komt omdat ze zijn "opgerold" in de $opzoeken
pijplijnfase zoals afgebeeld. Dit is in feite hoe MongoDB omgaat met de mogelijkheid dat de BSON-limiet kan worden overschreden door het resultaat van "joining" resultaten van $lookup
in een array van het bovenliggende document.
U kunt de bewerking ook als volgt schrijven:
Alle details
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$addFields": {
"link": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"linkCount": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"as": "l",
"in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
}
}
}
}}
])
Groeperen op partId
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$addFields": {
"link": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"linkCount": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"as": "l",
"in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
}
}
}
}},
{ "$unwind": "$link" },
{ "$group": {
"_id": "$partId",
"linkCount": { "$sum": "$linkCount" }
}}
])
Die dezelfde uitvoer heeft, maar "afwijkt" van de eerste query doordat de $filter
hier wordt toegepast "na" ALLE resultaten van de $lookup
worden geretourneerd in de nieuwe array van het bovenliggende document.
Dus in prestatietermen is het eigenlijk effectiever om het op de eerste manier te doen, maar het is ook overdraagbaar naar mogelijke grote resultatensets "vóór het filteren", die anders de 16 MB BSON-limiet zouden breken.
Als een kanttekening voor degenen die geïnteresseerd zijn, kun je in toekomstige releases van MongoDB (vermoedelijk 3.6 en hoger) $replaceRoot
in plaats van een $addFields
met gebruik van de nieuwe $mergeObjects
pijpleiding exploitant. Het voordeel hiervan is dat we als "blok" de "gefilterd"
kunnen declareren inhoud als een variabele via $let
, wat betekent dat u niet dezelfde $filter hoeft te schrijven
"tweemaal":
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$$ROOT",
{ "$let": {
"vars": {
"filtered": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
}
},
"in": {
"link": "$$filtered",
"linkCount": {
"$sum": {
"$map": {
"input": "$$filtered.linkHistory",
"as": "lh",
"in": { "$size": { "$ifNull": [ "$$lh", [] ] } }
}
}
}
}
}}
]
}
}}
])
Niettemin is de beste manier om dergelijke "gefilterde" $lookup
operations is op dit moment "still" met behulp van de $unwind
dan $match
patroon, totdat u queryargumenten kunt verstrekken aan $ opzoeken
rechtstreeks.