JSONクエリDSL

JSONリクエストで提供されるクエリとフィルタは、豊富で強力なクエリDSLを使用して指定できます。

クエリDSL構造

JSONリクエストAPIは、3つの異なる形式でクエリ値を受け入れます。

  • デフォルトのdeftype(ほとんどの場合lucene)を使用する有効なクエリ文字列、例:title:solr

  • そのdeftypeを明示的に指定する有効なローカルパラメータクエリ文字列、例:{!dismax qf=title}solr

  • クエリパーサの名前と関連するパラメータをJSONオブジェクトとして指定したもの、例:{ "lucene": {"df":"title", "query":"solr"}}

    • 最上位の"query" JSONブロックは、一般的に使用するクエリパーサの名前を表すプロパティを1つだけ持ちます。クエリパーサプロパティの値は、関連するパラメータをJSONプロパティとして含む子ブロックです。"local-params"クエリ文字列と同様の構造です。クエリ自体(ローカルパラメータではvという名前で表されることが多い)は、代わりにqueryキーで指定されます。

これらの構文はすべて、JSONリクエストAPIのqueryまたはfilterプロパティのクエリを指定するために使用できます。

クエリDSLの例

以下の例は、上記で説明した各構文を使用してクエリを表す方法を示しています。各スニペットは、同じ基本的な検索を表します。フィールドname内の用語iPod

単純なクエリ文字列を使用した標準クエリAPI

curl

curl -X GET "https://127.0.0.1:8983/solr/techproducts/query?q=name:iPod"

SolrJ

final SolrQuery query = new SolrQuery("name:iPod");
final QueryResponse response = solrClient.query(COLLECTION_NAME, query);

単純なクエリ文字列を使用したJSONリクエストAPI

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query" : "name:iPod"
}'

SolrJ

final JsonQueryRequest query = new JsonQueryRequest().setQuery("name:iPod");
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

ローカルパラメータ文字列を使用したJSONリクエストAPI

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "{!lucene df=name v=iPod}"
}'

SolrJ

final JsonQueryRequest query = new JsonQueryRequest().setQuery("{!lucene df=name}iPod");
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

完全に展開されたJSONオブジェクトを使用したJSONリクエストAPI

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": {
    "lucene": {
      "df": "name",
      "query": "iPod"
    }
  }
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> luceneQueryProperties = new HashMap<>();
queryTopLevel.put("lucene", luceneQueryProperties);
luceneQueryProperties.put("df", "name");
luceneQueryProperties.put("query", "iPod");
final JsonQueryRequest query = new JsonQueryRequest().setQuery(queryTopLevel);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

ネストされたクエリ

Solrの多くのクエリパーサでは、クエリを互いにネストできます。これらを使用する場合、標準クエリAPIを使用したリクエストは、記述、読み取り、理解が難しくなります。このようなクエリは、JSONリクエストAPIの方がはるかに扱いやすくなります。

ネストされたブーストクエリの例

例として、単純なクエリ(フィールドname内の用語iPod)をブーストクエリでラップした、以下の3つのリクエストを考えてみましょう。

標準クエリAPIを使用する場合

curl

curl -X GET "https://127.0.0.1:8983/solr/techproducts/query?q={!boost b=log(popularity) v=\'{!lucene df=name}iPod\'}"

SolrJ

final SolrQuery query =
    new SolrQuery("{!boost b=log(popularity) v=\'{!lucene df=name}iPod\'}");
final QueryResponse response = solrClient.query(COLLECTION_NAME, query);

JSONリクエストAPIを使用し、完全に展開されたクエリとローカルパラメータクエリの両方を組み合わせた場合。ご覧のとおり、特別なキーvはキーqueryに置き換えられています。

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query" : {
        "boost": {
            "query": {!lucene df=name}iPod,
            "b": "log(popularity)"
        }
    }
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> boostQuery = new HashMap<>();
queryTopLevel.put("boost", boostQuery);
boostQuery.put("b", "log(popularity)");
boostQuery.put("query", "{!lucene df=name}iPod");
final JsonQueryRequest query = new JsonQueryRequest().setQuery(queryTopLevel);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

JSONリクエストAPIを使用し、すべてのクエリをJSONとして完全に展開した場合

+

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query": {
        "boost": {
            "query": {
                "lucene": {
                    "df": "name",
                    "query": "iPod"
                }
            },
            "b": "log(popularity)"
        }
    }
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> boostProperties = new HashMap<>();
final Map<String, Object> luceneTopLevel = new HashMap<>();
final Map<String, Object> luceneProperties = new HashMap<>();
queryTopLevel.put("boost", boostProperties);
boostProperties.put("b", "log(popularity)");
boostProperties.put("query", luceneTopLevel);
luceneTopLevel.put("lucene", luceneProperties);
luceneProperties.put("df", "name");
luceneProperties.put("query", "iPod");
final JsonQueryRequest query = new JsonQueryRequest().setQuery(queryTopLevel);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

ネストされたブールクエリ例

クエリネストは、BoolQParser を使用した擬似ブール論理で複数のクエリ句を組み合わせる際に一般的に見られます。

以下の例は、BoolQParserを使用して強力なネストされたクエリを作成する方法を示しています。この例では、ユーザーはフィールドnameiPodが含まれ、popularityランキングの下半分にない結果を検索します。

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query": {
        "bool": {
            "must": [
                {"lucene": {"df": "name", query: "iPod"}}
            ],
            "must_not": [
                {"frange": {"l": "0", "u": "5", "query": "popularity"}}
            ]
        }
    }
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> boolProperties = new HashMap<>();
final List<Object> mustClauses = new ArrayList<>();
final List<Object> mustNotClauses = new ArrayList<>();
final Map<String, Object> frangeTopLevel = new HashMap<>();
final Map<String, Object> frangeProperties = new HashMap<>();

queryTopLevel.put("bool", boolProperties);
boolProperties.put("must", mustClauses);
mustClauses.add("name:iPod");

boolProperties.put("must_not", mustNotClauses);
frangeTopLevel.put("frange", frangeProperties);
frangeProperties.put("l", 0);
frangeProperties.put("u", 5);
frangeProperties.put("query", "popularity");
mustNotClauses.add(frangeTopLevel);

final JsonQueryRequest query = new JsonQueryRequest().setQuery(queryTopLevel);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

luceneがデフォルトのクエリパーサーの場合、上記の例は以下のように簡略化できます。

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query": {
        "bool": {
            "must": [
                "name:iPod"
            ],
            "must_not": "{!frange l=0 u=5}popularity"
        }
    }
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> boolProperties = new HashMap<>();
queryTopLevel.put("bool", boolProperties);
boolProperties.put("must", "name:iPod");
boolProperties.put("must_not", "{!frange l=0 u=5}popularity");

final JsonQueryRequest query = new JsonQueryRequest().setQuery(queryTopLevel);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

追加のクエリタグ付け、および除外を参照する例

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "queries": {
       "query_filters":[                                            // 1.
           {"#size_tag":{"field":{"f":"size","query":"XL"}}},
           {"#color_tag":{"field":{"f":"color","query":"Red"}}}     // 2.
       ]
    },
    "query": {
        "bool": {
            "must": {"param":"query_filters"},                      // refer both of 1.
            "excludeTags": "color_tag"                              // excluding 2.
        }
    }
}'

したがって、上記のクエリはsize:XLに一致するドキュメントのみを返します。

フィルタークエリ

上記で説明した構文は、メインクエリに加えて、クエリフィルター(filterキーの下)を指定するためにも使用できます。

たとえば、上記のクエリは、フィルター句を使用して以下のように書き直すことができます。

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query": {
        "bool": {
            "must_not": "{!frange l=0 u=5}popularity"
        }
    },
    "filter: [
        "name:iPod"
    ]
}'

SolrJ

final Map<String, Object> queryTopLevel = new HashMap<>();
final Map<String, Object> boolProperties = new HashMap<>();
queryTopLevel.put("bool", boolProperties);
boolProperties.put("must_not", "{!frange l=0 u=5}popularity");

final JsonQueryRequest query =
    new JsonQueryRequest().setQuery(queryTopLevel).withFilter("name:iPod");
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

追加のクエリ

上記で説明したのと同じ構文の選択肢を使用して、queriesキーの下に複数の追加クエリを指定できます。各エントリには、配列内に複数の値を含めることができます。

これらのクエリを参照するには、{"param":"query_name"}、または旧式の参照"{!v=$query_name}"を使用します。これらの参照のアーリティに注意してください。

コンテキストによっては、参照は配列の最初の要素に解決され、それ以降の要素は無視される場合があります。たとえば、以下の参照を{"param":"electronic"}から{"param":"manufacturers"}に変更すると、それ以降のクエリを無視してmanu:appleをクエリすることと同等になります。明示的に参照されるまで、これらのクエリはクエリ結果に影響を与えません。

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "queries": {
        "electronic": {"field": {"f":"cat", "query":"electronics"}},
        "manufacturers": [
           "manu:apple",
           {"field": {"f":"manu", "query":"belkin"}}
        ]
    },
    "query":{"param":"electronic"}
}'

全体として、この例はそれほど意味がありませんが、構文を示すだけです。この機能は、JSONファセットAPIのドメインのフィルタリングドメインの変更で役立ちます。これらの宣言によってリクエストパラメータが追加されるため、他のパラメータと同じ名前を使用すると、予期しない動作が発生する可能性があります。

JSONクエリDSLでのタグ付け

クエリ句とフィルター句は、個別に「タグ付け」することもできます。タグはクエリ句のハンドルとして機能し、リクエストの他の場所から参照できるようにします。従来のファセッティングとJSONファセッティングの両方で提供されるフィルター除外機能で最も一般的に使用されます。

クエリとフィルターは、周囲のJSONオブジェクトにラップすることでタグ付けされます。タグの名前はJSONキーとして指定され、クエリ文字列(またはオブジェクト)はそのキーに関連付けられた値になります。タグ名プロパティにはハッシュが接頭辞として付けられ、コンマで区切られた複数のタグを含めることができます。例:{"#title,tag2,tag3":"title:solr"}。JSONリクエストAPIの残りの部分では緩いJSON構文解析ルールを使用しますが、先頭の#文字のため、タグは二重引用符で囲む必要があることに注意してください。以下の例は、titleTaginStockTagという2つのタグ付き句を作成します。

curl

curl -X POST https://127.0.0.1:8983/solr/techproducts/select -d '
{
  "query": "*:*",
  "filter": [
    {
      "#titleTag": "name:Solr"
    },
    {
      "#inStockTag": "inStock:true"
    }
  ]
}'

SolrJ

final Map<String, Object> titleTaggedQuery = new HashMap<>();
titleTaggedQuery.put("#titleTag", "name:Solr");
final Map<String, Object> inStockTaggedQuery = new HashMap<>();
inStockTaggedQuery.put("#inStockTag", "inStock:true");
final JsonQueryRequest query =
    new JsonQueryRequest()
        .setQuery("*:*")
        .withFilter(titleTaggedQuery)
        .withFilter(inStockTaggedQuery);
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

上記の例で作成されたタグは、検索の実行方法には影響しません。タグは、それらを使用するリクエストの他の部分によって参照されない限り、クエリに影響を与えません。

ネストされたドキュメントのファセッティング

この段落では、多くの機能をまとめています。基本的に、ファセットが子ドキュメントフィールド上でカウントされる場合のネストされたドキュメントのフィルター除外の例ですが、ファセットカウントは親ドキュメントカウントにロールアップされます。これを行う必要がある典型的なシナリオは、さまざまな色/サイズのオプション(子)を持つSKU(親)を持つ製品のフィルターを表示する場合です。項目ごとに見ていきましょう。

  • フィルター除外は、通常、複数のフィルター値を各フィールドに適用できる場合に必要です。これは、ドリルサイドウェイズファセットとしても知られています。タグ付けとフィルター除外も参照してください。

  • ネストされたドキュメント、または子ドキュメントについては、ネストされたドキュメントのインデックス作成で説明されています。以下の例では、これはこの機能の頻繁なユースケースであるため、SKUとして参照されています。

  • 子ドキュメントに対するファセットのカウントとは、検索の結果が通常は親ドキュメントである場合でも、ファセッティングには子ドキュメントとそのフィールドが使用されることを意味します。

  • ファセットカウントのロールアップとは、子ドキュメントがリンクされている親ドキュメントがカウントされるため、子ドキュメントがファセットヒットに寄与することを意味します。たとえば、親ドキュメントに2つの赤い子ドキュメントがある場合、ロールアップのために1つとしてカウントされます。

{
    "queries": {
        "parents":"content_type:parentDocument",             (1)
        "sku_fqs": [{"#sku_attr1_tag":"sku_attr1:foo"},      (2)
                    {"#sku_attr2_tag":"sku_attr2:bar"}
                   ]
    },
    "filter": {
            "#sku_filters":{                                 (3)
                "parent": {
                    "which": {"param":"parents"},
                    "filters": {"param":"sku_fqs"}
                  }}
    },
    "facet": {
        "sku_attr1": {                                       (4)
            "type":"terms",
            "field":"sku_attr1",
            "limit":-1,
            "domain": {       (5)
                 "excludeTags":"sku_filters",
                 "blockChildren":"{!v=$parents}",
                 "filter":
                     "{!bool filter=$sku_fqs excludeTags=sku_attr1_tag}"
            },
            "facet": {
                "by_parent":"uniqueBlock({!v=$parents})"    (6)
            }
        }
    }
}
1 追加のクエリを介して、後で使用する親フィルターを定義します。
2 JSONクエリDSLでのタグ付けで説明されているように、異なる#タグの下に2つのSKUフィルターを宣言します。
3 後で除外するために、タグブロック結合クエリを定義します。これはSKUをトップレベルのドキュメントに結合し、先に定義された親クエリとSKUフィルタークエリを参照します。
4 SKUドキュメント上でドリルサイドウェイズtermsファセットを計算します。SKUフィールドはsku_attr1として定義されており、limit=-1を設定しているため、すべてのファセット値を取得します。
5 excludeTagsを使用して、domain内のトップレベルの親フィルターを削除します。blockChildren値はすべての親クエリを参照します。次に、SKUドキュメントに制限するフィルターを定義しますが、sku_attr2:barのみを保持するタグを1つ除外します。
6 親ドキュメントでサブファセットをカウントします。uniqueBlock()とブロック結合カウントも参照してください。