MongoDB における 集計 (Aggregatioin) の 基本

0 件のコメント

今回は「MongoDB における 集計 (Aggregation) の基本」についてまとめます。 あまり細かいところは触れませんが、大まかな概略はつかめるようにまとめました。

集計手続き(Aggregation Pipeline)

一般に「集計を行う」となると「さまざまな処理を経て目的とする値を取得する」ようになっていると思います。

例えば、「今月売れた商品を売れた数順に取得する」のであれば「売上データを対象に今月の売り上げで絞り込み→商品ごとに売り上げ個数を集計→数が多い順にソート」といった流れで処理します。

これはRDBだと複数のサブクエリを作成して実現しているかと思いますが、 MongoDB だと aggregate([<stages>]) のように個別処理(ステージ)を配列にして aggregate() へ渡すことで実現します。

前述のサンプル(「今月売れた商品を売れた数順に取得する」)を実際に実行するコードと動作イメージは以下のようになります。

1
2
3
4
5
db.orderes.aggregate([
  { $match: { datetime: /2017-12/g } },
  { $group: { _id: "$item", total: { $sum: "$amount" } } }
  { $sort: { total: -1 } }
]);

MongoDB における統計処理は上記の通り「ステージ」を連結して必要な情報を集めることで実現します(上記の例だと「ステージ」は $match, $group, $sort)。 また、集計する際の各「ステージ」ではより複雑な処理を行うため、子要素に「オペレーター」と呼ばれる補助関数を使用することができます(上記の例だと「オペレーター」は $sum)。

以下では、よく使いそうな「ステージ」と「オペレーター」をピックアップして詳しく見ていきます。

ステージ

さて、上記の通り MongoDB における集計処理の要は「ステージ」にあります。 どのような「ステージ」があるかを知っておくことで様々な集計ができるようになります。

ここでは MongoDB で利用可能な ステージ のうち利用頻度が高そうなものをピックアップして紹介します。

ステージの記述方法は基本的に { <stage> : { <expression> } } というようにオブジェクトを引数に取るような形で記述します。

名前 説明 / サンプル
$count

現在のステージに含まれるドキュメント数を指定したプロパティ名へ設定します。

{ $count: "total" }
$group

指定されたキー(_id)でグループ化を行います。 通常、統計関数をあわせて利用します。

{
  $group: {
    _id: "$item",
    total: { $sum: "$amount" }
  }
}
$limit

指定された件数までとなるようドキュメントを絞り込みます。

{ $limit: 15 }
$skip

先頭から指定された件数分のドキュメントを除外します。

{ $skip: 5 }
$sort

指定されたキーでソートします。 1 は 昇順、-1 は降順。

{ $sort: { total: -1 } }
$match

指定された条件に合致するドキュメントのみとなるよう絞り込みます。

{ $match: { datetime: /2017-12/g } }
$project

返却するドキュメントを再フォーマットします。

{
  $project: {
    _id: 0, item: 1, amount: 1
  }
}

オペレーター

その名の通り各種演算です。 各ステージで行う集計処理でより複雑な処理を行いたいときに利用します。 SQL と同じく1つの言語 (MongoDB 処理用の言語) と思ってみたほうが良い気がします。

オペレーターの記述方法は基本的に { <operator> : [ <arguments> ] } というように配列で引数を取るような形で記述します。 ステージを含めた記述だと { <stage> : { <operator> : [ <arguments> ] } } という形になります。 オペレーターの引数として渡すドキュメントのプロパティはプロパティ名の接頭辞に $ つけて指定します。

関係演算子

名前 説明 / サンプル
$eq

等値。

{ $eq: [ "$amount", 250 ] }
$ne

非等値。

{ $ne: [ "$amount", 250 ] }
$gt

より大きい。

{ $gt: [ "$amount", 250 ] }
$gte

より大きいか等しい。

{ $gte: [ "$amount", 250 ] }
$lt

より小さい。

{ $lt: [ "$amount", 250 ] }
$lte

より小さいか等しい。

{ $lte: [ "$amount", 250 ] }

論理演算子

名前 説明 / サンプル
$and

論理積演算。

{ $and: [ { $gte: [ "$amount", 5 ] }, { $lt: [ "$amount", 10 ] } ] }
$or

論理和演算。

{ $or: [ { $lt: [ "$amount", 5 ] }, { $gte: [ "$amount", 10 ] } ] }
$not

論理否定演算。

{ $not: [ { $gt: [ "$qty", 250 ] } ] }

算術演算子

名前 説明 / サンプル
$add

加算。

{ $add: [ "$price", "$fee" ] }
$subtract

減算。

{ $subtract: [ "$price", "$discount" ] }
$multiply

乗算。

{ $multiply: [ "$price", "$quantity" ] }
$divide

除算。

{ $divide: [ "$hours", 8 ] }
$mod

剰余演算。

{ $mod: [ "$hours", "$tasks" ] }
$ceil

切り上げ。

{ $ceil: "$value" }
$floor

切り捨て。

{ $floor: "$value" }
$pow

指数演算。以下のサンプルは分散を求める計算。

{ $pow: [ { $stdDevPop: "$scores.score" }, 2 ] } }
$sqrt

平方根。以下のサンプルは2点間の距離を求める計算。

{
  $sqrt: {
    $add: [
      { $pow: [ { $subtract: [ "$p2.y", "$p1.y" ] }, 2 ] },
      { $pow: [ { $subtract: [ "$p2.x", "$p1.x" ] }, 2 ] }
    ]
  }
}
$abs

絶対値。以下のサンプルは2値間の大きさを求める計算。

{ $abs: { $subtract: [ "$start", "$end" ] } }

統計関数

名前 説明 / サンプル
$min

最小値。

{ $min: "$amount" }
$max

最大値。

{ $max: "$amount" }
$avg

平均値。

{ $avg: "$amount" }
$sum

合計値。

{ $sum: "$amount" }
$stdDevPop

母標準偏差。

{ $stdDevPop: "$score" }

日付

名前 説明 / サンプル
$year

年。

{ $year: "$date" }
$month

月。 1 ~ 12 。

{ $month: "$date" }
$dayOfMonth

月の中の日付。 1 ~ 31。

{ $dayOfMonth: "$date" }
$dayOfWeek

曜日を示す値。 1(日曜) ~ 7(土曜)。

{ $dayOfWeek: "$date" }
$hour

時。 0 ~ 23 。

{ $hour: "$date" }
$minute

分。 0 ~ 59 。

{ $minute: "$date" }
$second

秒。 0 ~ 60 (うるう秒) 。

{ $second: "$date" }

文字列

名前 説明 / サンプル
$strLenBytes

文字列の UTF-8 バイト長を取得します。

{ $strLenBytes: "description" }
$indexOfBytes

指定された文字列が現れる UTF-8 バイトインデックスを取得します。 見つからなかった場合は -1 が戻ります。

{ $indexOfBytes: [ "$item", "e" ] }
$concat

指定された文字列を結合します。

{ $concat: [ "$item", " - ", "$description" ] }
$substrBytes

文字列の一部を取得します。 指定された UTF-8 バイトインデックスから、指定されたバイト数分取得します。

{ $substrBytes: [ "$description", 0, 2 ] }
$split

文字列を指定された区切り文字で分割します。

{ $split: ["$city", ","] }
$toLower

小文字に変換します。

{ $toLower: "$description" }
$toUpper

大文字に変換します。

{ $toUpper: "$description" }

名前 説明 / サンプル
$cond

if 文。 オブジェクトも引数としてとれます。

{ $cond: { if: { $gte: [ "$amount", 10 ] }, then: 1, else: 0 } }
{ $cond: [ { $gte: [ "$amount", 10 ] }, 1, 0 ] }
$switch

switch 文。 一連の論理式を評価して最初に true となる式を実行します。

{
  $switch: {
    branches: [
      { case: { $gt: [ "$amount", 0 ] }, then: "> 0" },
      { case: { $gt: [ "$amount", 5 ] }, then: "> 5" }
    ],
    default: "Did not match"
  }
}

今回は「MongoDB における 集計 (Aggregation) の基本」についてまとめました。 ポイントは「ステージ」と「オペレーター」にあります。 これらをどれだけ知っているかが MongoDB を使いこなせるかどうかにつながります。 独特な書き方ですが本記事を足掛かりに知識を深められればと思います。

本記事がお役に立っていると嬉しいです!!

参考記事

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