Azure Cosmos DB の計算プロパティ (プレビュー)
適用対象: 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 句にワイルドカード プロジェクションがある場合、計算されたプロパティの代わりに永続化されたプロパティが返されます。 これは、ワイルドカード プロジェクションに計算プロパティが自動的に含まれないためです。
計算プロパティ名に対する制約は次のとおりです。
すべての計算プロパティには、一意の名前が必要です。
名前プロパティの値は、計算プロパティを参照するために使用できる最上位のプロパティ名を表します。
id
、_rid
、_ts
などの予約済みのシステム プロパティ名は、計算プロパティ名として使用できません。計算プロパティ名は、既にインデックスが作成されているプロパティ パスと一致することはできません。 これは、含まれるパス、除外されたパス、空間インデックス、複合インデックスを含む、指定されたすべてのインデックス作成パスに適用されます。
クエリの制約
計算プロパティ定義内のクエリは、構文的にも意味的にも有効である必要があります。そうしないと、作成または更新操作は失敗します。 クエリは、コンテナー内のすべての項目の決定論的な値と評価される必要があります。
計算プロパティ クエリ定義に対する制約は次のとおりです。
クエリでは、ルート項目参照を表す FROM 句を指定する必要があります。 サポートされている FROM 句の例は
FROM c
、FROM root c
、FROM MyContainer c
です。クエリでは、プロジェクションで VALUE 句を使用する必要があります。
クエリでは、WHERE、GROUP BY、ORDER BY、TOP、DISTINCT、OFFSET LIMIT、EXISTS、ALL、NONE のいずれの句も使用できません。
クエリにスカラー サブクエリを含めることはできません。
集計関数、空間関数、非決定論的関数、およびユーザー定義関数はサポートされていません。
計算プロパティの作成
プレビュー期間中は、.NET v3 SDK を使用して計算プロパティを作成する必要があります。 計算プロパティが作成されたら、Azure portal 内のすべての SDK と Data Explorer を含む任意の方法を使用して、それらを参照するクエリを実行できます。
SDK | サポートされているバージョン | メモ |
---|---|---|
.NET SDK v3 | >= 3.34.0-preview | 現在、計算プロパティはプレビュー パッケージ バージョンでのみ使用できます。 |
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);
.NET SDK を使用して既存のコンテナーに計算プロパティを更新する方法の例を次に示します。
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 container with changes
await container.ReplaceContainerAsync(containerProperties);
ヒント
コンテナーのプロパティを更新するたびに、古い値が上書きされます。 既存の計算済みプロパティがあるときに、新しいものを追加する場合、新しい計算済みプロパティと既存の計算済みプロパティの両方をコレクションに追加する必要があります。
クエリでの計算プロパティの使用
計算プロパティは、永続プロパティと同じ方法でクエリで参照できます。 インデックス付けされていない計算プロパティの値は、計算プロパティ定義を使用して実行時に評価されます。 計算プロパティがインデックス付けされている場合、インデックスは永続プロパティの場合と同じ方法で使用され、計算プロパティは必要に応じて評価されます。 最適なコストとパフォーマンスを得るためには計算済みプロパティにインデックスを追加することをお勧めします。
これらの例では、Data Explorer のクイックスタート製品データセットを使用しています。 クイック スタートを起動して開始し、データセットを新しいコンテナーに読み込みます。
項目の例を次に示します。
{
"id": "08225A9E-F2B3-4FA3-AB08-8C70ADD6C3C2",
"categoryId": "75BF1ACB-168D-469C-9AA3-1FD26BB4EA4C",
"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": "27B7F8D5-1009-45B8-88F5-41008A0F0393",
"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 を減らすのに最も有益であるためです。 計算プロパティにインデックスを付けると、項目の書き込み操作中に実際の値が評価され、インデックス語句が生成され、保持されます。
計算プロパティのインデックス付けには、次のようないくつかの考慮事項があります。
計算プロパティは、含まれているパス、除外されたパス、および複合インデックス パスで指定できます。
計算プロパティに空間インデックスを定義することはできません。
計算プロパティ パスの下のワイルドカード パスは、通常のプロパティの場合と同様に機能します。
インデックス付けされた計算プロパティを削除する場合は、そのプロパティのすべてのインデックスも削除する必要があります。
注意
すべての計算プロパティは項目の最上位レベルで定義されるため、パスは常に /<computed property name>
になります。
計算プロパティに 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"
}
]
]
}