シェア

はじめに

MongoDB はドキュメントベースの NoSQL データベースであり、データは JSON ドキュメントで構成されるコレクションに編成されています。他のデータベースと同様に、MongoDB にはユーザーがデータにアクセスするために使用できる言語があります。MongoDB の場合、この言語は MongoDB Query Language、または単に 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 つのドキュメント (一致) またはゼロドキュメント (不一致) のいずれかを出力します。
  • $group : 入力ドキュメントを指定された識別子式でグループ化し、指定された場合はアキュムレータ式を各グループに適用します。すべての入力ドキュメントを消費し、個別のグループごとに 1 つのドキュメントを出力します。出力ドキュメントには、識別子フィールドと、指定された場合は累積フィールドのみが含まれます。
  • $sort : ドキュメントストリームを指定されたソートキーで並べ替えます。順序のみが変更されます。ドキュメントは変更されません。入力ドキュメントごとに、1 つのドキュメントを出力します。
  • $skip : 最初の n ドキュメントをスキップします。n は指定されたスキップ数であり、残りのドキュメントを変更せずにパイプラインに渡します。入力ドキュメントごとに、ゼロドキュメント (最初の n ドキュメントの場合) または 1 つのドキュメント (最初の n ドキュメントの後) のいずれかを出力します。
  • $limit : 最初の n ドキュメントを変更せずにパイプラインに渡します。n は指定された制限です。入力ドキュメントごとに、1 つのドキュメント (最初の n ドキュメントの場合) またはゼロドキュメント (最初の n ドキュメントの後) のいずれかを出力します。
  • $unwind : 入力ドキュメントから配列フィールドを分解して、要素ごとにドキュメントを出力します。各出力ドキュメントは、配列を要素値に置き換えます。入力ドキュメントごとに、n 個のドキュメントを出力します。n は配列要素の数であり、空の配列の場合はゼロになる可能性があります。

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

アグリゲーションを実践的な例で具体化するために、架空の書店でパイプラインの設定を順を追って説明します。いくつかの在庫注文データから始め、この生データを取り込み、複数の注文がある著者と、注文された書籍のコピー数を出力するパイプラインを作成します。

まず、サンプル注文ドキュメントを 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 }
]

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

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

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

結論

この記事では、MongoDB の Aggregation Framework を紹介しました。それが何であるか、そしてそれが複雑なロジックと長々としたクエリを簡素化するためのツールになり得る方法について説明しました。アグリゲーションパイプラインのステージは、ロジックを簡単に追跡および操作できるブロックに分解します。

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

著者について
Alex Emerich

Alex Emerich

Alex は、典型的なバードウォッチング、ヒップホップ好きの読書家であり、データベースについて書くことも楽しんでいます。彼は現在ベルリンに住んでおり、レオポルド・ブルームのように目的もなく街を歩いている姿が見られます。