Skip to content

Multi-select faceting in Solr with Solarium

Solarium is a library I often use at Enrise for querying Solr. For one of the projects I work on, a second hand car site, I was having issues on advanced faceting with Solr which I could easily solve using Solarium.

Before diving into the problem let’s explain something about faceting in general. Faceting is a technique for guided navigation where search results are separated into categories, often including counts on those categories. The user can then select from those categories to restrict their search step by step.

In this case one of those facets is the fuel type.

=== Fuel type ===
[ ] Petrol (43)
[ ] Diesel (28)
[ ] Hybrid (12)
[ ] Electric (2)

When selecting “Diesel” as an extra criteria facet will return no results for every other element in the category. A car can’t run on both “Petrol” and “Diesel” in general.

=== Fuel type ===
[ ] Petrol (0)
[x] Diesel (28)
[ ] Hybrid (0)
[ ] Electric (0)

Executing this query on Solr is as easy as adding the fq GET parameter:
This works great if there are no other criteria active which should filter out some data.

Facetting using facet.mincount=1

In our case we want to create a guided navigation on cars that are in stock for only one supplier. This is an easy task again when using Solr. Another simple addition to the GET parameters does the trick.
In this example the current supplier has no “Hybrid” or “Electric” cars in there stock resulting in the “missing” fields.

=== Fuel type ===
[ ] Petrol (19)
[ ] Diesel (8)

Again perfect behaviour, but after selecting “Diesel” we have an issue!

=== Fuel type ===
[x] Diesel (8)

The selection for “Petrol” is gone, which I don’t want, because it generates a bad user experience. The result is easy to explain although. The facet.mincount=1 filters out all elements with zero results.
Unfortunately I didn’t find a solution query wise to resolve this. I resolved this using a javascript solution. While working with this data though I could use this functionality in another case, which is the brand-model connection.

Selecting a model

Selecting a car model can’t be done without selecting a brand first. But when a user selects a model, I don’t want the other models to disappear. Maybe a user can’t decide if he/she wants to select between an Audi A4 or A6.

=== Model ===
[ ] A3 (11)
[ ] A4 (19)
[ ] A6 (7)

So in this example, two criteria should influence the models facet but selecting the model itself shouldn’t. And yet again, Solr has a solution.

=== Model ===
[ ] A3 (11)
[x] A4 (19)
[ ] A6 (7)

When using this query on Solr it will return 19 results, but leave the facet model untouched for the filter query on the model itself.
You can define any string you would like on tagging and exclusions. It can even be multiple exclusions seperated by a comma.
In this case we use ‘inner’ and ‘outer’. Inner defines the base criteria that always should be filtered. Outer defines the criteria that the user has added selecting facets in the guided navigation.

But this article was about Solarium also, wasn’t it?

Yes! Because Solarium will make these type of queries easy!

$client = new Solarium_Client($config);
$query = $client->createSelect(); // get a select query instance
$query->addFilterQuery(array('key'=>'supplier', 'query'=>'supplier:1', 'tag'=>'inner'));
$query->addFilterQuery(array('key'=>'brand', 'query'=>'brand:audi', 'tag'=>'inner'));
$query->addFilterQuery(array('key'=>'model', 'query'=>'model:A4', 'tag'=>'outer'));
$facets = $query->getFacetSet(); // get access to the facetset options
$facets->createFacetField(array('field'=>'model', 'exclude'=>'outer'));

There’s nothing more to it! Easy?!

Additional information

Solarium has way more options to query Solr. It is THE library to use if you want to query Solr from PHP. Almost every feature available in Solr has been implemented up to the last Solr 4.0 release.
Big kudos to Bas de Nooijer for creating the awesome Solarium library!


This article is also published on the Enrise website.

Leave a Reply

Your email address will not be published. Required fields are marked *