blob: 5d050fd0cad9ef43538967fd164facfb32b2a481 [file] [log] [blame]
= JSON Query DSL
:solr-root-path: ../../
:example-source-dir: {solr-root-path}solrj/src/test/org/apache/solr/client/ref_guide_examples/
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
Queries and filters provided in JSON requests can be specified using a rich, powerful query DSL.
== Query DSL Structure
The JSON Request API accepts query values in three different formats:
* A valid <<the-standard-query-parser.adoc#,query string>> that uses the default `deftype` (`lucene`, in most cases). e.g., `title:solr`.
* A valid <<local-parameters-in-queries.adoc#,local parameters query string>> that specifies its `deftype` explicitly. e.g., `{!dismax qf=title}solr`.
* A valid JSON object with the name of the query parser and any relevant parameters. e.g., `{ "lucene": {"df":"title", "query":"solr"}}`.
** The top level "query" JSON block generally only has a single property representing the name of the query parser to use. The value for the query parser property is a child block containing any relevant parameters as JSON properties. The whole structure is analogous to a "local-params" query string. The query itself (often represented in local params using the name `v`) is specified with the key `query` instead.
All of these syntaxes can be used to specify queries for either the JSON Request API's `query` or `filter` properties.
=== Query DSL Examples
The examples below show how to use each of the syntaxes discussed above to represent a query. Each snippet represents the same basic search: the term `iPod` in a field called `name`:
Using the standard query API, with a simple query string
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-basic]
====
[.tab-label]*curl*
[source,bash]
----
curl -X GET "http://localhost:8983/solr/techproducts/query?q=name:iPod"
----
====
[example.tab-pane#solrj-ipod-query-basic]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-basic]
----
====
--
Using the JSON Request API, with a simple query string
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-dsl-1]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query" : "name:iPod"
}'
----
====
[example.tab-pane#solrj-ipod-query-dsl-1]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-dsl-1]
----
====
--
Using the JSON Request API, with a local-params string
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-dsl-2]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query": "{!lucene df=name v=iPod}"
}'
----
====
[example.tab-pane#solrj-ipod-query-dsl-2]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-dsl-2]
----
====
--
Using the JSON Request API, with a fully expanded JSON object
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-dsl-3]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query": {
"lucene": {
"df": "name",
"query": "iPod"
}
}
}'
----
====
[example.tab-pane#solrj-ipod-query-dsl-3]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-dsl-3]
----
====
--
== Nested Queries
Many of Solr's query parsers allow queries to be nested within one another. When these are used, requests using the standard query API quickly become hard to write, read, and understand. These sorts of queries are often much easier to work with in the JSON Request API.
=== Nested Boost Query Example
As an example, consider the three requests below, which wrap a simple query (the term `iPod` in the field `name`) within a boost query:
Using the standard query API:
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-boosted-basic]
====
[.tab-label]*curl*
[source,bash]
----
curl -X GET "http://localhost:8983/solr/techproducts/query?q={!boost b=log(popularity) v=\'{!lucene df=name}iPod\'}"
----
====
[example.tab-pane#solrj-ipod-query-boosted-basic]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-boosted-basic]
----
====
--
Using the JSON Request API, with a mix of fully-expanded and local-params queries.
As you can see, the special key `v` is replaced with the key `query`:
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-boosted-dsl-1]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query" : {
"boost": {
"query": {!lucene df=name}iPod,
"b": "log(popularity)"
}
}
}'
----
====
[example.tab-pane#solrj-ipod-query-boosted-dsl-1]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-boosted-dsl-1]
----
====
--
Using the JSON Request API, with all queries fully expanded as JSON:
+
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-boosted-dsl-2]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query": {
"boost": {
"query": {
"lucene": {
"df": "name",
"query": "iPod"
}
},
"b": "log(popularity)"
}
}
}'
----
====
[example.tab-pane#solrj-ipod-query-boosted-dsl-2]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-boosted-dsl-2]
----
====
--
=== Nested Boolean Query Example
Query nesting is commonly seen when combining multiple query clauses together using pseudo-boolean logic with the <<other-parsers.adoc#boolean-query-parser,BoolQParser>>.
The example below shows how the `BoolQParser` can be used to create powerful nested queries. In this example, a user searches for results with `iPod` in the field `name` which are _not_ in the bottom half of the `popularity` rankings.
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-bool]
====
[.tab-label]*curl*
[source,bash]
----
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"}}
]
}
}
}'
----
====
[example.tab-pane#solrj-ipod-query-bool]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-bool]
----
====
--
If `lucene` is the default query parser, the example above can be simplified to:
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-bool-condensed]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query": {
"bool": {
"must": [
"name:iPod"
],
"must_not": "{!frange l=0 u=5}popularity"
}
}
}'
----
====
[example.tab-pane#solrj-ipod-query-bool-condensed]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-bool-condensed]
----
====
--
Example of referencing <<Additional Queries,additional queries>>, <<Tagging in JSON Query DSL,tagging>> and <<other-parsers.adoc#boolean-query-parser,exclusions>>:
[source,bash]
----
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.
}
}
}'
----
Thus, the query above will return only docs matching `size:XL`.
== Filter Queries
The syntaxes discussed above can also be used to specify query filters (under the `filter` key) in addition to the main query itself.
For example, the above query can be rewritten using a filter clause as:
[.dynamic-tabs]
--
[example.tab-pane#curl-ipod-query-bool-filter]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"query": {
"bool": {
"must_not": "{!frange l=0 u=5}popularity"
}
},
"filter: [
"name:iPod"
]
}'
----
====
[example.tab-pane#solrj-ipod-query-bool-filter]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-ipod-query-bool-filter]
----
====
--
== Additional Queries
Multiple additional queries might be specified under `queries` key with all syntax alternatives described above.
Every entry might have multiple values in an array.
To reference these queries one can use `{"param":"query_name"}`, as well as old-style referencing `"{!v=$query_name}"`.
Beware of arity for these references.
Depending on the context, a reference might be resolved into the first element in the array ignoring the later elements, e.g., if one changes the reference below from `{"param":"electronic"}` to `{"param":"manufacturers"}`, it's equivalent to querying for `manu:apple`, ignoring the later query.
These queries don't impact the query result until explicit referencing.
[source,bash]
----
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"}
}'
----
Overall this example doesn't make much sense, but just demonstrates the syntax. This feature is useful in <<json-faceting-domain-changes.adoc#adding-domain-filters,filtering domain>> in JSON Facet API <<json-facet-api.adoc#changing-the-domain,domain changes>>. Note that these declarations add request parameters underneath, so using same names with other parameters might cause unexpected behavior.
== Tagging in JSON Query DSL
Query and filter clauses can also be individually "tagged". Tags serve as handles for query clauses, allowing them to be referenced from elsewhere in the request. This is most commonly used by the filter-exclusion functionality offered by both <<faceting.adoc#tagging-and-excluding-filters,traditional>> and <<json-faceting-domain-changes.adoc#filter-exclusions,JSON>> faceting.
Queries and filters are tagged by wrapping them in a surrounding JSON object. The name of the tag is specified as a JSON key, with the query string (or object) becoming the value associated with that key. Tag name properties are prefixed with a hash, and may include multiple tags, separated by commas. For example: `{"\#title,tag2,tag3":"title:solr"}`. Note that unlike the rest of the JSON request API which uses lax JSON parsing rules, tags must be surrounded by double-quotes because of the leading `#` character. The example below creates two tagged clauses: `titleTag` and `inStockTag`.
[.dynamic-tabs]
--
[example.tab-pane#curl-tagged-query]
====
[.tab-label]*curl*
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/select -d '
{
"query": "*:*",
"filter": [
{
"#titleTag": "name:Solr"
},
{
"#inStockTag": "inStock:true"
}
]
}'
----
====
[example.tab-pane#solrj-tagged-query]
====
[.tab-label]*SolrJ*
[source,java,indent=0]
----
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-tagged-query]
----
====
--
Note that the tags created in the example above have no impact in how the search is executed. Tags will not affect a query unless they are referenced by some other part of the request that uses them.
== Faceting Nested Documents
This paragraph binds many features together.
Basically it's an example of filter exclusions for nested documents when facets are counted over child document fields, but facet counts roll up in parent document counts.
Let's go on item by item:
* _Filter exclusion_ is usually necessary when multiple filter values can be applied to each field.
This is also also known as drill-sideways facets.
See also <<faceting.adoc#tagging-and-excluding-filters,the classic example>> of filter exclusion.
* _Nested documents_, or child documents, are described in <<indexing-nested-documents.adoc#,Indexing Nested Documents>>.
In the example below, they are referred as SKUs since this is a frequent use case for this feature.
* _Counting facets over children documents_ means that even though the result of the search are usually parent documents, the children documents and their fields are used for faceting.
* _Facet counts roll up_ means that child documents contribute facet hits because parent documents they are linked to are counted.
For example, if a parent doc has two (2) red children, it's counted as one (1) due to rollup.
[source,json,subs="verbatim,callouts"]
----
{
"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> Defines a parents filter via <<Additional Queries>> for later usage.
<2> Declares two SKU filters under different #tags, as described in <<Tagging in JSON Query DSL>>.
<3> Defines tag block join query for later exclusion.
This joins SKUs to top level docs, and refers to both the `parents` query and the SKU filter queries defined earlier.
<4> Calculates a drill-sideways `terms` facet over SKU documents.
The SKU field is defined as `sku_attr1`, and we've set `limit=-1` so we get all facet values.
<5> Removes the top-level parent filter in the `domain` with `excludeTags`.
The `blockChildren` value references the all-parents query.
Then we define a filter to restrict to SKU docs but exclude one tag retaining only `sku_attr2:bar`.
<6> Counts the subfacet in parent documents.
See also <<json-facet-api.adoc#uniqueblock-and-block-join-counts>>.