MongoDB データのグループ化

MongoDBBeginner
オンラインで実践に進む

はじめに

この実験では、MongoDB におけるデータ集計の基本を学びます。集計パイプラインを使用してドキュメントをグループ化し、グループ化されたデータに対して計算を実行し、その後結果をフィルタリング、ソート、および整形することに焦点を当てます。これらの操作は、MongoDB でのデータ分析およびレポート作成に不可欠です。この実験の終わりには、$group オペレーターとその他の主要な集計ステージを組み合わせて、データから有益な洞察を抽出することに慣れているでしょう。

フィールドによるドキュメントのグループ化

データ集計の最初のステップは、共通のフィールドに基づいてドキュメントをグループ化することです。このステップでは、MongoDB シェルに接続し、新しいデータベースとコレクションを作成し、$group オペレーターを使用してカテゴリ別にドキュメントをグループ化します。

まず、ターミナルで次のコマンドを実行して MongoDB シェルを開きます。

mongosh

シェルに入ると、test> プロンプトが表示されます。salesdb という新しいデータベースに切り替え、サンプル製品データを挿入しましょう。MongoDB は、データが最初に挿入されるときにデータベースとコレクションを自動的に作成します。

次のコマンドを mongosh シェルにコピーして貼り付けます。

use salesdb
db.products.insertMany([
  { category: "Electronics", brand: "Apple", price: 1200 },
  { category: "Electronics", brand: "Samsung", price: 800 },
  { category: "Electronics", brand: "Sony", price: 950 },
  { category: "Apparel", brand: "Nike", price: 150 },
  { category: "Apparel", brand: "Adidas", price: 120 },
  { category: "Books", brand: "Penguin", price: 25 },
  { category: "Books", brand: "Penguin", price: 35 }
]);

データが準備できたので、集計を実行できます。次のコマンドは、category フィールドでドキュメントをグループ化し、$sum アキュムレータを使用して各カテゴリの合計価格を計算します。

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: { $sum: "$price" }
    }
  }
]);

出力例:

[
  { "_id": "Books", "totalPrice": 60 },
  { "_id": "Apparel", "totalPrice": 270 },
  { "_id": "Electronics", "totalPrice": 2950 }
]

集計ステージの内訳を見てみましょう。

  • db.products.aggregate([...]): これは集計を実行するために使用されるメソッドです。パイプラインを形成するステージの配列を受け取ります。
  • $group: これは入力ドキュメントをグループ化するステージオペレーターです。
  • _id: "$category": この式は、グループ化するキーを指定します。ここでは、category フィールドの値でグループ化します。$ プレフィックスはフィールドパスを示します。
  • totalPrice: { $sum: "$price" }: これはアキュムレータです。totalPrice という名前の新しいフィールドを出力ドキュメントに定義します。$sum オペレーターは、グループ内のすべてのドキュメントの price フィールドの合計を計算します。

複数のアキュムレータの使用

$group ステージでは、複数の集計を同時に計算できます。グループごとに平均値、最小値または最大値、およびアイテム数を求めることができます。このステップでは、単一の $group ステージで複数のアキュムレータを使用する方法を示します。

引き続き mongosh シェルで salesdb データベースを使用してください。

各カテゴリの合計価格、平均価格、および製品数を計算する、より複雑な集計を記述しましょう。

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: { $sum: "$price" },
      averagePrice: { $avg: "$price" },
      productCount: { $sum: 1 }
    }
  }
]);

出力例:

[
  {
    "_id": "Books",
    "totalPrice": 60,
    "averagePrice": 30,
    "productCount": 2
  },
  {
    "_id": "Apparel",
    "totalPrice": 270,
    "averagePrice": 135,
    "productCount": 2
  },
  {
    "_id": "Electronics",
    "totalPrice": 2950,
    "averagePrice": 983.3333333333334,
    "productCount": 3
  }
]

ここで使用した新しいアキュムレータは次のとおりです。

  • averagePrice: { $avg: "$price" }: $avg オペレーターは、グループ内のすべてのドキュメントの price フィールドの平均を計算します。
  • productCount: { $sum: 1 }: これは、グループ内のドキュメントをカウントするための一般的な方法です。各ドキュメントに対して 1 を合計に加算することで、実質的にドキュメントをカウントします。

グループ化されたデータのフィルタリング

データをグループ化した後、計算された値に基づいてグループをフィルタリングする必要があることがよくあります。たとえば、総売上が特定の金額を超えるカテゴリのみを表示したい場合があります。この目的には $match ステージが使用されます。これは、グループ化されたドキュメントをフィルタリングするために $group ステージの後に配置できます。

製品の合計価格が 500 より大きいカテゴリを見つけましょう。

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: { $sum: "$price" }
    }
  },
  {
    $match: {
      totalPrice: { $gt: 500 }
    }
  }
]);

出力例:

[{ "_id": "Electronics", "totalPrice": 2950 }]

このパイプラインでは次のようになります。

  1. $group ステージは、まず各カテゴリの totalPrice を計算します。
  2. $group ステージからの出力ドキュメントは、次に $match ステージに渡されます。
  3. $match ステージはこれらのドキュメントをフィルタリングし、totalPrice フィールドが 500 より大きい ($gt) ドキュメントのみを保持します。

これは、あるステージの出力が次のステージの入力となる、集計パイプラインの強力さを示しています。

グループ化されたデータのソート

グループ化およびフィルタリングされたデータが得られたら、最終ステップは多くの場合ソートです。$sort ステージを使用すると、1 つ以上のフィールドに基づいてドキュメントを昇順または降順で並べ替えることができます。

製品をカテゴリ別にグループ化し、合計価格を計算してから、結果を totalPrice で降順(高い方から低い方へ)にソートしてみましょう。

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: { $sum: "$price" }
    }
  },
  {
    $sort: {
      totalPrice: -1
    }
  }
]);

出力例:

[
  { "_id": "Electronics", "totalPrice": 2950 },
  { "_id": "Apparel", "totalPrice": 270 },
  { "_id": "Books", "totalPrice": 60 }
]

$sort ステージは、ソートするフィールドとそのソート順を指定するドキュメントを受け取ります。

  • totalPrice: -1: これは、totalPrice フィールドでドキュメントをソートします。値 -1 は降順を指定します。昇順でソートするには 1 を使用します。

複数のフィールドでソートすることもできます。たとえば、$sort: { category: 1, totalPrice: -1 } は、まずカテゴリ名でアルファベット順にソートし、次に同じ名前のカテゴリについては合計価格で降順にソートします。

$project による出力のリシェイプ

$group ステージからの出力形式が、必ずしも必要とするものと一致しない場合があります。たとえば、グループキーはデフォルトで _id という名前になります。$project ステージを使用すると、フィールドの追加、削除、または名前の変更によって出力ドキュメントを整形できます。

カテゴリ別にグループ化し、合計価格でソートしてから、カテゴリのより説明的なフィールド名を持つように出力を整形するパイプラインを構築しましょう。

db.products.aggregate([
  {
    $group: {
      _id: "$category",
      totalPrice: { $sum: "$price" }
    }
  },
  {
    $sort: {
      totalPrice: -1
    }
  },
  {
    $project: {
      _id: 0,
      category: "$_id",
      total: "$totalPrice"
    }
  }
]);

出力例:

[
  { "category": "Electronics", "total": 2950 },
  { "category": "Apparel", "total": 270 },
  { "category": "Books", "total": 60 }
]

$project ステージは次のように機能します。

  • _id: 0: これは、出力から _id フィールドを除外します。デフォルトでは、明示的に除外しない限り _id は常に含まれます。
  • category: "$_id": これは category という名前の新しいフィールドを作成し、既存の _id フィールドの値を割り当てます。
  • total: "$totalPrice": これは total という名前の新しいフィールドを作成し、totalPrice フィールドの値を割り当てます。

$project を使用することは、アプリケーションやレポートのために集計パイプラインの最終出力をフォーマットするための強力な方法です。

まとめ

この実験では、MongoDB の集計パイプラインを使用してデータをグループ化および分析する方法を学びました。まず $group オペレーターでドキュメントをグループ化し、合計を計算しました。次に、$avg$sum: 1 のような複数のアキュムレーターを使用して、より複雑な計算を実行することで、これを拡張しました。また、集計ステージを連鎖させる方法も学びました。$match を使用してグループ化された結果をフィルタリングし、$sort で並べ替え、$project で最終的な出力をクリーンで読みやすい形式に整形しました。これらは、MongoDB を扱うすべての開発者またはデータアナリストにとって基本的なスキルです。