MongoDB での配列サイズが 1 より大きいドキュメントのクエリ

Tahseen Tauseef 2023年6月20日
  1. サンプルデータ
  2. $size 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする
  3. $where 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする
  4. MongoDB でドット表記を使用して配列サイズが 1 より大きいドキュメントをクエリする
  5. $expr (3.6+) を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする
  6. Aggregation $facet 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする
MongoDB での配列サイズが 1 より大きいドキュメントのクエリ

配列のサイズを確認したり、サイズが特定の長さよりも大きいまたは小さい要素を見つける必要があるプロジェクトで作業する場合、$size$where$exists などの MongoDB 演算子を使用できます。

以下で説明するアプローチは、配列の長さまたは配列サイズの問題を解決するのに役立ちます。

サンプルデータ

次のデータがあるとします。 このサンプル データを使用して、MongoDB で特定の配列サイズを持つドキュメントをクエリします。

db.inventory.insertMany([

{ item: "journal", qty: 25, tags: ["blank", "reds"], book: { author:`xyz`, price:50, location:[`india`, `USA`, `nepal`]} },

{ item: "notebook", qty: 50, tags: ["reds", "blank"], book: { author:`xyz`, price:50, location:[`india`, `usa`]} },

{ item: "paper", qty: 100, tags: ["reds", "blank", "plain"], book: { author:`xyz`, price:50, location:[]}},
{ item: "planner", qty: 75, tags: ["blank", "reds"], book: { author:`xyz`, price:50, location:[`india`]} },

{ item: "postcard", qty: 45, tags: ["blue"], book:{} }
]);

$size 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする

MongoDB の配列演算子クラスは、配列を参照してドキュメントを取得するために使用される多くの演算子で構成されています。 $size はその 1つです。 $size 演算子は、特定のサイズの配列フィールドを含むドキュメントを取得するために使用されます。

配列でのみ機能し、数値をパラメーターとして受け入れます。

$size 演算子の主な機能は次のとおりです。

  1. 配列フィールドをユーザーが指定したサイズと比較することから開始し、続行します。
  2. 前の手順を満たすフィールドを含むドキュメントを取得します。

構文:

{array-field: {$size: <length-of-array>}}

この場合、array-field はドキュメント内の目的のフィールドの名前であり、length-of-array は長さに一致する任意の数値です。

MongoDB で $size 演算子を使用する方法を確認するために、いくつかの例を以下で共有します。

tags の長さが 1 の要素を検索

db.inventory.find({tags:{$size:1}})

出力:

$size 1

books.location の長さが 1 である要素を検索

db.inventory.find({books.location:{$size:1}}

出力:

$size 2

$where 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする

JavaScript 式または完全な JavaScript 関数を含む文字列をクエリ システムに提供するには、$where 演算子を使用します。 これにより柔軟性が向上しますが、データベースがコレクション内のすべてのドキュメントに対して JavaScript 式または関数を実行する必要があります。

this または obj のいずれかを使用して、JavaScript 式または関数でレコードを参照します。

構文:

{ $where: <string|JavaScript Code> }

$where 演算子の動作を示す例を以下に示します。

tags の長さが 1 の要素を検索

db.inventory.find({tags:{$where:`this.tags.length == 1`}}

出力:

$where 1

tags の長さが 1 以上の要素を検索

db.inventory.find({tags:{$where:`this.tags.length >= 1`}}

books.location の長さが 1 である要素を検索

you cannot check this with the help of $where

$where クエリ演算子は、最上位のドキュメントでのみ使用してください。 ネストされたページ内では動作しません。

MongoDB でドット表記を使用して配列サイズが 1 より大きいドキュメントをクエリする

埋め込みドキュメントの配列要素とフィールドにアクセスするために、MongoDB はドット表記を採用しています。

配列要素へのアクセス

ゼロから始まるインデックス位置で配列要素を指定またはアクセスするには、配列名をドット (.) とゼロから始まるインデックス位置で連結し、引用符で囲みます。

構文:

"<array>.<index>"

たとえば、ドキュメント内の次のフィールドについて考えてみましょう。

{
   ...
   contribs: [ "Turing machine", "Turing test", "Turingery" ],
   ...
}

contribs.2 配列の 3 番目のメンバーを識別するには、ドット表記 "contribs.2" を使用します。

tags の長さが 0 より大きい要素を検索

db.inventory.find({tags.0:{$exists:true`}}
It will look for elements with at least one tag // array with a zero-based index.

books.location の長さが 1 より大きい要素を検索

db.invantory.find({book.location.1: {$exists:true}}
// It looks for all components in whose book. There are at least two elements to a place.

$expr (3.6+) を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする

構文:

{ $expr: { <expression> } }

$tags の長さが 0 より大きいドキュメントを検索

db.invantory.find({
    $expr: {
        $gt: [{ $size: { $ifNull: ["$tags", []] } }, 0]
    }
})

$books.location の長さが 1 より大きい要素を検索

db.invantory.find({
    $expr: {
        $gt: [{ $size: { $ifNull: ["$book.location", []] } }, 1]
    }
})
// $ifNull: ["$book.location", []] this is used to avoid any error if book.location is null

Aggregation $facet 演算子を使用して、MongoDB で配列サイズが 1 より大きいドキュメントをクエリする

このオペレーターは、1つのステージで同じ入力ドキュメントのセットに対する多数の集約を処理します。 各パイプラインには、結果がドキュメントの配列として保存される出力ドキュメント内のフィールドがあります。

$facet ステージでは、1つの集計ステージ内で複数のディメンションまたはファセットにわたってデータを特徴付ける多面的な集計を作成できます。 多面的な集計は、データの参照と分析を支援する複数のフィルターと分類を提供します。

たとえば、小売業者はファセットを使用して、製品の価格、メーカー、サイズなどに基づいてフィルターを作成し、検索結果を絞り込むことがよくあります。

入力ドキュメントは $facet ステップに 1 回だけ送信されます。 $facet を使用すると、複数回取得する必要なく、入力ドキュメントの同じセットで多数の集計を行うことができます。

構文:

{ $facet:
   {
      <outputField1>: [ <stage1>, <stage2>, ... ],
      <outputField2>: [ <stage1>, <stage2>, ... ],
      ...

   }
}

各パイプラインの出力フィールドの名前を入力します。

$facet の各サブパイプラインは、入力ドキュメントの同一のセットを受け取ります。 これらのサブパイプラインは互いに独立しており、それぞれによって生成されたドキュメント配列は、出力ドキュメントの異なるフィールドに格納されます。

同じ $facet ステージ内で、1つのサブパイプラインの出力を別のサブパイプラインの入力として利用することはできません。 $facet の後に追加のステージを追加し、さらに集計が必要な場合は、目的のサブパイプライン出力のフィールド名 outputField> を示します。

$facet ステージでのインデックスの使用

そのサブパイプラインが $match を使用する場合、または $facet がパイプラインの最初のステップである場合でも、$facet ステージとそのサブパイプラインはインデックスを使用できません。 実行中、$facet ステージは常に COLLSCAN を実行します。

次の artwork コレクションに在庫が保管されているオンライン ストアを考えてみましょう。

{ "_id" : 1, "title" : "The Pillars of Society", "artists" : "Grosz", "year" : 1926,
  "price" : NumberDecimal("199.99"),
  "tags" : [ "painting", "satire", "Expressionism", "caricature" ] }
{ "_id" : 2, "title" : "Melancholy III", "artists" : "Munch", "year" : 1902,
  "price" : NumberDecimal("280.00"),
  "tags" : [ "woodcut", "Expressionism" ] }
{ "_id" : 3, "title" : "Dancer", "artists" : "Miro", "year" : 1925,
  "price" : NumberDecimal("76.04"),
  "tags" : [ "oil", "Surrealism", "painting" ] }
{ "_id" : 4, "title" : "The Great Wave off Kanagawa", "artists" : "Hokusai",
  "price" : NumberDecimal("167.30"),
  "tags" : [ "woodblock", "ukiyo-e" ] }
{ "_id" : 5, "title" : "The Persistence of Memory", "artist" : "Dali", "year" : 1931,
  "price" : NumberDecimal("483.00"),
  "tags" : [ "Surrealism", "painting", "oil" ] }
{ "_id" : 6, "title" : "Composition VII", "artist" : "Kandinsky", "year" : 1913,
  "price" : NumberDecimal("385.00"),
  "tags" : [ "oil", "painting", "abstract" ] }
{ "_id" : 7, "title" : "The Scream", "artist" : "Munch", "year" : 1893,
  "tags" : [ "Expressionism", "painting", "oil" ] }
{ "_id" : 8, "title" : "Blue Flower", "artist" : "O`Keefe", "year" : 1918,
  "price" : NumberDecimal("118.42"),
  "tags" : [ "abstract", "painting" ] }

次の手順では、MongoDB のファセット機能を利用して、タグ、価格、および生成された年ごとに整理されたストアの在庫を消費者に表示します。 この $facet ステージは、$sortByCount$bucket、または $bucketAuto. を使用してこの多面的な集約を実行する 3つのサブパイプラインで構成されています。

artwork からの入力ドキュメントは、操作の開始時に一度だけデータベースから取得されます。

例:

db.artwork.aggregate( [
  {
    $facet: {
      "categorizedByTags": [
        { $unwind: "$tags" },
        { $sortByCount: "$tags" }
      ],
      "categorizedByPrice": [
        // Filter out documents without a price e.g., _id: 7
        { $match: { price: { $exists: 1 } } },
        {
          $bucket: {
            groupBy: "$price",
            boundaries: [  0, 150, 200, 300, 400 ],
            default: "Other",
            output: {
              "count": { $sum: 1 },
              "titles": { $push: "$title" }
            }
          }
        }
      ],
      "categorizedByYears(Auto)": [
        {
          $bucketAuto: {
            groupBy: "$year",
            buckets: 4
          }
        }
      ]
    }
  }
])

出力:

$facet 1

関連記事 - MongoDB Query