共有

はじめに

MongoDBは、JSONドキュメントで構成されるコレクションにデータが整理されるドキュメントベースのNoSQLデータベースです。他のデータベースと同様に、MongoDBにはユーザーがデータにアクセスするために使用できる言語があります。MongoDBの場合、この言語はMongoDBクエリ言語、略してMQLです。MQLであろうとSQLであろうと、データベースクエリは最初は単純ですが、データベースがスケーリングするにつれて、より複雑なクエリが発生します。

MongoDB Aggregation Frameworkは、これらの複雑なクエリを分解してMongoDBからドキュメントをクエリする方法です。複雑なロジックを順次操作に分離します。このガイドでは、MongoDB Aggregation Frameworkを紹介し、一般的なアグリゲーションステージについて説明し、最後に簡単なアグリゲーションパイプラインの例を挙げます。

MongoDB Aggregation Frameworkはどのように機能しますか?

MongoDBのAggregation Frameworkの目的は、ドキュメントを処理するための複数のステージで構成されるパイプラインを設計することです。コレクションのデータから始め、パイプラインの各ステージの後、目的のドキュメントとなる最終結果に近づきます。

各ステージはドキュメントに対して操作を実行します。実行できる操作はいくつかあります。例えば、ステージはデータをフィルタリングしたり、グループ化したり、さらには値を計算したりすることができます。各ステージの後、出力されたドキュメントは次のステージに渡され、すべてのステージがなくなるまで続きます。

アグリゲーションフレームワークを使用すると、いくつかの目標を達成できます。実際の操作構文を使った具体的な例を挙げますが、理論的には、書店のフィクション部門のアナリストは、ジャンルや著者に基づいて購入数をグループ化し、売り場に情報を提供するためのフレームワークを設定できます。彼らは、必要なデータが得られるまでステージを追加してクエリを繰り返すことができます。どのチームであっても、アグリゲーションパイプラインを構成することで、データからより簡単に発見できる洞察があります。

最も一般的なMongoDBアグリゲーション操作は何ですか?

この執筆時点では、MongoDBフレームワークには約38のアグリゲーションステージがあります。このガイドではすべてを詳しく説明しませんが、完全なリストは公式MongoDBドキュメントで確認できます。例のパイプラインでも使用されるいくつかのステージに焦点を当てて説明します。

  • $project : ストリーム内の各ドキュメントの形状を変更します。新しいフィールドの追加や既存のフィールドの削除などを行います。各入力ドキュメントに対し、1つのドキュメントを出力します。
  • $match : ドキュメントストリームをフィルタリングし、一致するドキュメントのみを次のパイプラインステージにそのまま渡します。$match は標準のMongoDBクエリを使用します。各入力ドキュメントに対し、1つのドキュメント(一致する場合)または0個のドキュメント(一致しない場合)を出力します。
  • $group : 入力ドキュメントを、指定された識別子式でグループ化し、指定されていれば、各グループにアキュムレータ式を適用します。すべての入力ドキュメントを消費し、各異なるグループごとに1つのドキュメントを出力します。出力ドキュメントには、識別子フィールドと、指定されていれば累積フィールドのみが含まれます。
  • $sort : 指定されたソートキーによってドキュメントストリームの順序を並べ替えます。順序のみが変更され、ドキュメントは変更されません。各入力ドキュメントに対し、1つのドキュメントを出力します。
  • $skip : 最初のn個のドキュメントをスキップし(nは指定されたスキップ数)、残りのドキュメントをパイプラインにそのまま渡します。各入力ドキュメントに対し、0個のドキュメント(最初のn個のドキュメントの場合)または1つのドキュメント(最初のn個のドキュメントの後にある場合)を出力します。
  • $limit : 最初のn個のドキュメントを(nは指定された制限数)そのままパイプラインに渡します。各入力ドキュメントに対し、1つのドキュメント(最初のn個のドキュメントの場合)または0個のドキュメント(最初のn個のドキュメントの後にある場合)を出力します。
  • $unwind : 入力ドキュメントから配列フィールドを分解し、各要素に対して1つのドキュメントを出力します。各出力ドキュメントは、配列を要素値に置き換えます。各入力ドキュメントに対し、n個のドキュメントを出力します。nは配列要素の数であり、空の配列の場合は0になることがあります。

アグリゲーションパイプラインの実践

実践的な例でアグリゲーションを具体的に示すため、架空の書店でパイプラインを設定してみましょう。まず、在庫注文データから始め、この生のデータを取り込み、複数の注文を持つ著者とその本の注文数を生成するパイプラインを作成します。

まず、コレクションbookOrdersにいくつかのサンプル注文ドキュメントを挿入します。

db.bookOrders.insertMany ( [
{ _id: 0, first_name: "Fyodor", last_name: "Dostoyevsky", book_title: 'Demons', genre: 'Fiction', quantity: 10, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 1, first_name: "Fyodor", last_name: "Dostoyevsky", book_title: 'Brothers Karamosov', genre: 'Fiction', quantity: 25, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 2, first_name: "Jacques", last_name: "Derrida", book_title: 'The Politics of Friendship', genre: 'Fiction', quantity: 5, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 3, first_name: "Charles", last_name: "Dickens", book_title: 'Tale of Two Cities', genre: 'Fiction', quantity: 6, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 4, first_name: "James", last_name: "Joyce", book_title: 'Ulysses', genre: 'Fiction', quantity: 30, date: ISODate( "2021-03-13T11:19:30Z" ) },
{ _id: 5, first_name: "Henry David", last_name: "Thoreau", book_title: 'Walden', genre: 'Nonfiction', quantity: 15, date: ISODate( "2021-03-13T11:19:30Z" ) },
{ _id: 6, first_name: "Virginia", last_name: "Woolf", book_title: "A Room of One's Own", genre: 'Nonfiction',
quantity: 18, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 7, first_name: "Virginia", last_name: "Woolf", book_title: "Mr's Dalloway", genre: 'Fiction', quantity: 14, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 8, first_name: "Zadie", last_name: "Smith", book_title: 'White Teeth', genre: 'Fiction', quantity: 8, date: ISODate( "2022-10-21T11:19:30Z" ) },
{ _id: 9, first_name: "Charles", last_name: "Dickens", book_title: 'The Old Curiousity Shop', genre: 'Fiction', quantity: 6, date: ISODate( "2022-10-21T11:19:30Z" ) }
] )

コレクションにサンプルドキュメントがいくつかあるので、クエリを開始できます。アグリゲーションパイプラインはdb.<collection-name>.aggregate()メソッドで実行されます。私たちの目標は、最も多くのフィクション本の総コピーが注文された著者のリストを返すクエリを設計することです。各ステージが記述されたアグリゲーションクエリの例を以下に示します。

db.bookOrders.aggregate ( [
// Stage 1: The $match operator scans the collection for documents
matching the specified condition to pass to the next stage.
{
$match:
{
genre: "Fiction"
}
},
// Stage 2: The $project operator specifies which fields
in the matched documents should pass onto the next stage.
{
$project:
{
last_name : 1,
quantity : 1
}
},
// Stage 3: The $group operator groups the documents by the specified expression
and outputs a document for each unique grouping. The _id field specifies the distinct key to group by.
{
$group:
{
_id: "$last_name",
totalQuantity: { $sum: "$quantity" } }
},
// Stage 4: The $sort operator specifies the field(s) to sort by and the order.
-1 specifies a descending order and 1 specifies ascending order.
{
$sort:
{ totalQuantity: -1 }
}
] )

アグリゲーションクエリを実行した後、次の出力が得られます。

[
{ _id: 'Dostoyevsky', totalQuantity: 35 },
{ _id: 'Joyce', totalQuantity: 30 },
{ _id: 'Woolf', totalQuantity: 14 },
{ _id: 'Dickens', totalQuantity: 12 },
{ _id: 'Smith', totalQuantity: 8 },
{ _id: 'Derrida', totalQuantity: 5 }
]

この例は意図的に単純ですが、アグリゲーションパイプラインがどのようにして一部のクエリの複雑さを取り除くことができるかを示しています。目的の出力に到達するための各ステップは、明確なステージに細かく分割され、区画化されています。

コレクションとドキュメントのデータ構造によっては、アグリゲーションパイプラインを構築する際に考慮すべき最適化があります。さらに、このフレームワークはすべての複雑なロジックに機能するわけではありません。これはケースに依存します。

例の最初の2つのステージで、指摘すべき小さな最適化が見られます。一般的に、ほとんどのパイプラインは$matchオペレータで開始することが推奨され、これがベストプラクティスです。ただし、コレクションが非常に大きなドキュメントでいっぱいの場合、代わりに$projectオペレータから開始することをお勧めします。$projectから開始すると、パイプラインの早い段階で次のステージに渡されるフィールドの量が制限され、不要な負荷が軽減されます。

まとめ

この記事では、MongoDBのAggregation Frameworkを紹介しました。それが何であり、複雑なロジックや長文のクエリを簡素化するためのツールとしてどのように機能するかを議論しました。アグリゲーションパイプラインのステージは、ロジックを容易に追跡および操作できるブロックに分解します。

アグリゲーションパイプラインはデータアクセスを簡素化し、その仕組みを理解することが重要です。MongoDBのAggregation Frameworkは、書店の例で示した以上のことを行うために使用でき、この紹介がさらなる探求の道を開くことを願っています。

著者について
Alex Emerich

Alex Emerich

アレックスは、鳥の観察、ヒップホップを愛する典型的な読書家であり、データベースについて書くことも楽しんでいます。現在ベルリンに住んでおり、レオポルド・ブルームのように街を目的もなく歩いている姿が見られます。
© . All rights reserved.