MongoDB Academy · レッスン

$pushと$addToSet:グループ内での配列の構築

グループ化したドキュメントの値を配列に集め、$addToSetで重複を除去します。

レッスン 2/413 ステップ

「$pushと$addToSet:グループ内での配列の構築」はCoddyKit上の無料MongoDB Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはMongoDB Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 MongoDB Academyコースには全4レッスンが含まれています。

値を配列に収集する

ドキュメントをグループ化するとき、数値を集計するのではなく、個々のフィールド値を配列に収集したい場合があります。MongoDBには、そのためのアキュムレーターとして$pushと$addToSetがあります。どちらもグループ化したドキュメントから配列を作成しますが、重複値の扱いが異なります。これらのアキュムレーターは、非正規化された結果やグループ化された結果を生成するうえで不可欠です。

$push:すべての値を収集する

$pushは、グループ内の各ドキュメントについて、指定した式の値を配列に追加します。重複を保持するため、複数のドキュメントが同じ値を持つ場合、その値は結果の配列に複数回現れます。配列内の要素の順序はドキュメントが処理された順序に対応しますが、グループ化の前にソートしない限り、その順序は変わる可能性があります。

db.orders.aggregate([
  {
    $group: {
      _id: '$customerId',
      // Collect all product IDs ordered by this customer
      orderedProducts: { $push: '$productId' },
      orderDates: { $push: '$createdAt' }
    }
  }
])

$addToSet:一意の値を収集する

$addToSetは、値の重複を排除する点を除いて$pushと同様に動作します。一意の値はそれぞれ1回だけ配列に追加されます。結果の配列には、数学的な集合と同様に、重複する要素が含まれません。$addToSetを使用した場合、出力要素の順序は保証されないため、要素の順序に依存しないでください。グループごとに重複のない値の一覧が必要な場合に使用します。

db.logs.aggregate([
  {
    $group: {
      _id: {
        year: { $year: '$timestamp' },
        month: { $month: '$timestamp' }
      },
      // Unique users who logged in this month
      uniqueUsers: { $addToSet: '$userId' },
      // Every event type including duplicates
      allEvents: { $push: '$eventType' }
    }
  }
])

埋め込みオブジェクトを追加する

スカラー値だけでなく、サブドキュメントや計算したオブジェクト全体を$pushできます。オブジェクト式を構築すると、各ドキュメントの複数のフィールドを構造化された配列要素として収集できます。これは、各 contributing document の詳細を埋め込んだサマリーレコードを作成する場合に便利です。

db.orders.aggregate([
  {
    $group: {
      _id: '$customerId',
      orderHistory: {
        $push: {
          orderId: '$_id',
          amount: '$amount',
          status: '$status',
          date: '$createdAt'
        }
      }
    }
  }
])

$pushと$sortの組み合わせ

$push 配列の要素の順序はドキュメントの処理順序に依存するため、収集した配列の順序が重要な場合は、$group の前に $sort ステージを追加してください。たとえば、顧客グループごとに注文を時系列順で収集するには、最初に日付でソートします。ただし、$group の前にソートすると、MongoDB が利用できる多くのインデックス最適化が使えなくなるため、パフォーマンスとのトレードオフを考慮してください。

db.orders.aggregate([
  // Sort by date first so $push produces ordered arrays
  { $sort: { createdAt: 1 } },
  {
    $group: {
      _id: '$customerId',
      ordersInChronologicalOrder: {
        $push: {
          orderId: '$_id',
          date: '$createdAt',
          amount: '$amount'
        }
      }
    }
  }
])

ドキュメントサイズの制限と配列の増大

MongoDB のドキュメントには16 MB のサイズ制限があります。$group ステージで $push を使用すると、グループに多数のドキュメントが含まれている場合や、追加する各値が大きい場合に、結果のドキュメントがこの制限を超える可能性があります。これはスキーマエラーではなく、実行時エラーです。グループ化の前にデータをフィルタリングする、$push に必要なフィールドだけを投影する、または $sort と $limit を組み合わせて上位 N 件だけを追加することで対処できます。

// Safe pattern: project only needed fields before pushing
db.events.aggregate([
  { $match: { year: 2024 } },
  {
    $project: {
      userId: 1,
      eventType: 1  // exclude large 'payload' field
    }
  },
  {
    $group: {
      _id: '$userId',
      events: { $push: '$eventType' }
    }
  }
])

一意なタグの収集に $addToSet を使用する

$addToSet の代表的な用途は、グループ内のドキュメント全体から一意なタグやカテゴリを集約することです。たとえば、各企業の求人情報に含まれるスキルタグをすべて一意に収集したり、各顧客が購入した商品カテゴリをすべて一意に収集したりできます。重複排除はサーバー側だけで行われるため、アプリケーション側でフィルタリングする必要はありません。

db.jobPostings.aggregate([
  {
    $group: {
      _id: '$companyId',
      uniqueSkills: { $addToSet: '$requiredSkills' },
      totalPostings: { $sum: 1 }
    }
  },
  { $sort: { totalPostings: -1 } }
])

$project で $size を使って配列のサイズを確認する

$push または $addToSet で値を収集した後、配列にいくつの要素が入ったかを確認したくなることがよくあります。後続の $project または $addFields ステージで $size を使用すると、配列の長さを計算できます。また、$expr と $size を指定した $match を使用して、配列のサイズでグループをフィルタリングすることもできます。

db.orders.aggregate([
  {
    $group: {
      _id: '$customerId',
      products: { $addToSet: '$productId' }
    }
  },
  {
    $addFields: {
      uniqueProductCount: { $size: '$products' }
    }
  },
  // Only customers who bought 3 or more unique products
  { $match: { uniqueProductCount: { $gte: 3 } } }
])

グループ化後にアンワインドする

場合によっては、$push を逆にする必要があります。つまり、グループ化された配列を個々のドキュメントに展開し、さらに処理します。$unwind ステージがまさにこの処理を行います。一般的なパターンは、$push を使った $group で集約 → 変換のための $project → 展開のための $unwind → 結果を絞り込むための追加の $group または $match、という流れです。

db.orders.aggregate([
  { $group: { _id: '$customerId', products: { $push: '$productId' } } },
  // Expand back into per-product documents
  { $unwind: '$products' },
  // Now further filter or group by product
  {
    $group: {
      _id: '$products',
      customerCount: { $sum: 1 }
    }
  },
  { $sort: { customerCount: -1 } }
])

実践的なパターン: ユーザーアクティビティの概要

本番環境でよく使われるパターンの1つは、ログイベントをグループ化してユーザーアクティビティの概要ドキュメントを作成することです。$push と $addToSet を組み合わせると、すべてのイベント時刻($push による順序付き配列)、訪問したページの一意な一覧($addToSet による重複排除)、イベント総数を、1回の集約処理ですべて含むドキュメントを生成できます。

db.pageViews.aggregate([
  { $sort: { timestamp: 1 } },
  {
    $group: {
      _id: '$userId',
      visitTimestamps: { $push: '$timestamp' },
      uniquePages: { $addToSet: '$page' },
      totalVisits: { $sum: 1 }
    }
  },
  {
    $addFields: {
      uniquePageCount: { $size: '$uniquePages' }
    }
  }
])

$push と $addToSet の比較

$push と $addToSet のどちらを選ぶか迷ったら、重複を考慮する必要があるかを確認してください。繰り返しを含むすべての値が必要な場合(イベントログや購入履歴など)は $push を使用します。一意な値だけが必要な場合(一意なタグや訪問したページの一覧など)は $addToSet を使用します。$addToSet は結果の配列内の要素の順序を保証しませんが、$push はパイプラインへの入力に対する挿入順序を保持することに注意してください。

// $push — all values, order preserved
{ $push: '$tag' }  // ['mongodb', 'nosql', 'mongodb', 'database']

// $addToSet — unique values only, order not guaranteed
{ $addToSet: '$tag' }  // ['mongodb', 'database', 'nosql']

クイックチェック

このレッスンで扱った MongoDB と NoSQL Databases の概念について、理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、$push は重複を含むすべての値を配列に収集すること、$addToSet は順序が保証されない一意な値だけを収集すること、そしてどちらも複雑なサブドキュメントを追加でき、16 MB のドキュメントサイズ制限に従う必要があることを学びました。次は、グループごとに1つのドキュメントを選択するための $first、$last、$top/$bottom アキュムレーターについて学びます。

無料で開始

AI チューターと学ぶ JavaScript — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
30
レッスン
120

よくある質問

「$pushと$addToSet:グループ内での配列の構築」レッスンは無料ですか?

はい。「$pushと$addToSet:グループ内での配列の構築」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、MongoDB Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 MongoDB Academyコースには全4レッスンが含まれています。

「$pushと$addToSet:グループ内での配列の構築」で何を学びますか?

グループ化したドキュメントの値を配列に集め、$addToSetで重複を除去します。 ブラウザで直接実行するハンズオンコードでMongoDB Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

MongoDB Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのMongoDB Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「$pushと$addToSet:グループ内での配列の構築」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このMongoDB Academyレッスンでコードを書いて実行できますか?

はい。すべてのMongoDB Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. $sum、$avg、$min、$max:数値の集約
  2. $pushと$addToSet:グループ内での配列の構築
  3. $first、$last、$top/$bottom累計演算子
  4. $setWindowFieldsによるウィンドウ関数
← MongoDB Academyに戻る