Multi-select faceting in Solr with Solarium

Solarium is a library we often use at Enrise for querying Solr. For one of our projects, AutoTrack.nl a second hand car site, we were having issues on advanced faceting with Solr which we 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 our case one of those facets is the fuel type.

facet=true&facet.field=fueltype
--------------------------------------------------------------------------------
=== 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.

facet=true&facet.field=fueltype&fq=fueltype:diesel
--------------------------------------------------------------------------------
=== 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.

facet=true&facet.field=fueltype&facet.mincount=1&fq=suplier:1
--------------------------------------------------------------------------------
=== Fuel type ===
[ ] Petrol (19)
[ ] Diesel (8)

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

facet=true&facet.field=fueltype&facet.mincount=1&fq=suplier:1&fq=fueltype:diesel
--------------------------------------------------------------------------------
=== Fuel type ===
[x] Diesel (8)

The selection for “Petrol” is gone, which we 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 we didn’t find a solution query wise to resolve this. We resolved this using a javascript solution. While working with this data though we 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, we 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.

facet=true&facet.field=model&facet.mincount=1&fq=suplier:1&fq=brand:audi
--------------------------------------------------------------------------------
=== 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.

facet=true&facet.field={!ex=outer}model&facet.mincount=1&
fq={!tag:inner}supplier:1&fq={!tag=inner}brand:audi&fq={!tag=outer}model:A4
--------------------------------------------------------------------------------
=== 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!

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!

Showing 4 comments
  • Jeroen Schouten
    Beantwoorden

    To solve the problem with the fuel type facet I’d use a combination of tagging/excluding and an OR-query:

    $client = new Solarium_Client($config);
    $query = $client->createSelect(); // get a select query instance
    $query->addFilterQuery(array(‘key’=>’fuel’, ‘query’=>’fuel:diesel OR fuel:petrol’, ‘tag’=>’the-tag’));
    $facets = $query->getFacetSet();
    $facets->createFacetField(array(‘field’=>’model’, ‘exclude’=>’the-tag’));
    $client->select($query);

    When the user selects either ‘Diesel’ or ‘Petrol’ both options remain selectable because of the tag/exclude.
    When both options are selected the sum of cars is returned because of the OR query.

    • Jeroen van Dijk
      Beantwoorden

      Hi Jeroen,

      I understand your strategy, but in the example of the fuel type we always want all the available items in the facet. But this availability has to be limited to the supplier that is current selected by the system as the base request. That criteria should have a mincount=1 for the facet. When a user selects a fueltype, we would like to have a mincount=0 for that criteria.

      But I haven’t seen a solution for that yet.

  • mortenb
    Beantwoorden

    Thank you so much for this post. I am working on a search interface using facets and I had somehow overlooked the possibility of tagging and excluding filters. This really really helped me 🙂

    • Jeroen van Dijk
      Beantwoorden

      Thank you and your welcome!

Reageer

Start typing to search