MongoDB で 日付ごと に 集計する 方法

0 件のコメント

MongoDB において 日付ごとにレコード数や総数、最小、最大、平均などを集計する方法をまとめます。

前提データ

以下のコードを利用して前提データを投入し、投入された db.figures に対して集計を行うサンプルを見ていきます。 キーとなる日付 datetimeDate型 で入れてあるものを前提に集計方法を見ていきます。

var MongoClient = require("mongodb").MongoClient;
var moment = require("moment");

var CONNECTION_URL = "mongodb://localhost:27017/test";

MongoClient.connect(CONNECTION_URL).then((db) => {
  var bulk = db.collection("figures").initializeOrderedBulkOp();

  bulk.insert({ datetime: moment("2017/10/29", "YYYY/M/D").toDate(), item: "ABC", price: 120 });
  bulk.insert({ datetime: moment("2017/10/29", "YYYY/M/D").toDate(), item: "ABC", price: 120 });
  bulk.insert({ datetime: moment("2017/10/29", "YYYY/M/D").toDate(), item: "BCD", price: 150 });
  bulk.insert({ datetime: moment("2017/10/30", "YYYY/M/D").toDate(), item: "ABC", price: 120 });
  bulk.insert({ datetime: moment("2017/10/30", "YYYY/M/D").toDate(), item: "BCD", price: 150 });
  bulk.insert({ datetime: moment("2017/11/1", "YYYY/M/D").toDate(), item: "BCD", price: 150 });
  bulk.insert({ datetime: moment("2017/11/2", "YYYY/M/D").toDate(), item: "ABC", price: 120 });
  bulk.insert({ datetime: moment("2017/11/2", "YYYY/M/D").toDate(), item: "BCD", price: 150 });
  bulk.insert({ datetime: moment("2017/11/2", "YYYY/M/D").toDate(), item: "CDE", price: 180 });
  
  bulk.execute().then((result) => {
    db.close();
  });
});

集計コード

集計には db.collection.aggregate()$group コマンドを利用します。

var MongoClient = require("mongodb").MongoClient;
var moment = require("moment");

var CONNECTION_URL = "mongodb://localhost:27017/test";

MongoClient.connect(CONNECTION_URL).then((db) => {
  db.collection("figures").aggregate([
    {
      $group: {
        _id: {
          yyyy: { $year: "$datetime" },
          mm: { $month: "$datetime" },
          dd: { $dayOfMonth: "$datetime" }
        },
        datetime: { $first: "$datetime" },
        total: { $sum: "$price" },
        min: { $min: "$price" },
        max: { $max: "$price" },
        average: { $avg: "$price" },
        count: { $sum: 1 }
      }
    }, {
      $project: {
        _id: 0, datetime: 1, total: 1, min: 1, max: 1, average: 1, count: 1
      }
    }, {
      $sort: {
        datetime: 1
      }
    }
  ], (err, results) => {
    console.log(JSON.stringify(results));
  });
});

日付ごとに集計する場合、 _id に日時を分解して入れるのがポイントになります。 グループ化する際のキーとなる日付に対して、Date型 から年月日を取り出すオペレーターは $year, $month, $dayOfMonth になります。 また、集計に利用できるコマンドとしては $sum(合計値計算), $min(最小値), $max(最大値), $avg(平均値) などがあります。 その他サブコマンドについては Group Accumulator Operators - MongoDB を参照してください。

aggregate() パイプラインの2番目の $project では 返却するドキュメントで不要な _id を削除しています。

パイプライン3番目の $sort では日付の昇順に並び替えをしています。 何も指定していないと順番は適当な順になってしまいます。

集計結果

上記「集計コード」を実行した結果は以下のようになります。

[
  {"datetime":"2017-10-28T15:00:00.000Z","total":390,"min":120,"max":150,"average":130,"count":3},
  {"datetime":"2017-10-29T15:00:00.000Z","total":270,"min":120,"max":150,"average":135,"count":2},
  {"datetime":"2017-10-31T15:00:00.000Z","total":150,"min":150,"max":150,"average":150,"count":1},
  {"datetime":"2017-11-01T15:00:00.000Z","total":450,"min":120,"max":180,"average":150,"count":3}
]

必要な集計結果が得られていることが確認できるかと思います。

datetime に入っている日時は要注意です。 データ投入時は「日本時間(+09:00)」で投入していますが、取り出す際は「グリニッジ標準時間(+00:00)」で取り出しているので、日時がずれたように見えます。 この時差は moment.js などを利用して日本時間に直して利用したほうがわかりやすくなると思います。

今回の記事は参考になったでしょうか。 MongoDB の aggregation はいろいろとできるようなので、今後も面白そうなネタがあれば取り上げていきたいと思います!!

最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!