Azure Cosmos DB for NoSQL の計算済みプロパティ

適用対象: NoSQL

Azure Cosmos DB の計算済みプロパティには、既存の項目プロパティから派生した値がありますが、プロパティは項目自体には保持されません。 計算済みプロパティのスコープは 1 つの項目で、永続プロパティであるかのようにクエリで参照できます。 計算プロパティを使用すると、複雑なクエリ ロジックを 1 回記述し、何度も参照しやすくなります。 これらのプロパティに 1 つのインデックスを追加することも、パフォーマンスを向上させるために複合インデックスの一部として使用することもできます。

注意

計算プロパティに関するフィードバックはありますか。 ご意見をお待ちしています。 ぜひ、Azure Cosmos DB エンジニアリング チーム (cosmoscomputedprops@microsoft.com) まで直接ご意見をお寄せください。

計算済みプロパティとは

計算プロパティは、項目の最上位レベルにある必要があり、入れ子になったパスを持つことができません。 各計算プロパティの定義には、名前とクエリの 2 つのコンポーネントがあります。 名前は計算プロパティ名であり、クエリは各項目のプロパティ値を計算するロジックを定義します。 計算プロパティのスコープは個々の項目であるため、複数の項目の値を使用したり、他の計算プロパティに依存したりすることはできません。 すべてのコンテナーは最大 20 個の計算済みプロパティを保持できます。

計算プロパティの定義の例:

{
  "computedProperties": [
    {
      "name": "cp_lowerName",
      "query": "SELECT VALUE LOWER(c.name) FROM c"
    }
  ]
}

名前の制限

永続化されたプロパティ名と競合しないように、計算済みプロパティに名前を付けることを強くお勧めします。 プロパティ名が重複しないようにするには、すべての計算プロパティ名にプレフィックスまたはサフィックスを追加します。 この記事では、すべての名前定義でプレフィックス cp_ を使用します。

重要

永続プロパティと同じ名前を使用して計算済みプロパティを定義してもエラーにはなりませんが、予期しない動作が発生する可能性があります。 計算されたプロパティがインデックス付きかどうかに関係なく、計算されたプロパティと名前を共有する永続化されたプロパティの値はインデックスに含まれません。 クエリでは常に、永続化されたプロパティではなく計算されたプロパティが使用されます。例外として、SELECT 句にワイルドカード プロジェクションがある場合、計算されたプロパティの代わりに永続化されたプロパティが返されます。 ワイルドカード プロジェクションに計算済みプロパティは自動的には含まれません。

計算プロパティ名に対する制約は次のとおりです。

  • すべての計算プロパティには、一意の名前が必要です。
  • name プロパティの値は、計算済みプロパティを参照するために使用できる最上位のプロパティ名を表します。
  • id_rid_ts などの予約済みのシステム プロパティ名は、計算済みプロパティ名として使用できません。
  • 計算プロパティ名は、既にインデックスが作成されているプロパティ パスと一致することはできません。 この制約は、次を含む、指定されたすべてのインデックス作成パスに適用されます。
    • 対象のパス
    • 対象外のパス
    • 空間インデックス
    • 複合インデックス

クエリの制約

計算プロパティ定義内のクエリは、構文的にも意味的にも有効である必要があります。そうしないと、作成または更新操作は失敗します。 クエリは、コンテナー内のすべての項目の決定論的な値と評価される必要があります。 一部の項目ではクエリが未定義または null に評価される場合があり、未定義または null 値を持つ計算プロパティは、クエリで使用される場合、未定義または null 値を持つ永続化されたプロパティと同じように動作します。

計算プロパティ クエリ定義に対する制限は次のとおりです。

  • クエリでは、ルート項目参照を表す FROM 句を指定する必要があります。 サポートされる FROM 句の例としては FROM cFROM root cFROM MyContainer c などがあります。
  • クエリでは、プロジェクションで VALUE 句を使用する必要があります。
  • クエリに JOIN を含めることはできません。
  • クエリでは、非決定的スカラー式を使用できません。 非決定的スカラー式の例としては、GetCurrentDateTime、GetCurrentTimeStamp、GetCurrentTicks、RAND があります。
  • クエリでは、WHERE、GROUP BY、ORDER BY、TOP、DISTINCT、OFFSET LIMIT、EXISTS、ALL、LAST、FIRST、NONE のいずれの句も使用することはできません。
  • クエリにスカラー サブクエリを含めることはできません。
  • 集計関数、空間関数、非決定論的関数、ユーザー定義関数 (UDF) はサポートされていません。

計算済みプロパティを作成する

計算されたプロパティが作成されたら、Azure portal のすべてのソフトウェア開発キット (SDK) や Azure Data Explorer など、任意のメソッドを使用して、プロパティを参照するクエリを実行できます。

サポートされているバージョン メモ
.NET SDK v3 >= 3.34.0-preview 現在、計算済みプロパティはプレビュー パッケージ バージョンでのみ使用できます。
Java SDK v4 >= 4.46.0 計算プロパティは、現在プレビュー バージョンになっています。
Python SDK >= v4.5.2b5 計算プロパティは、現在プレビュー バージョンになっています。

SDK を使用して計算済みプロパティを作成する

計算済みプロパティが定義された新しいコンテナーを作成するか、既存のコンテナーに計算済みプロパティを追加できます。

.NET SDK を使用して新しいコンテナーに計算プロパティを作成する方法の例を次に示します。

ContainerProperties containerProperties = new ContainerProperties("myContainer", "/pk")
{
    ComputedProperties = new Collection<ComputedProperty>
    {
        new ComputedProperty
        {
            Name = "cp_lowerName",
            Query = "SELECT VALUE LOWER(c.name) FROM c"
        }
    }
};

Container container = await client.GetDatabase("myDatabase").CreateContainerAsync(containerProperties);

既存のコンテナーで計算プロパティを更新する方法の例を次に示します。

var container = client.GetDatabase("myDatabase").GetContainer("myContainer");

// Read the current container properties
var containerProperties = await container.ReadContainerAsync();
// Make the necessary updates to the container properties
containerProperties.Resource.ComputedProperties = new Collection<ComputedProperty>
    {
        new ComputedProperty
        {
            Name = "cp_lowerName",
            Query = "SELECT VALUE LOWER(c.name) FROM c"
        },
        new ComputedProperty
        {
            Name = "cp_upperName",
            Query = "SELECT VALUE UPPER(c.name) FROM c"
        }
    };
// Update the container with changes
await container.ReplaceContainerAsync(containerProperties);

ヒント

コンテナーのプロパティを更新するたびに、古い値が上書きされます。 既存の計算済みプロパティがあり、新しいものを追加する場合、新しい計算済みプロパティと既存の計算済みプロパティの両方をコレクションに追加する必要があります。

データ エクスプローラーを使用して計算プロパティを作成する

データ エクスプローラーを使用して、コンテナーの計算プロパティを作成できます。

  1. データ エクスプローラーで既存のコンテナーを開きます。

  2. コンテナーの Settings セクションに移動します。 次に、*Computed プロパティ サブセクションに移動します。

  3. コンテナーの計算プロパティ定義 JSON を編集します。 この例では、この JSON を使用して、-区切り記号を使用して小売製品のSKU文字列を分割する計算プロパティを定義します。

    [
      {
        "name": "cp_splitSku",
        "query": "SELECT VALUE StringSplit(p.sku, \"-\") FROM products p"
      }
    ]
    

    データ エクスプローラー インターフェイスの計算プロパティ JSON エディターのスクリーンショット。

  4. 計算されたプロパティ 保存します。

クエリで計算済みプロパティを使用する

計算済みプロパティは、永続プロパティを参照するのと同じ方法でクエリで参照できます。 インデックス付けされていない計算済みプロパティの値は、計算済みプロパティの定義を使用して実行時に評価されます。 計算済みプロパティがインデックス付けされている場合、インデックスは永続プロパティの場合と同じ方法で使用され、計算済みプロパティは必要に応じて評価されます。 最適なコストとパフォーマンスを得るために、計算済みプロパティにインデックスを追加することをお勧めします。

次の例では、Azure portal の Data Explorer で使用できるクイックスタート製品データセットを使用します。 まず最初に、[クイック スタートの開始] を選び、データセットを新しいコンテナーに読み込みます。

サンプル データ セットをデータベースとコンテナーに読み込む方法を示すスクリーンショット。

項目の例を次に示します。

{
  "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
  "categoryId": "bbbbbbbb-1111-2222-3333-cccccccccccc",
  "categoryName": "Bikes, Touring Bikes",
  "sku": "BK-T79U-50",
  "name": "Touring-1000 Blue, 50",
  "description": "The product called \"Touring-1000 Blue, 50\"",
  "price": 2384.07,
  "tags": [
    {
      "id": "cccccccc-2222-3333-4444-dddddddddddd",
      "name": "Tag-61"
    }
  ],
  "_rid": "n7AmAPTJ480GAAAAAAAAAA==",
  "_self": "dbs/n7AmAA==/colls/n7AmAPTJ480=/docs/n7AmAPTJ480GAAAAAAAAAA==/",
  "_etag": "\"01002683-0000-0800-0000-6451fb4b0000\"",
  "_attachments": "attachments/",
  "_ts": 1683094347
}

Projection

計算プロパティを射影する必要がある場合は、明示的に参照する必要があります。 SELECT * などのワイルドカード プロジェクションは、すべての永続プロパティを返しますが、計算済みプロパティは含まれません。

name プロパティを小文字に変換する計算済みプロパティ定義の例を以下に示します。

{ 
  "name": "cp_lowerName", 
  "query": "SELECT VALUE LOWER(c.name) FROM c" 
} 

その後、このプロパティをクエリに射影できます。

SELECT 
    c.cp_lowerName 
FROM 
    c

WHERE 句

計算プロパティは、永続プロパティと同様に、フィルター述語で参照できます。 フィルターで計算済みプロパティを使用する場合は、関連する単一インデックスまたは複合インデックスを追加することをお勧めします。

20% の価格割引を計算する計算済みプロパティ定義の例を以下に示します。

{ 
  "name": "cp_20PercentDiscount", 
  "query": "SELECT VALUE (c.price * 0.2) FROM c" 
} 

その後、このプロパティをフィルター処理して、割引が $50 未満の商品のみが返されるようにすることができます。

SELECT 
    c.price - c.cp_20PercentDiscount as discountedPrice, 
    c.name 
FROM 
    c 
WHERE 
    c.cp_20PercentDiscount < 50.00

GROUP BY 句

永続プロパティと同様に、計算プロパティは GROUP BY 句で参照でき、可能な場合インデックスを使用できます。 最高のパフォーマンスを得るには、関連する単一インデックスまたは複合インデックスを追加します。

次の例は、categoryName プロパティから各項目のプライマリ カテゴリを検索する計算済みプロパティ定義です。

{
  "name": "cp_primaryCategory",
  "query": "SELECT VALUE SUBSTRING(c.categoryName, 0, INDEX_OF(c.categoryName, ',')) FROM c"
}

その後、cp_primaryCategory でグループ化して、各プライマリ カテゴリの項目数を取得できます。

SELECT 
    COUNT(1), 
    c.cp_primaryCategory 
FROM 
    c 
GROUP BY 
    c.cp_primaryCategory

ヒント

計算済みプロパティを使用せずにこのクエリを実行することもできますが、計算済みプロパティを使用すると、クエリの記述が大幅に簡略化され、cp_primaryCategory をインデックス付けできるため、パフォーマンスが向上します。 SUBSTRING()INDEX_OF() の両方で、コンテナー内のすべての項目のフル スキャンが必要ですが、計算済みプロパティにインデックスを付ける場合は、代わりにインデックスからクエリ全体を処理できます。 フル スキャンに依存するのではなく、インデックスからクエリを処理する機能により、パフォーマンスが向上し、クエリの要求ユニット (RU) コストが削減されます。

ORDER BY 句

永続プロパティと同様に、計算済みプロパティは ORDER BY 句で参照でき、クエリが成功するにはインデックスを付ける必要があります。 計算済みプロパティを使用すると、複雑なロジックまたはシステム関数の結果に ORDER BY を適用できます。これにより、Azure Cosmos DB を使用するときに多くの新しいクエリ シナリオが可能になります。

次の例は、_ts 値から月を取得する計算済みプロパティ定義です。

{
  "name": "cp_monthUpdated",
  "query": "SELECT VALUE DateTimePart('m', TimestampToDateTime(c._ts*1000)) FROM c"
}

ORDER BY cp_monthUpdated を実行する前に、インデックス付けポリシーに追加する必要があります。 インデックス付けポリシーが更新されたら、計算済みプロパティで並べ替えることができます。

SELECT
    *
FROM
    c
ORDER BY
    c.cp_monthUpdated

計算済みプロパティにインデックスを付ける

計算プロパティは既定ではインデックス付けされず、インデックス付けポリシーのワイルドカード パスではカバーされません。 永続プロパティにインデックスを追加するのと同じ方法で、インデックス付けポリシーの計算プロパティに単一インデックスまたは複合インデックスを追加できます。 すべての計算プロパティに関連するインデックスを追加することをお勧めします。 これらのインデックスは、パフォーマンスの向上と要求ユニット (RU) の削減に役立つため、推奨されます。 計算プロパティにインデックスを付けると、項目の書き込み操作中に実際の値が評価され、インデックス語句が生成され、保持されます。

計算済みプロパティのインデックス付けには、次のようないくつかの考慮事項があります。

  • 計算プロパティは、インクルード パス、除外パス、複合インデックス パスで指定できます。
  • 計算されたプロパティに空間インデックスを定義することはできません
  • 計算されたプロパティ パスの下のワイルドカード パスは、通常のプロパティの場合と同様に機能します。
  • 削除されたプロパティとインデックス付きプロパティの関連するインデックスも削除する必要があります

Note

すべての計算済みプロパティは項目の最上位レベルで定義されます。 パスは常に /<computed property name> です。

ヒント

コンテナーのプロパティを更新するたびに、古い値が上書きされます。 既存の計算済みプロパティがあり、新しいものを追加する場合、新しい計算済みプロパティと既存の計算済みプロパティの両方をコレクションに追加する必要があります。

Note

インデックス付き計算プロパティの定義が変更されると、インデックスが自動的に再作成されません。 変更された計算プロパティにインデックスを付けるには、まず計算プロパティをそのインデックスからドロップする必要があります。 その後、再度インデックスを付けるのが完了したら、その計算プロパティをインデックス ポリシーに追加し直します。

計算プロパティを削除する場合は、まずその計算プロパティをインデックス ポリシーから削除する必要があります。

計算プロパティに 1 つのインデックスを追加する

cp_myComputedProperty という名前の計算済みプロパティの 1 つのインデックスを追加するには、次のようにします。

{
  "indexingMode": "consistent",
  "automatic": true,
  "includedPaths": [
    {
      "path": "/*"
    },
    {
      "path": "/cp_myComputedProperty/?"
    }
  ],
  "excludedPaths": [
    {
      "path": "/\"_etag\"/?"
    }
  ]
}

計算プロパティに複合インデックスを追加する

2 つのプロパティ (1 つは cp_myComputedProperty として計算済み、もう 1 つは myPersistedProperty として永続化済み) に複合インデックスを追加するには、次のようにします。

{
  "indexingMode": "consistent",
  "automatic": true,
  "includedPaths": [
    {
      "path": "/*"
    }
  ],
  "excludedPaths": [
    {
      "path": "/\"_etag\"/?"
    }
  ],
  "compositeIndexes": [
    [
      {
        "path": "/cp_myComputedProperty"
      },
      {
        "path": "/path/to/myPersistedProperty"
      }
    ]
  ]
}

要求ユニットの消費量を把握する

計算済みプロパティをコンテナーに追加しても、RU は消費されません。 計算済みプロパティが定義されているコンテナーでの書き込み操作では、RU がわずかに増加する可能性があります。 計算済みプロパティにインデックスを付けると、計算済みプロパティのインデックス作成と評価のコストを反映して、書き込み操作の RU が増加します。