クエリの結果を射影する

完了

中間層アプリケーションと API アプリケーションを開発する場合、データベースの結果をビジネス アプリケーションが理解して使用できるものに変換するために、非常に複雑なソリューションを構築する傾向があります。 この回避策は、多くの場合、データベース プラットフォームに柔軟性がなく、変更できない固定スキーマにデータを格納する必要がある場合に発生します。

JSON の優れた機能の 1 つは、さまざまな開発者プラットフォームと互換性があり、柔軟性が高いことです。 Azure Cosmos DB for NoSQL では、クエリの JSON 結果を操作する機能を追加することで SQL クエリ言語が拡張され、開発者チームが必要とするスキーマとシェイプにマップされるようにクエリ結果を変更できます。

例を見てみましょう。

前のユニットでは、次のクエリを実行しました。

SELECT
    p.name, 
    p.categoryName,
    p.price
FROM 
    products p
WHERE
    p.price >= 50 AND
    p.price <= 100

結果の例を次に示します。

{
    "name": "LL Bottom Bracket",
    "categoryName": "Components, Bottom Brackets",
    "price": 53.99
}

この結果は許容されますが、開発チームはこの結果をこの C# オブジェクトにマップする必要があり、このタスクを実行するために追加のコードを記述する必要はありません。

public class ProductAdvertisement
{
    public string Name { get; set; }

    public string Category { get; set; }

    public class ScannerData
    {
        public decimal Price { get; set; }
    }
}

注意

この演習の目的上、大文字と小文字の区別は無視してかまいません。 JSON パーサーは、キャメルとパスカルの大文字と小文字の間の変換を適切に処理します。

この結果は許容されますが、Python チームはこの結果をこの Python クラスにマップする必要があり、このタスクを実行するために追加のコードを記述する必要はありません。

class ProductAdvertisement:
    def __init__(self, name, category, scanner_data):
        self.name = name
        self.category = category
        self.scanner_data = scanner_data

class ScannerData:
    def __init__(self, price):
        self.price = price

この結果は許容されますが、JavaScript チームはこの結果をこの JavaScript オブジェクトにマップする必要があり、このタスクを実行するために追加のコードを記述する必要はありません。

class ProductAdvertisement {
    constructor(name, category, scannerData) {
        this.name = name;
        this.category = category;
        this.scannerData = scannerData;
    }
}

class ScannerData {
    constructor(price) {
        this.price = price;
    }
}

最初に行う可能性がある変更は、SQL エイリアスを使用して categoryName プロパティを category に変更する方法です。 この変更は、既存のクエリに AS キーワードを追加して行います。

SELECT
    p.name, 
    p.categoryName AS category,
    p.price
FROM 
    products p
WHERE
    p.price >= 50 AND
    p.price <= 100

この新しいクエリの結果は、次の JSON 出力になります。

{
    "name": "LL Bottom Bracket",
    "category": "Components, Bottom Brackets",
    "price": 53.99
}

次の変更では、JSON 出力の構造を変更する方法について考える必要があります。 クエリを変更する前に、JSON オブジェクトをどのように変更するのかについて考える必要があります。 基本的に、子 JSON オブジェクトを作成する必要があります。 この例では、scannerData のプロパティを持つ子 price オブジェクトを使用します。

{
    "name": "LL Bottom Bracket",
    "category": "Components, Bottom Brackets",
    "scannerData": {
        "price": 53.99
    }
}

この子はクエリにどのように影響しますか? p.price プロパティと scannerData のエイリアスを参照する price という名前の単一のプロパティを持つ JSON オブジェクトを定義するフィールドを作成する必要があります。 この式は次のようになります。

{ "price": p.price } AS scannerData

要するに、クエリ全体は次のようになります。

SELECT
    p.name, 
    p.categoryName AS category,
    { "price": p.price } AS scannerData
FROM 
    products p
WHERE
    p.price >= 50 AND
    p.price <= 100

クエリ結果の特定のプロパティの確認

場合によっては、特定のプロパティを掘り下げるようにクエリ結果を整形する必要があります。 これらのシナリオでは、2 つのキーワードが役立ちます。

まず、コンテナー内のすべてのカテゴリ名を検索するシナリオについて考えます。 このクエリを使用すると、すべての項目のすべてのコンテナー名を取得できます。

SELECT
    p.categoryName
FROM
    products p

このクエリは JSON 結果セットを返します

残念ながら、結果セット内には繰り返される値が含まれます。

[
    {
        "categoryName": "Components, Road Frames"
    },
    {
        "categoryName": "Components, Touring Frames"
    },
    {
        "categoryName": "Bikes, Touring Bikes"
    },
    {
        "categoryName": "Clothing, Vests"
    },
    {
        "categoryName": "Accessories, Locks"
    },
    {
        "categoryName": "Components, Pedals"
    },
    {
        "categoryName": "Components, Touring Frames"
    },
...

代わりに、DISTINCT キーワードを使用して、結果セット内の一意の値のみを返すようにできます。

SELECT DISTINCT
    p.categoryName
FROM
    products p

別のシナリオについて考えてみましょう。 .NET 開発者がこのカテゴリ名の一覧を使用する場合は、この一覧を使用するために C# ラッパー クラスを作成する必要があります。

public class CategoryReader
{
    public string CategoryName { get; set; }
}

// Developers read this as List<CategoryReader>

別のシナリオについて考えてみましょう。 Python 開発者がこのカテゴリ名の一覧を使用する場合は、次のリストを使用するために Python ラッパー クラスを作成する必要があります。

class CategoryReader:
    def __init__(self, category_name):
        self.category_name = category_name

# Developers read this as List[CategoryReader]

別のシナリオについて考えてみましょう。 JavaScript 開発者がこのカテゴリ名の一覧を使用する場合は、次のリストを使用する JavaScript ラッパー クラスを作成する必要があります。

class CategoryReader {
    constructor(categoryName) {
        this.categoryName = categoryName;
    }
}

// Developers read this as List<CategoryReader>

この追加の手順は必要ありません。 コンテナー内の複数の型に対して複数回これを行う必要があるため、すぐに面倒になる可能性があります。 ただし、1 つのプロパティのみを持つオブジェクトを返すクエリがある場合は、VALUE キーワードを使用して、単純型の配列に結果セットをフラット化できます。

SELECT DISTINCT VALUE
    p.categoryName
FROM
    products p
[
    "Components, Road Frames",
    "Components, Touring Frames",
    "Bikes, Touring Bikes",
    "Clothing, Vests",
    "Accessories, Locks",
    "Components, Pedals",
...
// Developers read this as List<string>
# Developers read this as List[str]
// Developers read this as List<string>

VALUE キーワードは、DISTINCT キーワードを使用せずに単独で使用できます。

SELECT VALUE
    p.name
FROM
    products p
[
    "LL Road Frame - Red, 60",
    "LL Touring Frame - Blue, 58",
    "Touring-1000 Yellow, 54",
    "Classic Vest, L",
    "Cable Lock",
    "ML Road Pedal",
    "LL Touring Frame - Yellow, 62",
...