442

私のコレクションに次のドキュメントがあるとします。

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

これどうやってするの?

4

15 に答える 15

484

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"
            }
        ]
    }
]
于 2012-09-03T04:19:22.347 に答える
110

MongoDB 2.2+の新しいAggregation Frameworkは、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
}
于 2012-09-03T03:44:07.393 に答える
33

注意:この回答は、MongoDB 2.2 以降の新機能が導入される前に、当時関連していた解決策を提供します。最新バージョンの MongoDB を使用している場合は、他の回答を参照してください。

フィールド セレクター パラメーターは、完全なプロパティに制限されています。配列の一部を選択するために使用することはできません。配列全体のみを選択します。$ positional operatorを使用してみましたが、うまくいきませんでした。

最も簡単な方法は、クライアントで形状をフィルタリングすることです。

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()
于 2010-10-21T09:25:09.430 に答える
31

もう 1 つの興味深い方法は、 MongoDB 2.6の新しい集約機能の 1 つである$redactを使用することです。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 「ドキュメント自体に保存されている情報に基づいてドキュメントの内容を制限する」 . そのため、ドキュメント内でのみ実行されます。基本的にドキュメントを上から下までスキャンし、 にifある条件と一致するかどうかを確認します。一致する$cond場合は、コンテンツを保持するか ( $$DESCEND) または削除 ( $$PRUNE) します。

上記の例では、最初に配列$match全体を返し、shapes$redact はそれを期待される結果に落とし込みます。

{$not:"$color"}最上位のドキュメントもスキャンするため、これが必要であることに注意してください。最上位のフィールドが$redact見つからない場合、これが返され、不要なドキュメント全体が削除される可能性があります。colorfalse

于 2014-06-04T08:31:35.973 に答える
28

$slice配列内の重要なオブジェクトを返すのに役立つので、一致する配列要素でクエリを実行する方がよいでしょう。

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

$slice要素のインデックスがわかっている場合に役立ちますが、条件に一致する配列要素が必要な場合があります。演算子を使用して、一致する要素を返すことができます$

于 2014-09-18T08:35:27.610 に答える
14

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

これにより、望ましい結果が得られます。

于 2013-12-22T08:14:14.123 に答える
9

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"
    }
]}
于 2014-03-23T07:05:06.817 に答える
8

クエリを実行するだけです

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

このクエリの出力は

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

ご想像のとおり、配列から color:'red' に一致する正確なフィールドが得られます。

于 2016-09-17T17:22:49.267 に答える
4

それに加え $projectて、ドキュメント内の他の要素と一緒に一致する要素がより適切になります。

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : { "shapes.color": "red" } },
  { 
    "$project": {
      "_id":1,
      "item":1
    }
  }
)
于 2013-02-09T15:45:13.257 に答える