JSON 查询 DSL
JSON 请求中提供的查询和过滤器可以使用功能强大的查询 DSL 指定。
查询 DSL 结构
JSON 请求 API 接受三种不同格式的查询值
-
使用默认
deftype
(在大多数情况下为lucene
)的有效查询字符串,例如,title:solr
。 -
指定其
deftype
的有效本地参数查询字符串,例如,{!dismax qf=title}solr
。 -
具有查询解析器名称和任何相关参数的有效 JSON 对象,例如,
{ "lucene": {"df":"title", "query":"solr"}}
。-
顶层“query”JSON 块通常只有一个属性,表示要使用的查询解析器的名称。查询解析器属性的值是包含任何相关参数作为 JSON 属性的子块。整个结构类似于“本地参数”查询字符串。查询本身(通常在本地参数中使用名称
v
表示)使用键query
指定。
-
所有这些语法都可用于为 JSON 请求 API 的 query
或 filter
属性指定查询。
查询 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 查询 DSL 中的标记
查询和过滤器通过将它们包装在周围的 JSON 对象中来标记。标签的名称指定为 JSON 键,查询字符串(或对象)成为与该键关联的值。标签名称属性以井号开头,并且可以包含多个标签,以逗号分隔。例如:{"#title,tag2,tag3":"title:solr"}
。请注意,与使用宽松 JSON 解析规则的其余 JSON 请求 API 不同,标签必须用双引号括起来,因为它们的前导 #
字符。下面的示例创建了两个已标记的子句:titleTag
和 inStockTag
。
-
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 的产品(父级)的过滤器。让我们逐项介绍
{
"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() 和块连接计数。 |