mongodbコレクションのオブジェクト配列のクエリ要素のみを取得する
-
10-10-2019 - |
質問
私のコレクションに次のドキュメントがあるとします。
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
クエリを行う:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
または
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
一致したドキュメントを返します (ドキュメント1), 、しかし、常にすべての配列アイテムがあります shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
ただし、ドキュメントを取得したいと思います (ドキュメント1) 含まれる配列のみ color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
これどうやってするの?
解決
Mongodb 2.2の新しい $elemMatch
投影演算子は、返されたドキュメントを変更する別の方法を提供します。 最初 一致した shapes
エレメント:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
戻り値:
{"shapes" : [{"shape": "circle", "color": "red"}]}
2.2では、これを使用してこれを行うこともできます $ projection operator
, 、 どこ $
投影オブジェクトでは、フィールド名は、クエリのフィールドの最初のマッチング配列要素のインデックスを表します。以下は、上記と同じ結果を返します。
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2アップデート
3.2リリースから始めて、新しいリリースを使用できます $filter
プロジェクション中に配列をフィルタリングする集約演算子は、含めることの利点があります すべて 最初のものだけでなく、一致します。
db.test.aggregate([
// Get just the docs that contain a shapes element where color is 'red'
{$match: {'shapes.color': 'red'}},
{$project: {
shapes: {$filter: {
input: '$shapes',
as: 'shape',
cond: {$eq: ['$$shape.color', 'red']}
}},
_id: 0
}}
])
結果:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
他のヒント
新しい 集約フレームワーク MongoDBで2.2+は、Map/Reduceの代替品を提供します。 $unwind
オペレーターを使用して分離できます shapes
一致できるドキュメントのストリームへの配列:
db.test.aggregate(
// Start with a $match pipeline which can take advantage of an index and limit documents processed
{ $match : {
"shapes.color": "red"
}},
{ $unwind : "$shapes" },
{ $match : {
"shapes.color": "red"
}}
)
結果:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
注意: この回答は、関連するソリューションを提供します その時, 、MongoDB 2.2以上の新機能が導入される前に。 Mongodbのより最近のバージョンを使用している場合は、他の回答をご覧ください。
フィールドセレクターパラメーターは、完全なプロパティに制限されています。配列の一部を選択するために使用することはできません。アレイ全体のみです。を使用してみました $ポジションオペレーター, 、しかし、それはうまくいきませんでした。
最も簡単な方法は、形状をフィルタリングすることです クライアントで.
あなたが本当に 必要 Mongodbから直接正しい出力をすることができます Map-Reduceを使用します 形状をフィルタリングします。
function map() {
filteredShapes = [];
this.shapes.forEach(function (s) {
if (s.color === "red") {
filteredShapes.push(s);
}
});
emit(this._id, { shapes: filteredShapes });
}
function reduce(key, values) {
return values[0];
}
res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })
db[res.result].find()
もう1つの取り付け方法は使用することです $編集, 、これはの新しい集約機能の1つです Mongodb 2.6. 。 2.6を使用している場合、大きな配列がある場合にパフォーマンスの問題を引き起こす可能性のある$ unvindは必要ありません。
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
「ドキュメント自体に保存されている情報に基づいて、ドキュメントの内容を制限する」. 。したがって、実行のみが実行されます ドキュメント内. 。それは基本的にあなたのドキュメントトップを下にスキャンし、それがあなたと一致するかどうかをチェックします if
にある条件 $cond
, 、一致している場合、コンテンツを保持します($$DESCEND
)または削除する($$PRUNE
).
上記の例では、最初に $match
全体を返します shapes
アレイ、および$ redactは、予想される結果まで引き下げます。
ご了承ください {$not:"$color"}
必要なのは、それが一番上のドキュメントをスキャンするので、そして $redact
見つかりません color
トップレベルのフィールドこれは戻ります false
それは私たちが望んでいないドキュメント全体を剥奪するかもしれません。
より良い方が、Array要素を使用して一致するのをクエリすることができます $slice
重要なオブジェクトを配列に戻すと役立ちますか?
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
要素のインデックスを知っている場合は役に立ちますが、時にはどちらの配列要素があなたの基準に一致しているかを望んでいる場合があります。一致する要素をで返すことができます $
オペレーター。
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
出力
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
Mongodbで発見するための構文はです
db.<collection name>.find(query, projection);
そして、あなたが書いた2番目のクエリ、それは
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
これで、あなたは使用しました $elemMatch
クエリパーツのオペレーター。一方、この演算子を投影パートで使用すると、目的の結果が得られます。クエリを書き留めることができます
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
これにより、目的の結果が得られます。
ありがとう ジョニーフク.
ここでは、もっと複雑な使用法を追加したいだけです。
// Document
{
"_id" : 1
"shapes" : [
{"shape" : "square", "color" : "red"},
{"shape" : "circle", "color" : "green"}
]
}
{
"_id" : 2
"shapes" : [
{"shape" : "square", "color" : "red"},
{"shape" : "circle", "color" : "green"}
]
}
// The Query
db.contents.find({
"_id" : ObjectId(1),
"shapes.color":"red"
},{
"_id": 0,
"shapes" :{
"$elemMatch":{
"color" : "red"
}
}
})
//And the Result
{"shapes":[
{
"shape" : "square",
"color" : "red"
}
]}
クエリを実行するだけです
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
このクエリの出力はです
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
予想通り、色と一致するアレイから正確なフィールドが得られます:「赤」。
$プロジェクトに加えて、より適切な他の賢明なマッチング要素は、ドキュメント内の他の要素と一緒にクラブされます。
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})