私のコレクションに次の文書があるとします。
{
"_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"}
]
}
ただし、color=red
を含む配列でのみ文書(文書1)を取得したいのですが。
{ "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
}
Caution:この回答は、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つの興味深い方法は、 $ redact を使用することです。これは、の新しい集約機能の1つです。MongoDB 2.6。 2.6を使っているのであれば、大規模な配列があるとパフォーマンスの問題を引き起こす可能性のある$ unwindは不要です。
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$Prune"
}
}}]);
$redact
は、「文書自体に格納されている情報に基づいて文書の内容を制限します」。そのため、ドキュメント内でのみ実行されます。それは基本的にあなたのドキュメントを上から下にスキャンし、それが$cond
にあるif
条件にマッチするかどうかチェックします、マッチするならそれは内容を保持するか($$DESCEND
)または削除する($$Prune
)。
上の例では、最初の$match
はshapes
配列全体を返し、$ redactはそれを期待される結果にストリップします。
{$not:"$color"}
がトップドキュメントもスキャンするので必要であることに注意してください、そして$redact
がトップレベルでcolor
フィールドを見つけられないなら、これは我々が望まないドキュメント全体を取り除くかもしれないfalse
を返すでしょう。
$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でfindの構文は次のとおりです。
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"}
]
}
あなたが予想したようにそれは色と一致する配列からの正確なフィールドを与えるでしょう: '赤'。
$ projectとともに、他の賢明なマッチング要素が文書内の他の要素と一緒にまとめられることがより適切になるでしょう。
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})