Filtering attribute with min/max values

Hi all,

I have a use case where I have a couple of attributes which have min and max values. For example the width of a door. The attribute door_width has a minimum width (e.g. 120mm) and a maximum width (e.g. 240mm). Now, I would like to use the rangeSlider widget to filter for those values.

In my first approach I saved all possible values inbetween min and max into Algolia’s index. So in that case this would have been: 120, 121, 122, 123, …, 240. As you can imagine, this would generate a lot of values if you have big differences between the min and the max values. It’s gotten so big, that I reached the limit of 10KB (was up to 17KB) per index item.

Therefore, this is not a possible solution for me anymore. I would much rather like to only save the actual min and max value (e.g. [120, 240]). My question now is, how can I filter for those values?

Let’s say my range-slider has a value of [150, 350], the item with a range of [120, 240] should be displayed. How could I do this with the range-slider?

Thanks for your suggestions.

I’ve tested it with only saving the min and max value into the index and here’s a visual explanation of my test results. Let’s assume we have a min value of 10 and a max value of 20. If the range slider’s values are between 8 and 11 or 18 and 23, the item is shown. Unfortunately, if the range slider’s values are between for example 11 and 18 (both values are between min and max), the item is not shown.

Right now I have saved my values as an array [10, 20] into the index. Maybe there’s a trick to saving a “range value” (I’m thinking something like 10:20 as a string)?

Hope this makes all sense – thanks for your suggestions!

Hi @aarongerig,

If I understood correctly, you need to calculate the range of values for a numeric facet. If so, Algolia does it for you.
Each search response contains Facets Stats field field with the calculated min and max value for each numeric facet.

I hope this solves your problem.

Hi @vladislav.fitc,

Thank you for looking into this! Unfortunately, I don’t think that’s what I’m looking for. I do not need to know the range of values in the index.

Imagine a product with variable sizes (height, width, depth) and therefore also a variable weight. You as a vendor want to specify only the min and max values of those attributes. So for example the height is at minimum 5mm and at maximum 10mm (just an example). That I’d like to insert into the Algolia Index and that’s why I’m passing an array of number => [min, max] or [5, 10].

Now that being said, those values have to be filtered by a range slider. You want your customer to be able to filter a range of product height’s, for example minimum 3mm and maximum 8mm. Your product with a height between 5 and 10 is then displayed, because ranges 3 to 8 and 5 to 10 are overlapping. This works well with my current implementation of the range slider. What doesn’t work is when the customer chooses a range between 5 and 10 (e.g. min: 6 and max: 9) => see image above.

Ranges 6 to 9 and 5 to 10 would be overlapping, but Algolia doesn’t give me that result. Do you know a solution to that?

Hello Aaron,

If I understand your problem correctly, you’re trying to filter results based on a range, where you records don’t contain exhaustive or specific values, but ranges that they belong to. So for instance, a record can look like this:

{
  "title": "Deanta Eton Unfinished Oak Internal Door",
  "min_width": 120,
  "max_width": 240
}

What you’re trying to do is set up a range slider which lets users select their own range, and filters results accordingly. In this scenario, a user setting the range slider to 100-230 wouldn’t retrieve the above result because the minimum width is above their minimum width. However, when setting the range slider to 130-200, they would, as the value would fit within the bounds of the record. Is this correct?

If this is what you’re trying to achieve, you can do numeric comparisons with Algolia filters. We support common numerical comparison operators (< , <= , = , != , >= and >) so you can create filters like: min_width <= ${lower_range_bound} AND max_width >= ${upper_range_bound} (where lower_range_bound and upper_range_bound) are the retrieved values from the range slider).

Note that you will need to compute this filter by hand. You can’t use the default InstantSearch rangeSlider widget, as it only works with specific facet values, not ranges (so, you’d need to have exhaustive values as you initially did, which is impractical and prone to exceed the record size limit). In this case, I recommend using a custom range slider (either a third-party component or building your own), retrieving the values when users select them, computing the filter, and passing it to Algolia at query time.

Since this would be a custom implementation, to know what are the lowest and highest possible bounds, I’d recommend retrieving that information from your own back end and exposing it in your front end, so that you can initialize the range slider with minimum and maximum values.

Does this help?

Hi @sarah.dayan,

Yes, your description is very close to what I’m trying to do. The way I’d like to filter based on those values is a bit different, but still not doable with the default implementation of the range slider.

I guess I have to find a way to make it work with a custom made widget. Any help with that implementation or even suggestions are greatly appreciated!

Thanks for the help though!

Hey Aaron,

I would recommend using a third-party widget like noUiSlider. You can listen for value updates and use the connectConfigure connector to refine InstantSearch’s state.

If you’re struggling with this solution, don’t hesitate to provide us with a minimal CodeSandbox so that we can help you troubleshoot.

Hi @sarah.dayan,

Thank you for your suggestions! I finally made it back to this project – it’s been a busy year. :wink:

I was already using noUiSlider in conjunction with the connectRange connector. Here’s what I have so far: https://codesandbox.io/s/algolia-instantsearch-input-range-slider-xzp6z

Unfortunately, I cannot get it to work with the configure connector. Would you be so kind and help me with this one? I’m stuck here.

If you need any other information or code, please let me know!

The connectRange connector almost completely does what I need. It returns the correct range the values are in, it filters the items correctly, and I’m able to connect a third party library for the range slider.

There’s only one use case, where it doesn’t work properly. If my search range is in between the min and max values of an item in the index, it won’t return that item. For example: Range filter is set to min: 5 / max: 10, an item with values min: 3 / max: 12 won’t be returned. That’s because none of the numbers 5, 6, 7, 8, 9 and 10 are present within the index for that item.

If anyone of you could show me how to build custom queries and apply them to the current state, that would be awesome! The sandbox provided in my last comment, should give you a basic idea of what I have so far. Thank you guys!

Hello @aarongerig

I have an example with connectRange + noUiSlider.

You can use renderOptions.refine() to refine the ui state.
Let me know if it works for you and if you have any question :slight_smile:

@eunjae.lee Thank you for your comment, but it’s a bit more complicated than that. :wink: If you look above, I have my noUiSlider already configured to work with connectRange. I also stated, that connectRange almost 100% does what I need, but only misses one use case (see my last comment above).

I need something to build custom queries and then apply them to the current state (something like configure, but re-rendering the hits as well).

Hey @aarongerig ,
I’m sorry I missed that part.

I tried to modify your example https://codesandbox.io/s/algolia-instantsearch-input-range-slider-xzp6z but it is not using your appId, which means there is no attributes like min_width and max_width.

Anyways, connectRange works for an attribute. However, you want to make filters on two attributes min_width and max_width. So connectRange won’t fit. As my colleague Sarah said, connectConfigure or configure widget is a better choice.

There is a new thing in the latest version of InstantSearch.js(4.9.2) that isn’t documented yet. It’s called “renderState”. It exposes an object containing all the data and methods to render the widgets.

Please refer to the example above.

search.renderState.instant_search.configure.refine({
  numericFilters: ...,
});

You will need to replace instant_search part with your own index name, like

search.renderState.<your-index-name>.<widget-name>

So in your case, you can call something like

search.renderState.your_index.configure.refine({
  numericFilters: [['min_width:5 TO 10', 'max_width:5 TO 10']],
});

Please be aware that

numericFilters: [['min_width:5 TO 10', 'max_width:5 TO 10']],

// means

min_width:5 TO 10 OR max_width:5 TO 10

while

numericFilters: ['min_width:5 TO 10', 'max_width:5 TO 10'],

// means

min_width:5 TO 10 AND max_width:5 TO 10

(More about numericFilters)

I hope this helps!

@eunjae.lee Thank you so much, that actually looks like a valuable solution. Unfortunately I get following error, when I try to call search.renderState.my_index.configure.refine(...):

Uncaught Error: [Numeric filters] Can't switch from the managed API to the advanced. It is probably an error, if this is really what you want, you have to first clear the numeric filters.

How am I supposed to clear the numeric filters, if none are set? At least I can’t find anything in my code, that would set numericFilters to something. Am I missing something?

Btw, I still wanna use connectRange to initialize the noUiSlider widget.

Thanks!

This error happens because you also have a range connector on your page. Just like you can you can use renderState to call refine of the configure, I suggest you call refine of the numeric menu @aarongerig. If you have your app as a sandbox, that will help debugging too.

The problem the error warns you for is the fact that numericFilters is “controlled” by the range, and doesn’t use the “TO” syntax

Hi @haroen

I have now completely reworked my previously shared CodeSandbox. It now reflects exactly what I have so far and it is even connected to my actual Algolia index. Algolia Instantsearch / Input Range Slider - CodeSandbox

If you start dragging the slider you will see the error I described above. As you mentioned I’m still using connectRange, which is essential for me to get some values out of the index in order to apply it to the range slider (values like range and start values):

Bildschirmfoto 2021-01-08 um 11.37.12

Bildschirmfoto 2021-01-08 um 11.37.22

Bildschirmfoto 2021-01-08 um 11.38.02

How can I overcome this issue? Thank you again for looking into it!

Here’s my suggestion avoiding configure on a managed attribute: Algolia Instantsearch / Input Range Slider (forked) - CodeSandbox

Unfortunately your suggestion does not work in every case. As described above, my attribute in the index looks like this: door_light_height: [12, 30]. This means it has a min value (12) and a max value (30). When filtering for a range between 15 and 25 the item with a min/max range of 12 to 30 should be shown. With the range filter this is not the case. Therefore, I want to use some custom queries to make sure it works.

I suggest something like numericFilters: [['door_light_height >= 15', 'door_light_height <= 30']] or in other words: door_light_height with values bigger or equal to 15 OR door_light_height with values lower or equal to 30. This should give me the desired results.

I really hope you understand my problem here.

1 Like

Sorry for misunderstanding you, I didn’t realise what problem you were trying to solve. The solution is to use numericRefinements (the helper “controlled” value), instead of numericFilters (the search parameter)

here’s that fixed: Algolia Instantsearch / Input Range Slider (forked) - CodeSandbox

1 Like

No problem @haroen, this feels actually very close to what I’m trying to achieve now. :slight_smile:

Unfortunately with that, it still has the same behavior as before, excluding items where the filter range is within its value range. Could you elaborate a bit more on this feature? For example, what query is generated from this piece of code?

numericRefinements: {
  'attribute': {
    '>=': 10,
    '<=': 20,
  },
},

Does it result in:

attribute >= 10 OR attribute <= 20

Or does it result in:

attribute >= 10 AND attribute <= 20

Thank you very much!

1 Like

You can check what query it generates in the network tab, but it generates this:

numericFilters: ["door_light_height>=10","door_light_height<=20"]

which is equivalent to an and. I see what you mean now with the “overlapping range”. The best way to achieve that would be to not use connectRange (but something like connectStateResults from Vue: vue-instantsearch/connectStateResults.js at master · algolia/vue-instantsearch · GitHub) and continue using the previous query you were using (doing OR with numeric filters).

This is indeed a feature that our range doesn’t yet have at the moment