ここでの質問は、実際には別のことに関するものであり、$lookup
は必要ありません。 まったく。しかし、純粋に「$ lookup後のフィルタリング」というタイトルからここに到着する人にとって、これらはあなたのためのテクニックです:
MongoDB3.6-サブパイプライン
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
以前-$lookup+ $ unwind +$match合体
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$unwind": "$childs" },
{ "$match": { "childs.value": "1" } },
{ "$group": {
"_id": "$_id",
"id": { "$first": "$id" },
"value": { "$first": "$value" },
"contain": { "$first": "$contain" },
"childs": { "$push": "$childs" }
}}
])
質問がある場合は、なぜ$unwind
$filter
を使用するのとは対照的に アレイで、Aggregate $ lookupを読み取ります。一致するパイプライン内のドキュメントの合計サイズが、これが一般的に必要であり、はるかに最適である理由のすべての詳細について、最大ドキュメントサイズを超えています。
MongoDB 3.6以降のリリースでは、より表現力豊かな「サブパイプライン」は、通常、配列に何かが返される前に、外部コレクションの結果を「フィルタリング」したいものです。
答えに戻ると、質問された質問に「参加しない」必要がある理由が実際に説明されています。...
オリジナル
$lookup
を使用する このように、ここでやりたいことを行うための最も「効率的な」方法ではありません。しかし、これについては後で詳しく説明します。
基本的な概念として、$filter
を使用するだけです。 結果の配列:
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" ] }
}
}
}}
]);
または、$redact
を使用します 代わりに:
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"
}
}}
]);
どちらも同じ結果になります:
{
"_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 ]
}
]
}
つまり、$lookup
それ自体は、特定のデータのみを選択するための「まだ」クエリを実行することはできません。したがって、すべての「フィルタリング」は$lookup
の後に行う必要があります。
しかし、実際には、このタイプの「自己参加」には、$lookup
を使用しない方がよいでしょう。 まったく、追加の読み取りと「ハッシュマージ」のオーバーヘッドを完全に回避します。関連アイテムと$group
を取得するだけです 代わりに:
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 ] }
}
}
}}
])
余分なフィールドを意図的に削除したので、少し違うだけです。本当に必要な場合は、自分で追加してください:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
したがって、ここでの唯一の実際の問題は、null
を「フィルタリング」することです。 現在のドキュメントがparent
のときに作成された配列の結果 $push
へのアイテムの処理 。
また、ここで欠落しているように見えるのは、探している結果に集計や「サブクエリ」がまったく必要ないということです。結論を出した、または他の場所で見つけた可能性のある構造は、1回のクエリリクエストで「ノード」とそのすべての「子」を取得できるように「設計」されています。
つまり、実際に必要なのは「クエリ」だけであり、データ収集(コンテンツが実際に「削減」されていないために行われているのはこれだけです)は、カーソルの結果を繰り返す機能にすぎません。
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);
これはまったく同じことをします:
{
"_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
]
}
]
}
そして、ここで本当に行う必要があるのは、親と子の両方を選択するための「単一」クエリを発行することだけであるという証拠として機能します。返されるデータはまったく同じであり、サーバーまたはクライアントのいずれかで行っているのは、収集された別の形式に「マッサージ」することだけです。
これは、「リレーショナル」データベースでの作業方法を考えると「巻き込まれ」、データの保存方法が「変更」されたため、使用する必要がなくなったことに気付かない場合の1つです。同じアプローチ。
これが、ドキュメントの例「子参照を使用したモデルツリー構造」のポイントです。 その構造では、1つのクエリ内で親と子を簡単に選択できます。