20

created_date属性を持つドキュメントでいっぱいのコレクションがあります。これらのドキュメントを集約パイプラインを介して送信し、いくつかの作業を行いたいと思います。理想的には、インデックスを利用できるように、他の作業を行う前に$ matchを使用してそれらをフィルタリングしたいのですが、新しい$ year / $ month /$dayOfMonth演算子を使用する方法がわかりません。 $match式。

$ project操作で演算子を使用する方法のいくつかの例が浮かんでいますが、パイプラインの最初のステップとして$ projectを配置すると、インデックスにアクセスできなくなるのではないかと心配しています(MongoDBのドキュメントによるとインデックスを利用するには、最初の式は$ matchでなければなりません)。

サンプルデータ:

{
    post_body: 'This is the body of test post 1',
    created_date: ISODate('2012-09-29T05:23:41Z')
    comments: 48
}
{
    post_body: 'This is the body of test post 2',
    created_date: ISODate('2012-09-24T12:34:13Z')
    comments: 10
}
{
    post_body: 'This is the body of test post 3',
    created_date: ISODate('2012-08-16T12:34:13Z')
    comments: 10
}

これを集計パイプラインで実行して、9月に行われたすべての投稿に対する合計コメントを取得したいと思います。

{
    aggregate: 'posts',
    pipeline: [
         {$match:
             /*Can I use the $year/$month operators here to match Sept 2012?
             $year:created_date : 2012,
             $month:created_date : 9
             */
             /*or does this have to be 
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt: {$date:'2012-10-01T04:00:00Z'} }
             */
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

これは機能しますが、一致すると、より複雑なクエリのインデックスにアクセスできなくなります。

{
    aggregate: 'posts',
    pipeline: [
         {$project:
              {
                   month : {$month:'$created_date'},
                   year : {$year:'$created_date'}
              }
         },
         {$match:
              {
                   month:9,
                   year: 2012
               }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }
4

3 に答える 3

21

すでに見つけたように、ドキュメントにないフィールドで$ matchを実行することはできず(検索とまったく同じように機能します)、最初に$ projectを使用すると、インデックスを使用できなくなります。

代わりにできることは、次のように努力を組み合わせることです。

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt:  {date:'2012-10-01T04:00:00Z'} 
                  }}
             }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

上記は9月の集計のみを提供します。複数の月を集計する場合は、たとえば次のようにできます。

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  { $gte:'2012-07-01T04:00:00Z', 
                    $lt: '2012-10-01T04:00:00Z'
                  }
         },
         {$project: {
              comments: 1,
              new_created: {
                        "yr" : {"$year" : "$created_date"},
                        "mo" : {"$month" : "$created_date"}
                     }
              }
         },
         {$group:
             {_id: "$new_created",
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

そして、あなたは次のようなものを取り戻すでしょう:

{
    "result" : [
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 7
            },
            "totalComments" : 5
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 8
            },
            "totalComments" : 19
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 9
            },
            "totalComments" : 21
        }
    ],
    "ok" : 1
}
于 2012-10-02T18:04:07.410 に答える
3

すでに慣れ親しんでいる操作を含むパイプラインの構築を見てみましょう。したがって、次の段階を見ていきます。

  • match-これは、と同様のフィルタリング段階findです。
  • project
  • sort
  • skip
  • limit

この機能がクエリ言語ですでに提供されていることを考えると、なぜこれらのステージが必要なのかを自問するかもしれません。MongoDBその理由は、集計フレームワークに含まれるより複雑な分析指向の機能をサポートするためにこれらのステージが必要だからです。以下のクエリは、単純に次のようになりますfind


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, ])

この集約パイプラインにプロジェクトステージを導入しましょう。


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $project: {
    _id: 0,
    name: 1,
    founded_year: 1
  }
}])

aggregate集約フレームワークを実装するためのメソッドを使用します。集約パイプラインは、単なるドキュメントの配列です。各ドキュメントには、特定のステージオペレーターを規定する必要があります。したがって、上記の場合、 2つのステージを持つ集約パイプラインがあります。$matchステージでは、ドキュメントを1つずつステージに渡します$project

limitステージに拡張しましょう:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

これにより、一致するドキュメントが取得され、フィールドを投影する前に5つに制限されます。したがって、プロジェクションは5つのドキュメントでのみ機能します。次のようなことをするとします。


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $project: {
    _id: 0,
    name: 1
  }
}, {
  $limit: 5
}])

これにより、一致するドキュメントが取得され、それらの多数のドキュメントが投影され、最終的に5つに制限されます。したがって、プロジェクションは多数のドキュメントに取り組んでおり、最終的には5つに制限されています。これにより、ドキュメントを次のステージに渡すために絶対に必要なものに限定する必要があるという教訓が得られます。それでは、sortステージを見てみましょう。


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $sort: {
    name: 1
  }
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

これにより、すべてのドキュメントが名前で並べ替えられ、そのうち5つだけが表示されます。次のようなことをするとします。


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $limit: 5
}, {
  $sort: {
    name: 1
  }
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

これにより、最初の5つのドキュメントが取得され、並べ替えられます。skipステージを追加しましょう:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $sort: {
    name: 1
  }
}, {
  $skip: 10
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}, ])

これにより、すべてのドキュメントが並べ替えられ、最初の10個のドキュメントがスキップされて返送されます。$matchパイプラインのできるだけ早い段階でステージを含めるようにする必要があります。ステージを使用してドキュメントをフィルタリングする$matchには、の場合と同じ構文を使用してクエリドキュメント(フィルター)を作成しますfind()

于 2016-09-16T06:35:39.913 に答える
0

これを試して;

db.createCollection("so");
db.so.remove();
db.so.insert([
{
    post_body: 'This is the body of test post 1',
    created_date: ISODate('2012-09-29T05:23:41Z'),
    comments: 48
},
{
    post_body: 'This is the body of test post 2',
    created_date: ISODate('2012-09-24T12:34:13Z'),
    comments: 10
},
{
    post_body: 'This is the body of test post 3',
    created_date: ISODate('2012-08-16T12:34:13Z'),
    comments: 10
}
]);
//db.so.find();

db.so.ensureIndex({"created_date":1});
db.runCommand({
    aggregate:"so",
    pipeline:[
        {
            $match: { // filter only those posts in september
                created_date: { $gte: ISODate('2012-09-01'), $lt: ISODate('2012-10-01') }
            }
        },
        {
            $group: {
                _id: null, // no shared key
                comments: { $sum: "$comments" } // total comments for all the posts in the pipeline
            }
        },
]
//,explain:true
});

結果は;

{ "result" : [ { "_id" : null, "comments" : 58 } ], "ok" : 1 }

したがって、前の例を変更してこれを行うこともできますが、パイプラインで月と年を使用して何か他のことを行う予定がない限り、なぜそうしたいかはわかりません。

{
    aggregate: 'posts',
    pipeline: [
     {$match: { created_date: { $gte: ISODate('2012-09-01'), $lt: ISODate('2012-10-01') } } },
     {$project:
          {
               month : {$month:'$created_date'},
               year : {$year:'$created_date'}
          }
     },
     {$match:
          {
               month:9,
               year: 2012
           }
     },
     {$group:
         {_id: '0',
          totalComments:{$sum:'$comments'}
         }
      }
    ]
 }
于 2012-10-02T17:29:19.770 に答える