Guides / Solutions / Ecommerce / Filtering

Dynamic Facets Positioning

The dynamic facets positioning solution lets you update the display order of your facets based on your users’ search query.

The relevance of specific facets varies based on a user’s search input. For example, if a user types “red shirt”, then color facets become less important than those for brand or size. By repositioning facets based on user queries, you can maximize the relevance of your facets, improve and streamline user experience, and help users find what they’re looking for faster.

Solution requirements

Difficulty
Intermediate
Plan Premium
Features Rules
Prerequisites Instant Search 3+

Implementation guide

To dynamically implement positioned facets, create a Rule for each query and context whose displayed facets you want to reposition. The Rule’s consequence should return a customData object that defines a new facet order.

After creating your Rules, make a custom widget with a custom render method. For every query that your user enters, this custom widget checks if the customData, which contains the facet positions, has been returned. It then sets the order CSS property for each of your facet containers to its corresponding position value.

Creating Rules

Your Rule should be triggered by the queries or contexts for which you want to dynamically reposition facets. These Rules should return a customData object containing a facetPositions list. The facetPositions list should contain objects with two attributes:

  • name: the name of the facet you want to reposition,
  • position: the new position of that facet.
1
2
3
4
5
6
7
8
9
10
11
12
{
  "facetPositions": [
    {
      "name": "facetName1",
      "position": 2
    },
    {
      "name": "facetName2",
      "position": 1
    }
  ]
}

Using the dashboard

  1. Select the Rules section from the left sidebar menu in the Algolia dashboard.
  2. Under the heading Rules, select the index you are adding a Rule to.
  3. Click the New rule button.
  4. In the Condition(s) section, keep Query toggled on, select Contains in the dropdown, and enter the keyword whose displayed facets you want to reposition. Whenever this term appears in your user’s search result, your Rule repositions the according to the Rule’s consequence.
  5. In the Consequence(s) section:
    • Click the Add consequence button and select Return Custom Data.
    • In the input field that appears, provide the following JSON code:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
       {
         "facetPositions": [
           {
             "name": "brand",
             "position": 2
           },
           {
             "name": "color",
             "position": 1
           }
         ]
       }
      
  6. Don’t forget to save your changes.

Repeat this process for each condition which displayed facets you want to reposition.

Using an API client

It’s best to trigger your Rule if a query contains a specific word. Your Rule’s consequence should return a customData object containing an array of objects with two attributes:

  • name: the name of the facet you want to reposition.
  • position: the new position of that facet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$rule = [
    'objectID' => 'a-rule-id',
    'condition' => [
        'pattern' => 'dress',
        'anchoring' => 'contains',
        'alternatives' => true,
    ],
    'consequence' => [
        'params' => [
            'filters' => 'category = 1',
        ],
        'userData' => [
            'facetPositions' => [
                [
                    'name' => 'brand',
                    'position' => 2,
                ],
                [
                    'name' => 'color',
                    'position' => 1,
                ],
            ],
        ],
    ],
];

$index->saveRule($rule);

Repeat this process for each condition which displayed facets you want to reposition.

Create a dynamic facet positioning rendering widget

Add the display:flex CSS attribute to your facet container. This lets you use the order CSS property.

In your instantsearch widget, add a custom widget. This custom widget only requires a render method, which takes a renderOptions parameter.

1
2
3
4
5
search.addWidget({
  render(renderOptions) {
    // ...
  }
});

Define three constants:

  • results: the search results of your user’s current query.
  • userData: the userData returned with the current results (this is where you store the customData object returned by your Rule).
  • facetsContainer: the DOM element that contains your facets.
1
2
3
4
5
6
search.addWidget({
  render(renderOptions) {
    const results = renderOptions.results;
    const userData = results.userData;
    const facetsContainer = document.querySelector("#facets-container");
});

Revert to the default order of your facets by setting the order of each facet element to its position in your facetsContainer. Then, check whether userData is empty, and return if that’s the case (it would mean that no dynamic faceting Rule was triggered).

1
2
3
4
5
6
7
8
9
10
11
12
search.addWidget({
  render(renderOptions) {
    // ...

    // Revert to default order of facets
    Array.from(facetsContainer.children).forEach((node, index) => {
      node.style.order = index + 1;
    });

    if (!userData) return null;
  }
});

Your results may contain userData that is unrelated to facet positions. This means you need to extract your facet positioning customData, to verify that it exists and that it’s properly formatted.

1
2
3
4
5
6
7
8
9
10
11
12
search.addWidget({
  render(renderOptions) {
    // ...

    // Update order of facets per query
    const customFacetsData = userData.find(
      data => data.facetPositions && Array.isArray(data.facetPositions)
    );

    if (!customFacetsData) return null;
  }
});

For each facetPosition object in customData, a DOM element is selected based on the facet’s name. This element’s order property is then set to the position defined in your customData object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
search.addWidget({
  render(renderOptions) {
    // ...

    customFacetsData.facetPositions.forEach(facet => {
      const element = document.getElementById(facet.name);

      if (element) {
        // Order property is zero-based, so we decrement by one
        element.style.order = facet.position - 1;
      }
    });
  }
});

Did you find this page helpful?