ここで一致を「投影」する必要があります。これは、MongoDBクエリが行うのは、「少なくとも1つの要素」を含む「ドキュメント」を探すことだけだからです。 つまり、「より大きい」 あなたが求めた状態。
したがって、「配列」のフィルタリングは、「クエリ」条件と同じではありません。
単純な「プロジェクション」は、「最初の」一致したアイテムをその条件に戻すだけです。したがって、それはおそらくあなたが望むものではありませんが、例として:
Order.find({ "articles.quantity": { "$gte": 5 } })
.select({ "articles.$": 1 })
.populate({
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
}).exec(function(err,orders) {
// populated and filtered twice
}
)
その「一種の」はあなたが望むことをします、しかし問題は本当にそれがせいぜい1つしか戻らないということになるでしょう "articles"
内の要素 配列。
これを適切に行うには、.aggregate()
が必要です。 配列の内容をフィルタリングします。理想的には、これはMongoDB3.2と$filter
を使用して行われます。 。ただし、.populate()
には特別な方法もあります。 ここ:
Order.aggregate(
[
{ "$match": { "artciles.quantity": { "$gte": 5 } } },
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$gte": [ "$$article.quantity", 5 ]
}
}
},
"__v": 1
}}
],
function(err,orders) {
Order.populate(
orders.map(function(order) { return new Order(order) }),
{
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
},
function(err,orders) {
// now it's all populated and mongoose documents
}
)
}
)
したがって、ここで行われるのは、配列の実際の「フィルタリング」が.aggregate()
内で行われることです。 ステートメントですが、もちろん、.aggregate()
の1つの側面があるため、これによる結果は「マングースドキュメント」ではなくなります。 これは、ドキュメント構造を「変更」できるということです。このため、マングースはその場合を「推定」し、「プレーンオブジェクト」を返すだけです。
$project
が表示されるので、これは実際には問題ではありません。 ステージでは、実際には、定義されたスキーマに従って、ドキュメントに存在する同じフィールドをすべて要求しています。したがって、これは単なる「プレーンオブジェクト」ですが、マングースドキュメントに「キャスト」するのに問題はありません。
ここで.map()
変換された「ドキュメント」の配列を返すため、次のステージで重要になります。
ここで、Model.populate()
を呼び出します。 その後、「マングース文書の配列」でさらに「母集団」を実行できます。
その結果、最終的にあなたが望むものになります。
3.2.xより古いバージョンのMongoDB
ここで実際に変更されるのは集約パイプラインだけです。簡潔にするために含める必要があるのはそれだけです。
MongoDB 2.6 -$map
の組み合わせで配列をフィルタリングできます および$setDifference
。結果は「セット」ですが、マングースが_id
を作成する場合は問題ありません。 デフォルトでは、すべてのサブドキュメント配列のフィールド:
[
{ "$match": { "artciles.quantity": { "$gte": 5 } } },
{ "$project": {
"orderdate": 1,
"articles": {
"$setDiffernce": [
{ "$map": {
"input": "$articles",
"as": "article",
"in": {
"$cond": [
{ "$gte": [ "$$article.price", 5 ] },
"$$article",
false
]
}
}},
[false]
]
},
"__v": 1
}}
],
それより古いリビジョンでは、$unwind
を使用する必要があります :
[
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$unwind": "$articles" },
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$group": {
"_id": "$_id",
"orderdate": { "$first": "$orderdate" },
"articles": { "$push": "$articles" },
"__v": { "$first": "$__v" }
}}
],
$ lookup Alternative
もう1つの方法は、代わりに「サーバー」ですべてを実行することです。これは$lookup
のオプションです MongoDB 3.2以降のバージョン:
Order.aggregate(
[
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$gte": [ "$$article.quantity", 5 ]
}
}
},
"__v": 1
}},
{ "$unwind": "$articles" },
{ "$lookup": {
"from": "articles",
"localField": "articles.article",
"foreignField": "_id",
"as": "articles.article"
}},
{ "$unwind": "$articles.article" },
{ "$group": {
"_id": "$_id",
"orderdate": { "$first": "$orderdate" },
"articles": { "$push": "$articles" },
"__v": { "$first": "$__v" }
}},
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$lte": [ "$$article.article.price", 500 ]
}
}
},
"__v": 1
}}
],
function(err,orders) {
}
)
これらは単なるドキュメントですが、.populate()
から得られる結果とまったく同じです。 アプローチ。そしてもちろん、本当に必要な場合は、いつでもマングースのドキュメントに「キャスト」して、すべての場合に再度「キャスト」することができます。
「最短」パス
これは実際には、「クエリ」が配列コンテンツを「フィルタリング」することを意図していないことを基本的に「受け入れる」という元のステートメントに戻ります。 .populate()
これは単なる別の「クエリ」であり、便宜上「ドキュメント」に詰め込まれているため、喜んでそうすることができます。
したがって、元のドキュメント配列内の追加の配列メンバーを削除して帯域幅の「バケットロード」を実際に保存していない場合は、.filter()
後処理コードでそれらを出します:
Order.find({ "articles.quantity": { "$gte": 5 } })
.populate({
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
}).exec(function(err,orders) {
orders = orders.filter(function(order) {
order.articles = order.articles.filter(function(article) {
return (
( article.quantity >= 5 ) &&
( article.article != null )
)
});
return order.aricles.length > 0;
})
// orders has non matching entries removed
}
)