<p><strong>Answer edited 2022</strong>: I've now written a blog post tutorial on how to do multi-faceted search filtering natively: <a href="https://cld.agency/blog/multi-faceted-filtering-in-craftcms" rel="nofollow noreferrer">https://cld.agency/blog/multi-faceted-filtering-in-craftcms</a></p>
<hr />
<p>I've done this a few times in Twig and a couple of times with the Faceted Nav plugin and would recommend doing it yourself in Twig, as it gives you full control. More specifically, Faceted Nav plugin cannot achieve what most people want which is to separate the filter groups with 'AND' logic while separating the individual filters in each group with 'OR' logic.</p>
<p>The secret to achieving this in Twig is leveraging Craft's <a href="https://craftcms.com/docs/3.x/relations.html#the-relatedto-parameter" rel="nofollow noreferrer">relatedTo</a> parameter, which is incredibly powerful and can accept multiple sets of criteria. Here's a simplified example:</p>
<pre><code>{% set facet1 = craft.entries.slug(['slug-1','slug-2']).ids() %}
{% set facet2 = craft.entries.slug(['slug-3','slug-4']).ids() %}
{% set facet3 = craft.categories.slug(['slug-5','slug-6']).ids() %}

{% set params = ['and',
    { targetElement: facet1, field: 'facetFieldHandle_1' },
    { targetElement: facet2, field: 'facetFieldHandle_2' },
    { targetElement: facet3, field: 'facetFieldHandle_3' }
] %}

{% set entries = craft.entries.section('products').relatedTo(params).all() %}

&lt;ul&gt;
    {% for entry in entries %}
        &lt;li&gt;{{ entry.title }}&lt;/li&gt;
    {% endfor %}
&lt;/ul&gt;
</code></pre>
<p>...So you can see there that my <code>facet1</code>, <code>facet2</code>, <code>facet3</code> arrays are basically acting like 'OR' filters (&quot;fetch me the entries with this slug OR this slug OR this slug&quot;), then we feed those arrays into the relatedTo parameter prefixed with the 'AND' keyword. (Naturally, those hardcoded <code>slug-1</code>, <code>slug-2</code> values will need to come from your form data - usually best to take them via query string params so that the result pages are URL-addressable e.g. <code>craft.app.request.getParam('facet1')</code>).</p>
<p>Once you start adding logic to pre-check the right checkboxes and adding eager loading etc, it gets more complicated, but that's the basics. The last time I did this I added some simple JS to enhance/prettify the URL query strings (by default a form submission will append multiple values repeatedly like this <code>?facet1[]=abc&amp;facet1[]=def&amp;facet2[]=123</code> -- which gets quite ugly and long). With a little JS you can transform into something more like <code>?facet1=abc|def&amp;facet2=123</code>, and use Twig's <code>is iterable</code> test to check whether you're dealing with the enhanced version or the default/no-js/slow-js/googlebot version and <code>split('|')</code> the data accordingly.</p>

CC BY-SA 4.0
2020-09-29T13:02:45.210Z
Did you ever consider or try out Algolia or similar "enterprise search as-a-service" providers?
nitech
CC BY-SA 4.0
2020-09-29T13:19:40.380Z
Yes, looked at Algolia pretty extensively and in the end it just seemed overly complicated for something I could build in Twig very easily. I was also massively put off by their pushy sales rep who literally kept calling every few days to see if I was ready to sign up.
James Smith
CC BY-SA 4.0
2020-09-29T13:22:16.670Z
Ah... I understand. My idea was to use a third party search service so I could leave AI and smart search result adaptation to them. Could I ask how many products your sites have?
nitech
CC BY-SA 4.0
2020-09-29T14:05:08.097Z
One I'm working on at the moment which is using this kind of filtering has 4000 Commerce products with 11,000 variants, and the filtering is working nicely.
James Smith
CC BY-SA 4.0
2020-09-24T10:23:02.020Z
2022-05-03T15:49:07.627Z
2022-05-03T15:49:07.627Z
James Smith
<p><strong>Algolia</strong> is excellent, and I've found the scout plugin to be solid. Algolia will give you pre-built faceted navigation with <a href="https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/" rel="nofollow noreferrer">instantsearch.js</a> so there is very little development time.</p>
<hr />
<p><strong>Elastic search</strong> comes with the added complexity of running/configuring an ES server, will likely require on-going maintenance.</p>
<hr />
<p>However if you have the time and don't want to rely on 3rd parties, I would recommend just DIY with twig. If you want it to refresh the listing 'live' you can use <a href="https://plugins.craftcms.com/sprig" rel="nofollow noreferrer">Sprig</a>, or <a href="https://htmx.org" rel="nofollow noreferrer">htmx</a>.</p>
<p>Basically your filters/facets will be a form that passes a query string to your listing page. Your listing page will then check for the query string and filter accordingly.</p>
<p>An example from a recent project:</p>
<pre><code>{# Get any URL Parameters #}

{% set keywords = craft.app.request.getParam('q')|default(null) %}
{% set kind = craft.app.request.getParam('kind')|default('*') %}
{% set order = craft.app.request.getParam('order')|default('title asc') %}

{# Run the query #}

{% set query = entries
  .search(keywords)
  .kind(kind)
  .orderBy(order)
  .limit(30)
%}
</code></pre>
<p>The difficulty will be the UI for sliders etc, but there are plenty of libraries for this kind of thing.</p>

CC BY-SA 4.0
2020-09-24T09:56:29.017Z
Thanks dmatthams. I didn't dive all in on Algolia yet. Do you have any examples of these kinds of searches that you have built using Algolia?
nitech
CC BY-SA 4.0
2020-09-24T10:14:47.170Z
I don't have anything public, but there are examples over here: https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/
dmatthams
CC BY-SA 4.0
2020-09-24T11:37:34.150Z
Thank you. I read more about the `instantsearch.js`-family (which also has VueJs, Angular, React, etc versions), and they output very nice widgets.
nitech
CC BY-SA 4.0
2020-09-24T09:31:39.713Z
2020-09-24T10:16:36.927Z
2020-09-24T10:16:36.927Z
dmatthams
<p>I want to create these kinds of filters on my Craft Commerce site:</p>
<p><a href="https://i.sstatic.net/ltTU8.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/ltTU8.png" alt="faceted filters faceted search" /></a></p>
<p>I have poked around with the <a href="https://plugins.craftcms.com/faceted-navigation" rel="nofollow noreferrer">Faceted Navigation</a> plugin, <del>but haven't gotten it to work the way I want yet (posted an issue about <a href="https://github.com/pdaleramirez/super-filter/issues/12" rel="nofollow noreferrer">high bandwidth usage</a>)</del> (Fixed in version 1.1.80), and it seems to work nicely- and it's quite fast. However, I wanted to check out 3rd party enterprise search as-a-service.</p>
<p>I also see there is pretty good support for a third party enterprise search service called Algolia through the <a href="https://plugins.craftcms.com/scout" rel="nofollow noreferrer">Scout</a> and <a href="https://plugins.craftcms.com/algolia" rel="nofollow noreferrer">Algolia</a> plugins. I have no previous experience with Algolia, but it seems that they should be able to spit out these kinds of filters.</p>
<p>There also seems to be a somewhat abandonned plugin called <a href="https://transition.topshelfcraft.com/software/craft/searchplus" rel="nofollow noreferrer">Search Plus</a> (which I believe stalled at Craft 2.5) and the <a href="https://plugins.craftcms.com/elasticsearch" rel="nofollow noreferrer">ElasticSearch-plugin</a> which also seems to be stalled because of a dependency on a YII2-&gt;ElasticSearch plugin.</p>
<p>So... should I build my own? <a href="https://nystudio107.com/blog/using-the-craft-cms-graphql-api-on-the-frontend" rel="nofollow noreferrer">NyStudio107 talks about using React with the GraphQL API</a> or <a href="https://nystudio107.com/blog/autocomplete-search-with-the-element-api-vuejs" rel="nofollow noreferrer">the Element API</a> and <a href="https://www.webstoemp.com/blog/combined-searches-and-filters-craft-cms/" rel="nofollow noreferrer">Webstoemp has an example of building this using Twig</a>.</p>
<p>How would you go about doing this?</p>

<ecommerce><plugin-craftcommerce3><filtering><plugin-superfilter>
How to create a faceted search/facet filter in Craft Commerce
# How to create a faceted search/facet filter in Craft Commerce

(Asked by: username_0 on 2020-09-24)

I want to create these kinds of filters on my Craft Commerce site:

[![faceted filters faceted search](https://i.sstatic.net/ltTU8.png)](https://i.sstatic.net/ltTU8.png)

I have poked around with the [Faceted Navigation](https://plugins.craftcms.com/faceted-navigation) plugin, ~~but haven't gotten it to work the way I want yet (posted an issue about [high bandwidth usage](https://github.com/pdaleramirez/super-filter/issues/12))~~ (Fixed in version 1.1.80), and it seems to work nicely- and it's quite fast. However, I wanted to check out 3rd party enterprise search as-a-service.

I also see there is pretty good support for a third party enterprise search service called Algolia through the [Scout](https://plugins.craftcms.com/scout) and [Algolia](https://plugins.craftcms.com/algolia) plugins. I have no previous experience with Algolia, but it seems that they should be able to spit out these kinds of filters.

There also seems to be a somewhat abandonned plugin called [Search Plus](https://transition.topshelfcraft.com/software/craft/searchplus) (which I believe stalled at Craft 2.5) and the [ElasticSearch-plugin](https://plugins.craftcms.com/elasticsearch) which also seems to be stalled because of a dependency on a YII2->ElasticSearch plugin.

So... should I build my own? [NyStudio107 talks about using React with the GraphQL API](https://nystudio107.com/blog/using-the-craft-cms-graphql-api-on-the-frontend) or [the Element API](https://nystudio107.com/blog/autocomplete-search-with-the-element-api-vuejs) and [Webstoemp has an example of building this using Twig](https://www.webstoemp.com/blog/combined-searches-and-filters-craft-cms/).

How would you go about doing this?

**Tags:** ecommerce, plugin-craftcommerce3, filtering, plugin-superfilter


---

## 2 answers

### Answer by username_1 on 2020-09-24. Score: 3

Answer edited 2022: I've now written a blog post tutorial on how to do multi-faceted search filtering natively: https://cld.agency/blog/multi-faceted-filtering-in-craftcms

I've done this a few times in Twig and a couple of times with the Faceted Nav plugin and would recommend doing it yourself in Twig, as it gives you full control. More specifically, Faceted Nav plugin cannot achieve what most people want which is to separate the filter groups with 'AND' logic while separating the individual filters in each group with 'OR' logic.
The secret to achieving this in Twig is leveraging Craft's relatedTo parameter, which is incredibly powerful and can accept multiple sets of criteria. Here's a simplified example:
{% set facet1 = craft.entries.slug(['slug-1','slug-2']).ids() %}
{% set facet2 = craft.entries.slug(['slug-3','slug-4']).ids() %}
{% set facet3 = craft.categories.slug(['slug-5','slug-6']).ids() %}

{% set params = ['and',
    { targetElement: facet1, field: 'facetFieldHandle_1' },
    { targetElement: facet2, field: 'facetFieldHandle_2' },
    { targetElement: facet3, field: 'facetFieldHandle_3' }
] %}

{% set entries = craft.entries.section('products').relatedTo(params).all() %}


    {% for entry in entries %}
        {{ entry.title }}
    {% endfor %}


...So you can see there that my facet1, facet2, facet3 arrays are basically acting like 'OR' filters ("fetch me the entries with this slug OR this slug OR this slug"), then we feed those arrays into the relatedTo parameter prefixed with the 'AND' keyword. (Naturally, those hardcoded slug-1, slug-2 values will need to come from your form data - usually best to take them via query string params so that the result pages are URL-addressable e.g. craft.app.request.getParam('facet1')).
Once you start adding logic to pre-check the right checkboxes and adding eager loading etc, it gets more complicated, but that's the basics. The last time I did this I added some simple JS to enhance/prettify the URL query strings (by default a form submission will append multiple values repeatedly like this ?facet1[]=abc&facet1[]=def&facet2[]=123 -- which gets quite ugly and long). With a little JS you can transform into something more like ?facet1=abc|def&facet2=123, and use Twig's is iterable test to check whether you're dealing with the enhanced version or the default/no-js/slow-js/googlebot version and split('|') the data accordingly.

> **Comments:**
> **username_0**: Did you ever consider or try out Algolia or similar "enterprise search as-a-service" providers?  
> **username_1**: Yes, looked at Algolia pretty extensively and in the end it just seemed overly complicated for something I could build in Twig very easily. I was also massively put off by their pushy sales rep who literally kept calling every few days to see if I was ready to sign up.  
> **username_0**: Ah... I understand. My idea was to use a third party search service so I could leave AI and smart search result adaptation to them. Could I ask how many products your sites have?  
> **username_1**: One I'm working on at the moment which is using this kind of filtering has 4000 Commerce products with 11,000 variants, and the filtering is working nicely.  

---

### Answer by username_2 on 2020-09-24. Score: 2

Algolia is excellent, and I've found the scout plugin to be solid. Algolia will give you pre-built faceted navigation with instantsearch.js so there is very little development time.

Elastic search comes with the added complexity of running/configuring an ES server, will likely require on-going maintenance.

However if you have the time and don't want to rely on 3rd parties, I would recommend just DIY with twig. If you want it to refresh the listing 'live' you can use Sprig, or htmx.
Basically your filters/facets will be a form that passes a query string to your listing page. Your listing page will then check for the query string and filter accordingly.
An example from a recent project:
{# Get any URL Parameters #}

{% set keywords = craft.app.request.getParam('q')|default(null) %}
{% set kind = craft.app.request.getParam('kind')|default('*') %}
{% set order = craft.app.request.getParam('order')|default('title asc') %}

{# Run the query #}

{% set query = entries
  .search(keywords)
  .kind(kind)
  .orderBy(order)
  .limit(30)
%}

The difficulty will be the UI for sliders etc, but there are plenty of libraries for this kind of thing.

> **Comments:**
> **username_0**: Thanks username_2. I didn't dive all in on Algolia yet. Do you have any examples of these kinds of searches that you have built using Algolia?  
> **username_2**: I don't have anything public, but there are examples over here: https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/  
> **username_0**: Thank you. I read more about the `instantsearch.js`-family (which also has VueJs, Angular, React, etc versions), and they output very nice widgets.  

---

