5

状況

特定の MongoDb クエリを実行するための良い方法を思いつくのに苦労しています。まず、これが私がやりたいクエリの種類です。電子カードのスワイプによって、入場イベントと退場イベント (および場合によっては他のアクションは重要ではない) をログに記録する単純なデータベースを想定します。したがって、次のswipelogような単純なドキュメントで呼び出されるコレクションがあります。

{
    _id: ObjectId("524ab4790a4c0e402200052c")
    name: "John Doe", 
    action: "entry",
    timestamp: ISODate("2013-10-01T1:32:12.112Z") 
}

ここで、名前とその最終エントリ時刻をリストしたいと思います (その他の必要なフィールドもありますが、以下の例ではこれら 2 つのフィールドのみを使用しています)。

現在のソリューション

MongoDb JavaScript コンソールの「ワンライナー」として、私が今持っているものを次に示します。

db.swipelog.distinct('name')
           .forEach( function(name) {

    db.swipelog.find( { name: name, action:"entry" } )
               .sort( { $natural:-1 } )
               .limit(1)
               .forEach( function(entry) {

        printjson( [ entry.name, entry.timestamp ] )
    }) 
})

次のようなものが出力されます。

[ "John Doe", ISODate("2013-10-01T1:32:12.112Z")]
[ "Jane Deo", ISODate("2013-10-01T1:36:12.112Z")]
...

質問

上記には明らかなスケーリングの問題があると思います。100 個の名前がある場合、データベースに対して 1+100 個のクエリが実行されます。timestampでは、「すべての個別の最後」を取得するための良い/正しい方法は何nameですか? データベース構造を変更したり、いくつかのコレクションを追加したりすることは、これが簡単になる場合は問題ありません。

4

2 に答える 2

16

これを達成するために集約フレームワークを使用できます。

 db.collection.aggregate(
          [
             {$match:
                  {action:'entry'}
             },
             {$group:
                  {_id:'$name',
                   first:
                         {$max:'$timestamp'}
                  }
             }
          ])

結果に他のフィールドを含める可能性が高い場合は、$first演算子を使用できます

 db.collection.aggregate(
          [
             {$match:
                  {action:'entry'}
             },
             {$sort:
                  {name:1, timestamp:-1}
             },
             {$group:
                  {_id:'$name',
                   timestamp: {$first:'$timestamp'},
                   otherField: {$first:'$otherField'},
                  }
             }
          ])
于 2013-10-01T13:22:36.537 に答える