すべてのプロダクト
Search
ドキュメントセンター

Tablestore:ネストクエリ

最終更新日:Dec 28, 2024

ネストクエリを実行して、ネストフィールドの子行のデータをクエリできます。ネストフィールドは直接クエリできません。ネストフィールドをクエリするには、NestedQuery オブジェクトでネストフィールドのパスとサブクエリを指定する必要があります。サブクエリは任意のタイプのクエリにすることができます。

前提条件

パラメータ

パラメータ

説明

path

ネストフィールドのパス。パスはツリー構造に似ています。たとえば、news.title は、news という名前のネストフィールド内の title サブフィールドを示します。

query

ネストフィールドのサブフィールドで実行するクエリ。クエリは任意のクエリタイプにすることができます。

scoreMode

フィールドに複数の値が含まれている場合にスコアを計算するために使用される値。

getTotalCount

クエリ条件を満たす行の総数を返すかどうかを指定します。このパラメータのデフォルト値は false で、クエリ条件を満たす行の総数は返されません。

このパラメータを true に設定すると、クエリのパフォーマンスが低下します。

weight

BM25 ベースのキーワード関連性スコアを計算するために、クエリ対象のフィールドに割り当てる重み。このパラメータは、全文検索シナリオで使用されます。クエリ対象のフィールドに高い重みを指定すると、そのフィールドの BM25 ベースのキーワード関連性スコアが高くなります。このパラメータの値は正の浮動小数点数です。

このパラメータは、返される行の数には影響しません。ただし、このパラメータは、クエリ結果の BM25 ベースのキーワード関連性スコアに影響します。

tableName

データテーブルの名前。

indexName

検索インデックスの名前。

columnsToGet

クエリ条件を満たす各行のすべての列を返すかどうかを指定します。このパラメータには、returnAll パラメータと columns パラメータを設定できます。

returnAll パラメータのデフォルト値は false で、すべての列が返されるわけではないことを指定します。この場合、columns パラメータを使用して、返す列を指定できます。返す列を指定しない場合は、プライマリキー列のみが返されます。

returnAll パラメータを true に設定すると、すべての列が返されます。

InnerHits

ネストフィールドのサブフィールドの設定。

  • sort: ネストフィールドの子行のソート規則。

  • offset: ネストフィールドが複数の子行で構成されている場合に返す子行の開始位置。

  • limit: ネストフィールドが複数の子行で構成されている場合に返す子行の最大数。デフォルト値: 3。

  • highlight: ネストフィールドのサブフィールドのハイライト設定。詳細については、ハイライトを参照してください。

次の例は、ネストクエリを実行する方法を示しています。

単一レベルのネストフィールドのクエリ

次のサンプルコードは、col_nested.nested_1 列の値が tablestore である行をクエリする方法の例を示しています。この例では、col_nested という名前のネスト列には、nested_1 と nested_2 のサブ列が含まれています。

private static void nestedQuery(SyncClient client) {
    SearchQuery searchQuery = new SearchQuery();
    NestedQuery nestedQuery = new NestedQuery(); // クエリタイプを NestedQuery に設定します。
    nestedQuery.setPath("col_nested"); // ネスト列のパスを指定します。
    TermQuery termQuery = new TermQuery(); // ネストクエリを実行するためのサブクエリを指定します。
    termQuery.setFieldName("col_nested.nested_1"); // 列の名前を指定します。名前には、ネスト列のパスを含める必要があります。
    termQuery.setTerm(ColumnValue.fromString("tablestore")); // 列値の照合に使用する値を指定します。
    nestedQuery.setQuery(termQuery);
    nestedQuery.setScoreMode(ScoreMode.None);
    searchQuery.setQuery(nestedQuery);
    //searchQuery.setGetTotalCount(true);// GetTotalCount パラメータを true に設定して、一致する行の総数を返します。

    SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
    // columnsToGet パラメータを設定して、返す列を指定するか、すべての列を返すように指定できます。このパラメータを設定しない場合は、プライマリキー列のみが返されます。
    //SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
    //columnsToGet.setReturnAll(true); // すべての列を返すように指定します。
    //columnsToGet.setColumns(Arrays.asList("ColName1","ColName2")); // 返す列を指定します。
    //searchRequest.setColumnsToGet(columnsToGet);

    SearchResponse resp = client.search(searchRequest);
    //System.out.println("TotalCount: " + resp.getTotalCount()); // 返される行の数ではなく、一致する行の総数を表示するように指定します。
    System.out.println("Row: " + resp.getRows());
}

複数レベルのネストフィールドのクエリ

次のサンプルコードは、col_nested.nested_2.nested_2_2 列の値が tablestore である行をクエリする方法の例を示しています。この例では、col_nested という名前のネスト列には、nested_1 と nested_2 のサブ列が含まれています。 nested_2 サブ列には、nested_2_1 列と nested_2_2 列が含まれています。

private static void nestedQuery(SyncClient client) {
    SearchQuery searchQuery = new SearchQuery();
    NestedQuery nestedQuery = new NestedQuery(); // クエリタイプを NestedQuery に設定します。
    nestedQuery.setPath("col_nested.nested_2"); // ネスト列のパスを指定します。これは、クエリ対象の列の親パスです。
    TermQuery termQuery = new TermQuery(); // ネストクエリを実行するためのサブクエリを指定します。
    termQuery.setFieldName("col_nested.nested_2.nested_2_2"); // 列の名前を指定します。名前には、ネスト列のパスを含める必要があります。
    termQuery.setTerm(ColumnValue.fromString("tablestore")); // 列値の照合に使用する値を指定します。
    nestedQuery.setQuery(termQuery);
    nestedQuery.setScoreMode(ScoreMode.None);
    searchQuery.setQuery(nestedQuery);
    //searchQuery.setGetTotalCount(true);// GetTotalCount パラメータを true に設定して、一致する行の総数を返します。

    SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
    // columnsToGet パラメータを設定して、返す列を指定するか、すべての列を返すように指定できます。このパラメータを設定しない場合は、プライマリキー列のみが返されます。
    //SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
    //columnsToGet.setReturnAll(true); // すべての列を返すように指定します。
    //columnsToGet.setColumns(Arrays.asList("ColName1","ColName2")); // 返す列を指定します。
    //searchRequest.setColumnsToGet(columnsToGet);

    SearchResponse resp = client.search(searchRequest);
    //System.out.println("TotalCount: " + resp.getTotalCount()); // 返される行の数ではなく、一致する行の総数を表示するように指定します。
    System.out.println("Row: " + resp.getRows());
}

ネストクエリとブールクエリを組み合わせる

クエリ要件

この例では、データテーブルは、String 型の col_string 列と String 型の col_nested 列で構成されています。 col_nested 列には、JSON 形式のデータが格納されます。次の表に、データテーブルのサンプル行を示します。

説明

デモをより理解しやすくするために、各行にシリアル番号が追加されています。

シリアル番号

col_string

col_nested

1

a

[{"col_keyword": "tablestore"},{"col_keyword": "searchindex","col_long": 1}]

2

b

[{"col_keyword": "tablestore","col_long": 1}]

3

c

[{"col_keyword": "searchindex"},{"col_long": 1}]

たとえば、col_nested 列に対して次のクエリ要件があるとします。

  • 同じ子行が複数のクエリ条件を満たす

    たとえば、col_keyword 列の値が "tablestore" で、col_long 列の値が空でない行をクエリするとします。 col_keyword 列と col_long 列は、col_nested 列の同じ子行に属しています。

  • 異なる子行が複数のクエリ条件を満たす

    たとえば、col_keyword 列の値が "tablestore" で、col_long 列の値が空でない行をクエリするとします。 col_keyword 列と col_long 列は、col_nested 列の同じ子行または異なる子行に属しています。

上記の要件を満たす行をクエリするには、次の手順を実行します。

  1. データテーブルの検索インデックスを作成し、検索インデックスの col_nested 列のタイプを Nested に設定します。

    col_nested 列は、Keyword 型の col_keyword サブフィールドと Long 型の col_long サブフィールドで構成されます。

  2. クエリ要件に基づいて適切なクエリ方法を選択します。

    • 同じ子行が複数のクエリ条件を満たすというクエリ要件を満たす場合は、ネストクエリで複数のブールクエリを指定できます。

    • 異なる子行が複数のクエリ条件を満たすという要件を満たす場合は、ブールクエリで複数のネストクエリを指定できます。

次のサンプルコードは、クエリ要件に基づいてデータをクエリする方法の例を示しています。クエリ要件に基づいて、対応するサンプルコードを参照してください。

同じ子行が複数のクエリ条件を満たす

次のサンプルコードは、col_nested.col_keyword 列の値が "tablestore" で、col_nested.col_long 列の値が空でない行をクエリする方法の例を示しています。 col_nested.col_keyword 列と col_nested.col_long 列は同じ子行に属しています。

データテーブルのサンプル行に基づくと、シリアル番号が 2 の行のみがクエリ条件を満たします。

public static void nestedQuery(SyncClient client) {
    // クエリ条件 1: col_nested 列の子行の col_keyword 列の値が "tablestore" である。
    TermQuery termQuery = new TermQuery();
    termQuery.setFieldName("col_nested.col_keyword");
    termQuery.setTerm(ColumnValue.fromString("tablestore"));

    // クエリ条件 2: col_nested 列の子行の col_long 列の値が空でない。
    ExistsQuery existsQuery = new ExistsQuery();
    existsQuery.setFieldName("col_nested.col_long");

    // ブールクエリの And 演算子を使用して、子行が上記の 2 つのクエリ条件を同時に満たす行をクエリします。
    List<Query> mustQueries = new ArrayList<>();
    mustQueries.add(termQuery);
    mustQueries.add(existsQuery);
    BoolQuery boolQuery = new BoolQuery();
    boolQuery.setMustQueries(mustQueries);

    // ネストクエリで複数のブールクエリを指定して、子行が上記の 2 つのクエリ条件を同時に満たす行をクエリします。
    NestedQuery nestedQuery = new NestedQuery(); // クエリタイプを NestedQuery に設定します。
    nestedQuery.setPath("col_nested"); // ネスト列のパスを指定します。これは、クエリ対象の列の親パスです。
    nestedQuery.setQuery(boolQuery);
    nestedQuery.setScoreMode(ScoreMode.None);

    SearchQuery searchQuery = new SearchQuery();
    searchQuery.setQuery(nestedQuery);

    SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
    // columnsToGet パラメータを設定して、返す列を指定するか、すべての列を返すように指定できます。このパラメータを設定しない場合は、プライマリキー列のみが返されます。
    //SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
    //columnsToGet.setReturnAll(true); // すべての列を返すように指定します。
    //columnsToGet.setColumns(Arrays.asList("ColName1","ColName2")); // 返す列を指定します。
    //searchRequest.setColumnsToGet(columnsToGet);

    SearchResponse resp = client.search(searchRequest);
    //System.out.println("TotalCount: " + resp.getTotalCount()); // 返される行の数ではなく、一致する行の総数を表示するように指定します。
    System.out.println("Row: " + resp.getRows());
}

異なる子行が複数のクエリ条件を満たす

次のサンプルコードは、col_nested.col_keyword 列の値が "tablestore" で、col_nested.col_long 列の値が空でない行をクエリする方法の例を示しています。 col_nested.col_keyword 列と col_nested.col_long 列は、同じ子行または異なる子行に属しています。

データテーブルのサンプル行に基づくと、シリアル番号が 1 と 2 の行がクエリ条件を満たします。

public static void nestedQuery(SyncClient client) {
        // クエリ条件 1: col_nested 列の子行の col_keyword 列の値が "tablestore" である。
        TermQuery termQuery = new TermQuery();
        termQuery.setFieldName("col_nested.col_keyword");
        termQuery.setTerm(ColumnValue.fromString("tablestore"));
        NestedQuery nestedTermQuery = new NestedQuery();
        nestedTermQuery.setPath("col_nested");
        nestedTermQuery.setScoreMode(ScoreMode.None);
        nestedTermQuery.setQuery(termQuery);

        // クエリ条件 2: col_nested 列の子行の col_long 列の値が空でない。
        ExistsQuery existsQuery = new ExistsQuery();
        existsQuery.setFieldName("col_nested.col_long");
        NestedQuery nestedExistsQuery = new NestedQuery();
        nestedExistsQuery.setPath("col_nested");
        nestedExistsQuery.setScoreMode(ScoreMode.None);
        nestedExistsQuery.setQuery(existsQuery);

        // ブールクエリの And 演算子を使用して、上記の 2 つのクエリ条件を満たす行をクエリします。
        List<Query> mustQueries = new ArrayList<>();
        mustQueries.add(nestedTermQuery);
        mustQueries.add(nestedExistsQuery);

        // ブールクエリで複数のネストクエリを指定して、子行がクエリ条件を満たす行をクエリします。
        BoolQuery boolQuery = new BoolQuery();
        boolQuery.setMustQueries(mustQueries);

        SearchQuery searchQuery = new SearchQuery();
        searchQuery.setQuery(boolQuery);

        SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
        // columnsToGet パラメータを設定して、返す列を指定するか、すべての列を返すように指定できます。このパラメータを設定しない場合は、プライマリキー列のみが返されます。
        //SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
        //columnsToGet.setReturnAll(true); // すべての列を返すように指定します。
        //columnsToGet.setColumns(Arrays.asList("ColName1","ColName2")); // 返す列を指定します。
        //searchRequest.setColumnsToGet(columnsToGet);

        SearchResponse resp = client.search(searchRequest);
        //System.out.println("TotalCount: " + resp.getTotalCount()); // 返される行の数ではなく、一致する行の総数を表示するように指定します。
        System.out.println("Row: " + resp.getRows());
    }

ネストクエリでハイライト機能を使用する

次のサンプルコードは、ネストクエリを使用して、Col_Nested という名前のネスト列の Level1_Col1_Nested サブ列の値が hangzhou shanghai と一致する行をクエリし、クエリ結果でクエリ文字列をハイライトする方法の例を示しています。

/**
 * ネストクエリの innerHits パラメータを使用してハイライト機能を有効にします。
 */
public static void nestedQueryWithHighlighting(SyncClient client) {
        SearchRequest searchRequest = SearchRequest.newBuilder()
                .tableName("<TABLE_NAME>")
                .indexName("<SEARCH_INDEX_NAME>")
                .returnAllColumnsFromIndex(true)
                .searchQuery(SearchQuery.newBuilder()
                        .limit(5)
                        .query(QueryBuilders.nested()
                                .path("Col_Nested")
                                .scoreMode(ScoreMode.Min)
                                .query(QueryBuilders.match("Col_Nested.Level1_Col1_Nested", "hangzhou shanghai"))
                                .innerHits(InnerHits.newBuilder()
                                        .highlight(Highlight.newBuilder()
                                                .addFieldHighlightParam("Col_Nested.Level1_Col1_Nested", HighlightParameter.newBuilder().build())
                                                .build())
                                        .build()))
                        .build())
                .build();
        SearchResponse resp = client.search(searchRequest);

        // ハイライトされた結果を表示します。
        printSearchHit(resp.getSearchHits(), "");
}

/**
 * クエリ条件を満たすコンテンツを表示します。
 * @param searchHits searchHits
 * 出力が @param prefix Nested 構造を使用する場合は、プレフィックスを追加して階層情報を表示します。
 */
private static void printSearchHit(List<SearchHit> searchHits, String prefix) {
    for (SearchHit searchHit : searchHits) {
        if (searchHit.getScore() != null) {
            System.out.printf("%s Score: %s\n", prefix, searchHit.getScore());
        }

        if (searchHit.getOffset() != null) {
            System.out.printf("%s Offset: %s\n", prefix, searchHit.getOffset());
        }

        if (searchHit.getRow() != null) {
            System.out.printf("%s Row: %s\n", prefix, searchHit.getRow().toString());
        }

        // 各行の列のハイライトされたテキストセグメントを表示します。
        if (searchHit.getHighlightResultItem() != null) {
            System.out.printf("%s Highlight: \n", prefix);
            StringBuilder strBuilder = new StringBuilder();
            for (Map.Entry<String, HighlightField> entry : searchHit.getHighlightResultItem().getHighlightFields().entrySet()) {
                strBuilder.append(entry.getKey()).append(":").append("[");
                strBuilder.append(StringUtils.join(",", entry.getValue().getFragments())).append("]\n");
            }
            System.out.printf("%s   %s", prefix, strBuilder);
        }

        // ネスト列のハイライトされた結果。
        for (SearchInnerHit searchInnerHit : searchHit.getSearchInnerHits().values()) {
            System.out.printf("%s Path: %s\n", prefix, searchInnerHit.getPath());
            System.out.printf("%s InnerHit: \n", prefix);
            printSearchHit(searchInnerHit.getSubSearchHits(), prefix + "    ");
        }

        System.out.println();
    }
}

たとえば、Col_Nested フィールドは、Text 型の Level1_Col1_Text サブフィールドと Nested 型の Level1_Col2_Nested サブフィールドで構成されています。 Nested 型の Level1_Col2_Nested サブフィールドも Level2_Col1_Text フィールドで構成されています。

次のサンプルコードは、ネストクエリにブールクエリを追加して、Level1_Col1_Text フィールドと Level1_Col2_Nested フィールドの Level2_Col1_Text サブフィールドのクエリ文字列をハイライトする方法の例を示しています。

public static void nestedQueryWithHighlighting(SyncClient client) {
    SearchRequest searchRequest = SearchRequest.newBuilder()
            .tableName("<TABLE_NAME>")
            .indexName("<SEARCH_INDEX_NAME>")
            .returnAllColumnsFromIndex(true)
            .searchQuery(SearchQuery.newBuilder()
                    .limit(5)
                    .query(QueryBuilders.nested()
                            .path("Col_Nested")
                            .scoreMode(ScoreMode.Min)
                            .query(QueryBuilders.bool()
                                    .should(QueryBuilders.match("Col_Nested.Level1_Col1_Text", "hangzhou shanghai"))
                                    .should(QueryBuilders.nested()
                                            .path("Col_Nested.Level1_Col2_Nested")
                                            .scoreMode(ScoreMode.Min)
                                            .query(QueryBuilders.match("Col_Nested.Level1_Col2_Nested.Level2_Col1_Text", "hangzhou shanghai"))
                                            .innerHits(InnerHits.newBuilder()
                                                    .highlight(Highlight.newBuilder()
                                                            .addFieldHighlightParam("Col_Nested.Level1_Col2_Nested.Level2_Col1_Text", HighlightParame
                                                            .build())
                                                    .build())))
                            .innerHits(InnerHits.newBuilder()
                                    .sort(new Sort(Arrays.asList(
                                            new ScoreSort(),
                                            new DocSort()
                                    )))
                                    .highlight(Highlight.newBuilder()
                                            .addFieldHighlightParam("Col_Nested.Level1_Col1_Text", HighlightParameter.newBuilder().build())
                                            .build())
                                    .build()))
                            .build())
            .build();
    SearchResponse resp = client.search(searchRequest);
    // ハイライトされた結果を表示します。
    printSearchHit(resp.getSearchHits(), "");
}

/**
 * クエリ条件を満たすコンテンツを表示します。
 * @param searchHits searchHits
 * 出力が @param prefix Nested 構造を使用する場合は、プレフィックスを追加して階層情報を表示します。
 */
private static void printSearchHit(List<SearchHit> searchHits, String prefix) {
    for (SearchHit searchHit : searchHits) {
        if (searchHit.getScore() != null) {
            System.out.printf("%s Score: %s\n", prefix, searchHit.getScore());
        }

        if (searchHit.getOffset() != null) {
            System.out.printf("%s Offset: %s\n", prefix, searchHit.getOffset());
        }

        if (searchHit.getRow() != null) {
            System.out.printf("%s Row: %s\n", prefix, searchHit.getRow().toString());
        }

        // 各行のフィールドのハイライトされたテキストセグメントを表示します。
        if (searchHit.getHighlightResultItem() != null) {
            System.out.printf("%s Highlight: \n", prefix);
            StringBuilder strBuilder = new StringBuilder();
            for (Map.Entry<String, HighlightField> entry : searchHit.getHighlightResultItem().getHighlightFields().entrySet()) {
                strBuilder.append(entry.getKey()).append(":").append("[");
                strBuilder.append(StringUtils.join(",", entry.getValue().getFragments())).append("]\n");
            }
            System.out.printf("%s   %s", prefix, strBuilder);
        }

        // ネスト列のハイライトされた結果。
        for (SearchInnerHit searchInnerHit : searchHit.getSearchInnerHits().values()) {
            System.out.printf("%s Path: %s\n", prefix, searchInnerHit.getPath());
            System.out.printf("%s InnerHit: \n", prefix);
            printSearchHit(searchInnerHit.getSubSearchHits(), prefix + "    ");
        }

        System.out.println();
    }
}

FAQ

参考資料