Let's assume for the purpose of this recipe, you have an application that can search for companies within a city and a state. However, the requirements say that not only should you show the search results, but also the number of companies in each city and the number of companies in each state (to say in the Solr way—you want to exclude the filter query from the faceting results). Can Solr do this efficiently ? Sure it can, and this recipe will show you how to do it.
Before you start reading this recipe, let's take a look at the Getting the number of documents with the same field value recipe of this chapter.
schema.xml
file:<field name="id" type="string" indexed="true" stored="true" required="true" /> <field name="name" type="text_general" indexed="true" stored="true" /> <field name="city" type="string" indexed="true" stored="true" /> <field name="state" type="string" indexed="true" stored="true" />
<add> <doc> <field name="id">1</field> <field name="name">Company 1</field> <field name="city">New York</field> <field name="state">New York</field> </doc> <doc> <field name="id">2</field> <field name="name">Company 2</field> <field name="city">New Orleans</field> <field name="state">Luiziana</field> </doc> <doc> <field name="id">3</field> <field name="name">Company 3</field> <field name="city">New York</field> <field name="state">New York</field> </doc> <doc> <field name="id">4</field> <field name="name">Company 4/field> <field name="city">New York</field> <field name="state">New York</field> </doc> </add>
company
, and we tell our application that the user needs the companies matching the word in the state of New York
. However, we would like to show the number of documents matching the word company
in all the states and in the cities of all the states. In that case, the query that will fulfill our requirement should look as follows:http://localhost:8983/solr/cookbook/select?q=name:company&facet=true &fq={!tag=stateTag}state:"New York"&facet.field={!ex=stateTag}city&facet.field={!ex=stateTag}state
The result for the preceding query will look as follows:
<?xml version="1.0" encoding="UTF-8"?> <response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">1</int> <lst name="params"> <str name="q">name:company</str> <arr name="facet.field"> <str>{!ex=stateTag}city</str> <str>{!ex=stateTag}state</str> </arr> <str name="fq">{!tag=stateTag}state:"New York"</str> <str name="facet">true </str> </lst> </lst> <result name="response" numFound="3" start="0"> <doc> <str name="id">1</str> <str name="name">Company 1</str> <str name="city">New York</str> <str name="state">New York</str> <long name="_version_">1471070665204301824</long></doc> <doc> <str name="id">3</str> <str name="name">Company 3</str> <str name="city">New York</str> <str name="state">New York</str> <long name="_version_">1471070665210593280</long></doc> <doc> <str name="id">4</str> <str name="name">Company 4</str> <str name="city">New York</str> <str name="state">New York</str> <long name="_version_">1471070665210593281</long></doc> </result> <lst name="facet_counts"> <lst name="facet_queries"/> <lst name="facet_fields"> <lst name="city"> <int name="New York">3</int> <int name="New Orleans">1</int> </lst> <lst name="state"> <int name="New York">3</int> <int name="Luiziana">1</int> </lst> </lst> <lst name="facet_dates"/> <lst name="facet_ranges"/> </lst> </response>
Now let's see how it works.
The index structure is pretty simple—it contains four fields that describe the company. The search will be performed against the name
field, while the filtering and the faceting will be done with the use of the state
and the city
fields.
So, let's get on with the query. As you can see, we have some typical elements there. First of all we have the q
parameter, which just tells Solr where and what to search for. Then, the facet=true
parameter enables the faceting mechanism. Following that you have a strange looking filter query (the fq
parameter) with the value of fq={!tag=stateTag}state:"New York"
. It tells Solr to only show those results that have both New York
in the state
field, but not the results that have either of them. By adding the {!tag=stateTag}
part, we basically gave that filter query a name (stateTag
) that we will use further.
Now look at the two facet.field
parameters. Our requirement was to show the number of companies in all states and in all cities. The only thing that was preventing us from getting those numbers was the filter query that we added to the query. So, let's exclude it from the faceting results. How do we do it? It's simple—just add {!ex=stateTag}
to the beginning of each of the facet.field
parameters like this: facet.field={!ex=stateTag}city
. It tells Solr to exclude the filter with the passed name.
As you can see in the results list, we got the numbers correctly, which means that the exclude works as intended.
3.144.228.78