バケット パターンでデータをグループ化する
項目一覧
バケット パターンは、長い一連のデータを個別のオブジェクトに分割します。 大規模なデータ系列を小さなグループに分離すると、クエリ アクセス パターンが改善され、アプリケーション ロジックが簡素化されます。 バケットは、単一のユーザーによって行われる株式トランザクションなど、中央エンティティに関連する同様のオブジェクトがある場合に便利です。
アプリケーションがページごとに表示する要素に基づいてデータをグループ化することで、バケット パターンをページ分割に使用できます。 このアプローチでは、MongoDB の柔軟なデータモデルを使用して、アプリケーションが必要とするデータに応じてデータを保存します。
Tip
時系列コレクションはバケット パターンを自動的に適用するため、時系列データのバケット化に関与するほとんどのアプリケーションに適しています。
このタスクについて
株式のトランザクションを追跡する次のスキーマを考慮します。 最初のスキーマではバケット パターンは使用されず、各トランザクションが個別のドキュメントに保存されます。
db.trades.insertMany( [ { "ticker" : "MDB", "customerId": 123, "type" : "buy", "quantity" : 419, "date" : ISODate("2023-10-26T15:47:03.434Z") }, { "ticker" : "MDB", "customerId": 123, "type" : "sell", "quantity" : 29, "date" : ISODate("2023-10-30T09:32:57.765Z") }, { "ticker" : "GOOG", "customerId": 456, "type" : "buy", "quantity" : 50, "date" : ISODate("2023-10-31T11:16:02.120Z") } ] )
このアプリケーションには、1 人のカスタマーが一度に行った株式トランザクションが表示され、1 ページあたり 10 件のトランザクションが表示されます。 アプリケーション ロジックを簡素化するために、バケット パターンを使用してトランザクションを 10 のグループでグループcustomerId
し 。
手順
データを customerId でグループ化
各customerId
に対して 1 つのドキュメントを含むようにスキーマを再構成します。
{ "customerId": 123, "history": [ { "type": "buy", "ticker": "MDB", "qty": 419, "date": ISODate("2023-10-26T15:47:03.434Z") }, { "type": "sell", "ticker": "MDB", "qty": 29, "date": ISODate("2023-10-30T09:32:57.765Z") } ] }, { "customerId": 456, "history": [ { "type" : "buy", "ticker" : "GOOG", "quantity" : 50, "date" : ISODate("2023-10-31T11:16:02.120Z") } ] }
バケット パターン の場合:
共通の
customerId
値を持つドキュメントは 1 つのドキュメントに凝縮され、customerId
は最上位フィールドになります。そのカスタマーのトランザクションは、
history
と呼ばれる埋め込み配列フィールドにグループ化されます。
各バケットの識別子とカウントの追加
1 db.trades.drop() 2 3 db.trades.insertMany( 4 [ 5 { 6 "_id": "123_1698349623", 7 "customerId": 123, 8 "count": 2, 9 "history": [ 10 { 11 "type": "buy", 12 "ticker": "MDB", 13 "qty": 419, 14 "date": ISODate("2023-10-26T15:47:03.434Z") 15 }, 16 { 17 "type": "sell", 18 "ticker": "MDB", 19 "qty": 29, 20 "date": ISODate("2023-10-30T09:32:57.765Z") 21 } 22 ] 23 }, 24 { 25 "_id": "456_1698765362", 26 "customerId": 456, 27 "count": 1, 28 "history": [ 29 { 30 "type" : "buy", 31 "ticker" : "GOOG", 32 "quantity" : 50, 33 "date" : ISODate("2023-10-31T11:16:02.120Z") 34 } 35 ] 36 }, 37 ] 38 )
_id
フィールド値は、 customerId
とhistory
フィールドの秒単位の最初のトランザクション時間( UNIX エポック以降)を連結したものです。
count
フィールドは、そのドキュメントのhistory
配列にある要素の数を示します。 count
フィールドは、ページ分割ロジックを実装するために使用されます。
次のステップ
バケット パターンを使用するようにスキーマを更新したら、データの読み取りと書き込みのアプリケーション ロジックを更新します。 次のセクションを参照してください。
バケット パターンを含むデータのクエリ
更新されたスキーマでは、各ドキュメントにアプリケーション内の 1 つのページのデータが含まれています。 _id
count
データを返して更新する方法を決定するには、 フィールドと フィールドを使用します。
適切なページのデータをクエリするには、正規表現クエリを使用して指定されたcustomerId
のデータを返し、 skip
を使用して正しいページのデータを返します。 _id
の正規表現クエリはデフォルトの _id インデックスを使用するため、追加のインデックスは必要ありません。
次のクエリは、カスタマー123
のトランザクションの最初のページのデータを返します。
db.trades.find( { "_id": /^123_/ } ).sort( { _id: 1 } ).limit(1)
後続のページのデータを返すには、データを表示するページより 1 つ小さいskip
値を指定します。 たとえば、ページ10のデータを表示するには、次のクエリを実行します。
db.trades.find( { "_id": /^123_/ } ).sort( { _id: 1 } ).skip(9).limit(1)
注意
上記のクエリでは結果が返されません。サンプル データには最初のページのドキュメントのみが含まれているためです。
バケット パターンを使用したデータの挿入
スキーマがバケット パターンを使用するようになりました。アプリケーション ロジックを更新して、新しいトランザクションを正しいバケットに挿入します。 アップデート コマンドを使用して、適切なcustomerId
と バケットを持つバケットにトランザクションを挿入します。
次のコマンドは、 customerId: 123
の新しいトランザクションを挿入します。
db.trades.updateOne( { "_id": /^123_/, "count": { $lt: 10 } }, { "$push": { "history": { "type": "buy", "ticker": "MSFT", "qty": 42, "date": ISODate("2023-11-02T11:43:10") } }, "$inc": { "count": 1 }, "$setOnInsert": { "_id": "123_1698939791", "customerId": 123 } }, { upsert: true } )
アプリケーションには、1 ページあたり10のトランザクションが表示されます。 アップデート フィルターは、 count
が10より小さいcustomerId: 123
のドキュメントを検索します。つまり、バケットには完全なページのデータが含まれていません。
"_id": /^123_/
に一致するドキュメントがあり、そのcount
が10より小さい場合、アップデート コマンドは新しいトランザクションを一致したドキュメントのhistory
配列にプッシュします。一致するドキュメントがない場合、アップデート コマンドは新しいトランザクションを含む新しいドキュメントを挿入します(
upsert
はtrue
であるため)。 新しいドキュメントの_id
フィールドは、customerId
とトランザクションの UNIX エポックからの時間(秒単位)を連結したものです。
アップデート コマンドのロジックは、 history
配列に10ドキュメントを超えるドキュメントが含まれないようにすることで、無制限配列を回避します。
アップデート操作の実行後、 trades
コレクションには次のドキュメントが含まれます。
[ { _id: '123_1698349623', customerId: 123, count: 3, history: [ { type: 'buy', ticker: 'MDB', qty: 419, date: ISODate("2023-10-26T15:47:03.434Z") }, { type: 'sell', ticker: 'MDB', qty: 29, date: ISODate("2023-10-30T09:32:57.765Z") }, { type: 'buy', ticker: 'MSFT', qty: 42, date: ISODate("2023-11-02T11:43:10.000Z") } ] }, { _id: '456_1698765362', customerId: 456, count: 1, history: [ { type: 'buy', ticker: 'GOOG', quantity: 50, date: ISODate("2023-10-31T11:16:02.120Z") } ] } ]
結果
バケット パターンを実装した後、アプリケーションで結果を返すためにページ分割ロジックを組み込む必要はありません。 データの保存方法は、アプリケーションでの使用方法と一致します。