web-dev-qa-db-ja.com

mongoDB配列から特定の要素を取得する

私は以下のようなmongoコレクションを持っています

{
  "auther" : "xyz" , 
  "location" : "zzz" , 
  "books" : 
    [
      {"book1" : "b1" , "date" : 2-3-00} ,
      {"book1" : "b2" , "date" : 4-9-00}
    ]
}

{
  "auther" : "pqr",
  "location" : "zzz" , 
  "books" : 
    [
      {"book1" : "b1" , "date" : 2-4-00}
    ]
}

本b1と著者xyzの日付のみを取得したい。

私は以下のようなクエリを作成しました

db.coll.find({"auther" : "xyz" , "books.book1" : "b1"} , {"books.date" : 1})

しかし、次のように出力されます

"books" : {"date" : 2-4-00} , "books" : {"date" : 4-9-00}

私は本b1と他のxyzの日付のみを取得したいです。意味のみ"books" : {"date" : 2-4-00}

mongoで可能ですか、何か間違っていますか?

25

MongoDBクエリ言語は、一致するすべてのドキュメントを返すように設計されています。

サブドキュメントのみを返すことはサポートされていません。

この問題には、MongoDBのチケットトラッカーに 未処理のチケット があります。


UPDATE:チケットが修正済みとしてマークされているようです。

これを使用する方法の例については、 here を参照してください。

21
Gates VP

Map/reduceを使用して実行でき、サブ要素を一時的なインラインコレクションに出力するだけです。そのハックとそれは動作しますが、map/reduceはシングルスレッドであり、達成したいことに対して大きなオーバーヘッドがあるため、アプリケーションのサブ要素を抽出する方がはるかに簡単です。

このようなもの...

地図:

m = function() { 
    this.books.forEach(function(book){ 
        if(book1 == 'b1'){
           emit("books", {date: book.date,});
         }
     });
}

削減:

r = function(key, values) {
      return this;
    }

クエリ:

 db.coll.mapReduce(m、r、{query:{"auther": "xyz"、 "books.book1": "b1"}、out:{inline:1}})
 
5
bm_i

一致する要素のみを選択する場合は、このようにクエリできます。

b.coll.find({"auther": "xyz"、 "books.book1": "b1"}、{"books。$。date":1})

3

少し想像して(pre mongo v 2.6)...

これは、集約またはマップの縮小を使用して実行できます。集計はより新しく、より簡単で、より最適化されています。コレクションの名前が「Authors」であると仮定して、集約されたサブ文書を返すサンプルを次に示します。綴りの自由を正しく取りました。

Authors.aggregate([
  { $match: { author: 'xyz' } },
  { $unwind: '$books' },
  { 
    $project: {  
      _id: '$books.book1',
      date: '$books.date'
    }
  },
  { $match: { '$_id' : 'b1' } } 
]);

次のような単一のエントリで配列を取得します。

[{ _id: 'b1', date: '2-4-00' }]

そうでない場合、mongo 2.6 +の場合、本当に簡単な方法を実行できます。

Authors.find({
  author: 'xyz',
  books: { $elemMatch: { book1: 'b1' } }
},'books')

書籍コレクションが見つかった場合にそれを取得し、レコードを1つだけ取得する場所:

{ _id: 'xyz', books: [ { book1: 'b1', date: '2-4-00' } ] }
2
Jason Sebring