Add, Remove, Sort, and Group Storefront Search Filters

In the course of developing a Workarea application, you may need to customize the UI for search filters on category browse and search results pages.

In this example, the filters shown are "Category", "Color", "Size", and "Price". Although "Category" is a special case, the "Color" and "Size" filters are configurable terms filters, while "Price" is exposed as a range filter.

You may need to add a new filter...

...remove an existing filter...

...sort them in a different order...

..."pin" the filter to the top of the page, treating it like a special case...

...or, arrange them into groups or some other custom design:

This guide will show you how to do all of these things, and by the end of it, you'll be a Workarea filter expert!

Etymology

Although the user sees these objects as "filters", you may see the word "facet" referring to the same feature, especially in the backend Ruby code (for example, the Facet class). The term "faceted search" is derived from Elasticsearch, which originally implemented a feature called "facets" (now replaced with "aggregations") that enabled filtering a resultset by various parameters. Workarea's implementation of filters echoes and compliments this functionality provided out-of-the-box by Elasticsearch. You'll see the terms "filter" and "facet" used interchangeably in this guide, but they refer to the same feature.

Implementation

The API calls for the above customizations are mostly contained within the view model for the given page, so either Storefront::SearchViewModel or Storefront::CategoryViewModel. Workarea iterates over the #facets method for the view model in the view. This represents the collection of filters (as well as their returned data) in storefront search and category browse pages. These are the points at which one must extend in order to customize how filter display works.

The data for the #facets method is derived from the #terms_facets and #range_facets methods on Search::Settings and Catalog::Category (again, dependent on whether you're browsing a category or viewing search results).

To summarize, here are the relevant API calls for this guide:

Workarea::Storefront::SearchViewModel#facets

Workarea::Storefront::CategoryViewModel#facets

Workarea::Search::Settings#terms_facets

Workarea::Search::Settings#range_facets

Workarea::Catalog::Category#terms_facets

Workarea::Catalog::Category#range_facets

Add a Search Filter

The attributes to filter on are enumerated in the site's global Search::Settings configuration, which is editable in the admin by visiting /admin/search_settings. There are two types of filters provided out-of-the-box for you, Terms filters and Range filters. Let's learn more about how to manipulate both kinds:

Add a Terms Filter

Terms filters are completely configurable in the admin's "Search Settings" page, and require no developer assistance to configure. However, developers setting up an application for the very first time may want to decorate the Workarea::SearchSettingsSeeds from core to add their own filterable attributes like so:

This will result in the "Material" filter rendering on the storefront below "Color" and "Size", since that's the order they were configured in:

Add a Range Filter

The only range filter provided out-of-the-box for you by Workarea is the "Price" filter. This is configurable through the admin, but requires developer intervention to customize the admin UI so the data for these filters can be entered in.

Next, extend the admin UI to allow admins to update the range filter values. First, you'll learn how to add this UI to the global search settings, then you'll learn how new range facets can be added to category edit pages.

To get started, decorate Admin::SearchSettingsController to output the data for your custom range facet:

module Workarea
decorate Admin::SearchSettingsController do
def show
super
@height_facets = @settings.range_facets['height'] || []
end
end
end

You'll now need to update the markup to add the fields necessary for editing the various range values for the filter. The easiest way to do this is to override the workarea/admin/facets/price_inputs partial and rename it to match your new filter, like workarea/admin/facets/height_inputs...

Now that you've added a range filter to global search settings, you'll need to add it to the category edit page in order to allow categories to override the global search settings. To do this, you'll follow a slightly different path than what was described above, but the concepts are the same.

Now, when you restart your server and refresh the category edit page in admin, you'll see your new range filter!

Add in the ranges you wish to filter on, ensure there's product data for that filter, and then you'll be ready to show it on the storefront.

You may have to prevent the existing price filter from showing twice, as well:

module Workarea
decorate Storefront::CategoryViewModel, Storefront::SearchViewModel do
def facets
super.uniq(&:system_name)
end
end
end

Remove a Search Filter

Filters can be omitted from display on the storefront by removing them from the search settings. But this will remove the filter from displaying at all. You may want to display the filter in certain cases, for example, on a category browse page but not on a search results page. To do this, you'll need to decorate the relevant view model. Here's an example of removing the price filter from category browse pages in app/view_models/workarea/storefront/category_view_model.decorator:

module Workarea
decorate Storefront::CategoryViewModel do
def facets
super.delete_if do |facet|
facet.system_name == 'price'
end
end
end
end

Before applying this decoration, filters might look like something like this:

After the decoration is applied, you should see the price filter omitted on category pages...

...but not on search pages!

Sort Filters

Out of the box, Workarea provides the following default sort order for your filters:

Category (when searching)

Terms filters in the order they appear in the Search::Settings#terms_facets Array

Range filters in the order they appear in the Search::Settings#range_facets Array

Sorting filters can be done by manipulating the order that filters appear in the collection:

module Workarea
decorate SearchSettingsSeeds do
def perform
Search::Settings.current.update_attributes!(
terms_facets: %w(Size Color) # original order was "Color", "Size"
)
end
end
end

Your filters should now look something like this:

"Special Case" Sorting

It is also possible to sort filters programmatically in the codebase to treat these filters like a "special case", for example in the case of category filtering only applying on search pages, and always sticking to the top of the filter navigation. To do so, follow the logic in "Remove a Search Filter" to decorate the appropriate view models' #facets method. Here's an example of "pinning" the price filter to the top of the sidebar:

This multiple decoration is best defined in the file app/view_models/workarea/storefront/product_browsing.decorator. While you can't actually decorate the ProductBrowsing module, this file path will be looked up if ProductBrowsing is decorated, and thus your multiple decorations will apply cleanly without the need to manually load them at app initialization.

Before applying this decoration, your filters might look like something like this:

After the decoration is applied, you should see the price filters appearing first:

Grouping Filters

A growing trend for retailers is to group multiple filters together in the UI. For example, a shoe retailer might want to express "Color" and "Material" within the same filter group, even though these are two distinct facets of the items in search results. In this example, you'll learn how to combine these filter values together visually, and call it "Style". To accomplish this, you'll need to override the workarea/storefront/categories/show.html.haml and workarea/storefront/searches/show.html.haml to render these filters in a slightly different way. To provide the data for this special filter group, you'll also need to override Storefront::SearchViewModel and Storefront::CategoryViewModel.

It's heavily based on the out-of-box workarea/storefront/facets/_terms.html.haml partial, but includes two separate dependencies (color_facet and material_facet) rather than the general facet used in the terms filter.

Now that your partial is defined, you'll need a way to render it. Begin by generating overrides for the aforementioned views:

Make sure Search::Settings#terms_facets includes the "Material" filter, and your new grouped filter will render in the storefront!

Additional Considerations

The storefront search filter UI is heavily cached on category browse pages. Some changes you make may not be visible until those caches expire, which can be anywhere from 15 minutes (HTTP page cache) to 1 hour (fragment cache for category pages). For this reason, Workarea developers typically favor testing changes to the filter UI on the search pages, but some special cases may force you to test on the category pages. In these cases, it's best to wait for the cache to expire.