JSON 查詢 DSL

JSON 請求中提供的查詢和篩選器可以使用功能強大的查詢 DSL 來指定。

查詢 DSL 結構

JSON 請求 API 接受三種不同格式的查詢值

  • 使用預設 deftype(在大多數情況下為 lucene)的有效查詢字串,例如,title:solr

  • 明確指定其 deftype 的有效本機參數查詢字串,例如,{!dismax qf=title}solr

  • 具有查詢剖析器名稱和任何相關參數的有效 JSON 物件,例如,{ "lucene": {"df":"title", "query":"solr"}}

    • 最上層的「查詢」JSON 區塊通常只有一個屬性,表示要使用的查詢剖析器名稱。查詢剖析器屬性的值是一個子區塊,其中包含任何相關參數作為 JSON 屬性。整個結構類似於「本機參數」查詢字串。查詢本身(通常在使用名稱 v 的本機參數中表示)使用鍵 query 指定。

所有這些語法都可以用於為 JSON 請求 API 的 queryfilter 屬性指定查詢。

查詢 DSL 範例

以下範例示範如何使用上述討論的每種語法來表示查詢。每個程式碼片段都代表相同的基本搜尋:在名為 name 的欄位中搜尋詞彙 iPod

使用標準查詢 API,使用簡單的查詢字串

  • curl

  • SolrJ

curl -X GET "https://127.0.0.1:8983/solr/techproducts/query?q=name:iPod"
final SolrQuery query = new SolrQuery("name:iPod");
final QueryResponse response = solrClient.query(COLLECTION_NAME, query);

使用 JSON 請求 API,使用簡單的查詢字串

  • curl

  • SolrJ

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query" : "name:iPod"
}'
final JsonQueryRequest query = new JsonQueryRequest().setQuery("name:iPod");
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

使用 JSON 請求 API,使用本機參數字串

  • curl

  • SolrJ

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "{!lucene df=name v=iPod}"
}'
final JsonQueryRequest query = new JsonQueryRequest().setQuery("{!lucene df=name}iPod");
final QueryResponse response = query.process(solrClient, COLLECTION_NAME);

使用 JSON 請求 API,使用完全展開的 JSON 物件

  • curl

  • SolrJ

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": {
    "lucene": {
      "df": "name",
      "query": "iPod"
    }
  }
}'
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)包在一個增強查詢中

使用標準查詢 API

  • curl

  • SolrJ

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

  • SolrJ

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

  • SolrJ

curl -X POST https://127.0.0.1:8983/solr/techproducts/query -d '
{
    "query": {
        "boost": {
            "query": {
                "lucene": {
                    "df": "name",
                    "query": "iPod"
                }
            },
            "b": "log(popularity)"
        }
    }
}'
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 建立強大的巢狀查詢。在這個範例中,使用者搜尋 name 欄位中包含 iPod,且popularity 排名後半部的結果。

  • curl

  • SolrJ

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"}}
            ]
        }
    }
}'
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

  • SolrJ

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"
        }
    }
}'
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

  • SolrJ

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"
    ]
}'
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 Facet API 的 篩選網域 網域變更中很有用。請注意,這些宣告會在下方新增請求參數,因此將相同名稱與其他參數一起使用可能會導致意外行為。

在 JSON 查詢 DSL 中標記

查詢和篩選子句也可以個別「標記」。標籤作為查詢子句的控制代碼,允許從請求中的其他位置參考它們。這最常用於 傳統JSON 分面所提供的篩選排除功能。

查詢和篩選器會透過將它們包圍在周圍的 JSON 物件中來標記。標籤的名稱指定為 JSON 鍵,查詢字串(或物件)成為與該鍵相關聯的值。標籤名稱屬性會加上雜湊符號為前綴,並且可以包含多個以逗號分隔的標籤。例如:{"#title,tag2,tag3":"title:solr"}。請注意,與使用寬鬆 JSON 解析規則的其餘 JSON 請求 API 不同,標籤必須以雙引號括起來,因為它們的前面有 # 字元。下面的範例會建立兩個已標記的子句:titleTaginStockTag

  • curl

  • SolrJ

curl -X POST https://127.0.0.1:8983/solr/techproducts/select -d '
{
  "query": "*:*",
  "filter": [
    {
      "#titleTag": "name:Solr"
    },
    {
      "#inStockTag": "inStock:true"
    }
  ]
}'
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 在不同的 #tags 下宣告兩個 SKU 篩選器,如在 JSON 查詢 DSL 中標記中所述。
3 定義標籤區塊聯結查詢以供稍後排除。這會將 SKU 聯結到頂層文件,並參考先前定義的 parents 查詢和 SKU 篩選查詢。
4 計算 SKU 文件上的橫向鑽取 terms 分面。SKU 欄位定義為 sku_attr1,我們已設定 limit=-1,以便我們取得所有分面值。
5 使用 excludeTags 移除 domain 中的頂層父項篩選器。blockChildren 值會參考所有父項查詢。然後,我們定義一個篩選器,以限制為 SKU 文件,但排除一個標籤,僅保留 sku_attr2:bar
6 在父文件中計算子分面。另請參閱 uniqueBlock() 和區塊聯結計數