Korzystanie $lookup
tak nie jest najbardziej „skuteczny” sposób zrobić to, co chcesz tutaj. Ale o tym później.
W podstawowej koncepcji, wystarczy użyć $filter
na wynikającym z tablicy:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$project": {
"id": 1,
"value": 1,
"contain": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$eq": [ "$$child.value", "1" ] }
}
}
}}
]);
Albo użyć $redact
zamiast:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$value", "0" ] },
{ "$eq": [ "$value", "1" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
]);
Zarówno uzyskać ten sam wynik:
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
}
]
}
Dół linia jest taka, że sama $lookup
nie może "jeszcze" zapytać o wybierz niektóre dane. Więc wszystkie "filtrowanie" musi nastąpić po $lookup
.
Ale tak naprawdę dla tego typu "samodzielnego łączenia" w ogóle lepiej nie używać $lookup
w ogóle i całkowicie uniknąć dodatkowych kosztów odczytu i "mieszania". Wystarczy pobrać związane z nimi przedmioty i $group
zamiast:
db.test.aggregate([
{ "$match": {
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}},
{ "$group": {
"_id": {
"$cond": {
"if": { "$eq": [ "$value", "0" ] },
"then": "$id",
"else": { "$arrayElemAt": [ "$contain", 0 ] }
}
},
"value": { "$first": { "$literal": "0"} },
"childs": {
"$push": {
"$cond": {
"if": { "$ne": [ "$value", "0" ] },
"then": "$$ROOT",
"else": null
}
}
}
}},
{ "$project": {
"value": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$ne": [ "$$child", null ] }
}
}
}}
])
Które tylko wychodzi trochę inaczej, bo celowo usunięte zbędne pola. Dodaj je do siebie, jeśli naprawdę chcesz:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
Więc jedynym prawdziwym problemem jest tu „filtrowanie” wszelkie null
wynik z tablicy stworzonej kiedy był obecny dokument z parent
w punktach przerobu do $push
.
Czego tu również brakuje, to to, że szukany wynik nie wymaga w ogóle agregacji lub "sub-zapytań". Struktura, którą zawarłeś lub ewentualnie znalazłeś w innym miejscu, została "zaprojektowana", abyś mógł otrzymać "węzeł" i wszystkie jego "dzieci" w jednym zapytaniu.
Oznacza to, że tylko "zapytanie" jest wszystkim, co jest naprawdę potrzebne, a zbieranie danych (co jest wszystkim, co dzieje się, ponieważ żadna zawartość nie jest naprawdę "zmniejszana") jest tylko funkcją iterowania wyniku kursora:
var result = {};
db.test.find({
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
if (doc.id == 100) {
result = doc;
result.childs = []
} else {
result.childs.push(doc)
}
})
printjson(result);
To robi dokładnie to samo:
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
i służy jako dowód, że wszystko, co naprawdę trzeba zrobić tutaj jest problem, który „single” query wybrać zarówno rodziców i dzieci. Zwrócone dane są takie same, a wszystko, co robisz na serwerze lub kliencie, "masuje" do innego zebranego formatu.
Jest to jeden z tych przypadków, w których można "złapać" myślenie o tym, jak robiłeś rzeczy w "relacyjnej" bazie danych, i nie zdawać sobie sprawy, że skoro sposób przechowywania danych "zmienił się", nie musisz dłużej trzeba korzystać z tego samego podejścia.
To jest dokładnie to, co jest punktem przykładu dokumentacji "Model Tree Structures with Child References" w jego strukturze, gdzie ułatwia wybór rodziców i dzieci w ramach jednego zapytania.
Dzięki, ale kiedy patrzymy na przedstawienie. Czy zatem lepiej jest wykonać 2 pytania, jeden do pobrania dokumentu, a drugi do dziecka? –
@PhillipBartschinski Jeśli chodzi o "wydajność", to '$ lookup' skutecznie wykonuje" dwa "zapytania, ale na" serwerze ". "Klient" może wykonywać kwerendy "dwa", ale oczywiście wiąże się z nim obciążenie sieci i reakcji, które spowolni proces. Stąd dlaczego robisz '$ lookup'. Ale mówię też, że twoja sprawa tego nie potrzebuje. Wszystkie elementy są już w tej samej kolekcji, więc po prostu wybierz je i '$ group' odpowiednio. Pod względem "wydajności" jest zdecydowanie lepszą opcją. –
@PhillipBartschinski Myślę, że nadal tkwisz w "myśleniu relacyjnym", które sprawia, że myślisz "sub-zapytanie" lub "samodzielne dołączanie", kiedy w rzeczywistości nie potrzebujesz. Dodano tu czystą metodę "bez agregacji", aby zademonstrować punkt. –