web-dev-qa-db-ja.com

MongoDBコレクション内のオブジェクト配列内の照会された要素のみを取得します

私のコレクションに次の文書があるとします。

{  
   "_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"}
  ] 
}

これどうやってするの?

333
Sebtm

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"
            }
        ]
    }
]
362
JohnnyHK

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
}
94
Stennie

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()
29

もう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)。

上の例では、最初の$matchshapes配列全体を返し、$ redactはそれを期待される結果にストリップします。

{$not:"$color"}がトップドキュメントもスキャンするので必要であることに注意してください、そして$redactがトップレベルでcolorフィールドを見つけられないなら、これは我々が望まないドキュメント全体を取り除くかもしれないfalseを返すでしょう。

27
anvarik

$sliceを使用して、一致する配列要素を問い合わせることができれば、配列内の重要なオブジェクトを返すのに役立ちます。

db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})

$sliceは要素のインデックスを知っているときに役立ちますが、時にはどちらの配列要素があなたの基準にマッチしたがっていることを望みます。一致する要素は$演算子で返すことができます。

18
Narendran
 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

出力

{

   "shapes" : [ 
       {
           "shape" : "circle",
           "color" : "red"
       }
   ]
}
14
Viral Patel

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"}}})

これはあなたに望ましい結果を与えるでしょう。

11
Vicky

JohnnyHKに感謝します。

ここでは、もう少し複雑な使用法を追加したいと思います。

// 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"
    }
]}
7
Eddy

あなただけのクエリを実行する必要があります

db.test.find(
{"shapes.color": "red"}, 
{shapes: {$elemMatch: {color: "red"}}});

このクエリの出力は

{
    "_id" : ObjectId("562e7c594c12942f08fe4192"),
    "shapes" : [ 
        {"shape" : "circle", "color" : "red"}
    ]
}

あなたが予想したようにそれは色と一致する配列からの正確なフィールドを与えるでしょう: '赤'。

6
Vaibhav Patil

$ projectとともに、他の賢明なマッチング要素が文書内の他の要素と一緒にまとめられることがより適切になるでしょう。

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
2
shakthydoss
db.test.find( {"shapes.color": "red"}, {_id: 0})
0
Poonam Agrawal