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 的 query
或 filter
属性指定查询。
查询 DSL 示例
以下示例展示了如何使用上面讨论的每种语法来表示查询。每个代码段都表示相同的基本搜索:名为 name
的字段中的术语 iPod
使用标准查询 API,使用简单的查询字符串
curl
curl -X GET "http://localhost: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 http://localhost: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,使用 local-params 字符串
curl
curl -X POST http://localhost: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 请求 API,使用完全展开的 JSON 对象
curl
curl -X POST http://localhost: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
)包装在一个提升查询中
使用标准查询 API
curl
curl -X GET "http://localhost: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,混合使用完全展开的查询和 local-params 查询。如您所见,特殊键 v
被键 query
替换
curl
curl -X POST http://localhost: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 http://localhost: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
创建强大的嵌套查询。在此示例中,用户在 name
字段中搜索包含 iPod
且不在 popularity
排名的下半部分中的结果。
curl
curl -X POST http://localhost: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 http://localhost: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 http://localhost: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 http://localhost: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 http://localhost:8983/solr/techproducts/query -d '
{
"queries": {
"electronic": {"field": {"f":"cat", "query":"electronics"}},
"manufacturers": [
"manu:apple",
{"field": {"f":"manu", "query":"belkin"}}
]
},
"query":{"param":"electronic"}
}'
JSON Query DSL 中的标记
通过将查询和过滤器包装在周围的 JSON 对象中来标记它们。标记的名称指定为 JSON 键,查询字符串(或对象)成为与该键关联的值。标记名称属性以哈希为前缀,并且可以包含多个标记,用逗号分隔。例如:{"#title,tag2,tag3":"title:solr"}
。请注意,与使用宽松 JSON 解析规则的其余 JSON 请求 API 不同,由于前导 #
字符,标记必须用双引号引起来。下面的示例创建了两个标记子句:titleTag
和 inStockTag
。
curl
curl -X POST http://localhost: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 的产品(父级)的过滤器。让我们逐项进行
{
"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 中的标记中所述,在不同的 # 标签下声明两个 SKU 过滤器。 |
3 | 定义标签块联接查询,以便以后排除。这会将 SKU 联接到顶级文档,并引用前面定义的 parents 查询和 SKU 过滤器查询。 |
4 | 计算 SKU 文档上的钻取侧 terms 分面。SKU 字段定义为 sku_attr1 ,并且我们已设置 limit=-1 ,以便我们获取所有分面值。 |
5 | 使用 excludeTags 在 domain 中移除顶级父过滤器。blockChildren 值引用 all-parents 查询。然后我们定义一个过滤器来限制到 SKU 文档,但排除一个标签,只保留 sku_attr2:bar 。 |
6 | 计算父文档中的子分面。另请参见uniqueBlock() 和块联接计数。 |