How do we prevent alphabetical ordering of returned facet values?

We’re searching our index on algolia through the api and rendering facets and their values each time the search is updated. Each facet returns a maximum of 5 values to show the user.

When a facet attribute is selected, the search result json returns that facet and its attributes re-ordered first by their count and second by alphabetical order. Usually the just-elected facet value is shown first and we’re happy with that.

If we then select another facet with a count of say 10, then in the returned search results, if there are other facet values that have not been selected but that also have a count of 10 and are higher up in alphabetical order they’ll popup ahead of the just-selected facet removing it from sight for the user. And that’s unusual because the user expects to see what they just selected in the returned results.

How can we ensure that the returned search result facet values show up in the order: highest count, selected, and then alphabetical as opposed to highest count, alphabetical?

Thanks

1 Like

Hi David,

Actually the behaviour you describe at the end is exactly the one that instantsearch.js uses by default.

You can see it on our live e-commerce demo.

If you think that something is not working properly, could you send a demo link on your live implementation or a JSFiddle which reproduce the behaviour? That would be super helpful to dig more into your specific issue.

Thanks!

Hi Julien,

I am David’s colleague. Thank you for your reply. Below is an example request from our application which shows that the results are ordered by count and then alphabetical, as opposed to count and then selected.

In this request, the facet filter contains services.name and the value is Webcasts.

curl 'http://b50aw4aeb1-dsn.algolia.net/1/indexes/Organisations/query?x-algolia-agent=Algolia%20for%20vanilla%20JavaScript%203.21.1&x-algolia-application-id=B50AW4AEB1&x-algolia-api-key=9addc09516618439ec4f3d725078f0e3' -H 'Origin: http://default-environment.k3w8vgzcnj.us-west-2.elasticbeanstalk.com' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,ar;q=0.6' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' -H 'content-type: application/x-www-form-urlencoded' -H 'accept: application/json' -H 'Referer: http://default-environment.k3w8vgzcnj.us-west-2.elasticbeanstalk.com/s?facets=%5B%22services.name%3AWebcasts%22%5D' -H 'Connection: keep-alive' --data '{"params":"query=&hitsPerPage=10&facets=*&maxValuesPerFacet=5&facetFilters=%5B%22services.name%3AWebcasts%22%5D"}' --compressed

The response will return these facets:

... "facets":{"services.name":{"Animation":1,"Campaign Implementation":1,"Conferences":1,"Crisis Management":1,"Digital Consultancy":1} ...

As you can see, Webcasts is not in the facets even though it was selected and sent with the request. The reason seems to be that it was tied with a count of 1 and then pushed down because they were sorted alphabetically. Since it have a limit of 5 facets, it didn’t make the cutoff. What we’d like is for the selected facet to always appear in the list of returned facets.

If this example isn’t clear, please let me know what other information I can provide.

We’re not using instantsearch.js but we’re using Algolia’s JS library algoliasearch-client-javascript.

Also, I noticed that the live e-commerce demo uses the queries endpoint for multiple indices and gets back multiple result objects, each containing a different facets array. What is the difference between them and why would you do that as opposed to calling the query endpoint, as we are doing? It seems that the demo has just one index anyway.

Hello @mohamed!

Thanks for giving more context, I see why you have issues now :slight_smile:

The main problem here is that you are using the raw API Client instead of the JS Helper which we strongly recommend: it handles a search state internally, it has advanced features built-in (like facets sorting) and it’s really easy to use. You can go from a JS Client to a Helper implementation very easily (you won’t struggle if you switch).

More details on the specific Helper feature that you are interested in: getFacetValues doc reference
A more practical guide with code examples is also available here.

About the Webcasts facet value that is not returned with your query, it’s because by default the API doesn’t return facets for which there are 0 result. This is to avoid the common pitfall of displaying useless filters :wink:

Finally, the single query vs. multi query endpoints are just 2 ways to send API calls: a multi-query will allow you to group different queries in one single HTTP call (even if it’s on the same index, you could use different filters/sorting). It does not have any impact on the number of operations though.

Hope this helps! Let me know if you could fix your issue with these recommendations.

Julien

1 Like

Hi Julien,

I just got around to updating our code to using the JS Helper client. Unfortunately, the problem persists where the selected facet isn’t appearing in the list of returned facets.

Please take a look at this JS Fiddle I created to demonstrate the problem: https://jsfiddle.net/czvy3b9d/

In the Fiddle, I specify Microsoft as a facet value for clients.name and then call getFacetValues(‘clients.name’). As you can see, Microsoft is missing from the results.

Please advise. Thank you!

Hi Julien,

Can you please take a look at the JS Fiddle and let me know if the Algolia behavior is expected?

Specifically, why isn’t a specified facet value being returned by Algolia in the list of facet values?

I appreciate your response.

Mohamed

Hi @mohamed - I tried your JSFiddle, I think the issue is with the maxValuesPerFacet. When I changed it to 20, for example, I do see Microsoft in the results.

Hi @dzello, I appreciate your response.

I agree that setting maxValuesPerFacet to 20 fixes the problem in this case, but it doesn’t seem like a permanent solution since we could get another case that requires 30, and another maybe that requires 40, etc, depending on the results.

The problem is that the facet sorting is done in the client. I didn’t find any options to do the sorting on the server. Is increasing maxValuesPerFacet the best we can do and hope we cast a wide enough net to capture the selected facets?

The sorting does happen on the server, true. A few options come to mind:

  1. If the number of facet values can be very high, you could implement search for facet values.

  2. You can give the user a button to “Show more” facet values - when they click it, you would increase maxValuesPerFacet and do another query, replacing the results.

Hi @dzello, thanks again for your response.

About the first option: we do have a large number of facet values but we still want to show a list of checkboxes so the user can see the facet values with the most results, including the ones they have already selected so they have the ability to un-select them.

About the second option: this will still have the problem of the user not seeing their current selections unless they click “Show more” to get more facet values, which I think would look strange.

Sure thing! With search for facet values you can still display top results and the selections:

More info in the blog post here.

With the second option, you would need to make sure to preserve the selections in whatever custom templating that you implemented, but if you can put it in a scrollable div you could probably use the default behavior (at least this is my experience with React InstantSearch).

Hi @dzello and thanks again for your response.

This InstantSearch widget looks pretty good but InstantSearch doesn’t seem to give programmatic access to get and set facet selections. Am I wrong about this?

It’s important for us to have access to the facet values because we update the page URL to reflect the state of the search, thus having the ability to access any specific search using the URL.

Hi @mohamed! Great questions. The way you can change the facet values programmatically is by changing the state of the search. That can be done via the underlying helper or changing the URL.

Hi @dzello and thanks again for replying. Do we have access to the underlying helper with InstantSearch? The only way I see possible is by specifying options.searchFunction during initialization and then saving the helper that is passed in as a parameter to that function. Is that the best way?

Yes, that’ll be your best bet. :thumbsup:

Hi @dzello, I did that and got the helper but when I set a facet value programmatically on the helper using addDisjunctiveFacetRefinement() and call search() on the helper, the results come back as if that facet wasn’t selected, ie the results aren’t refined and getFacetValues() returns that facet value with isRefined as false.

Would you know why that’s the case? Is there some other way I should be setting refinements outside of the widgets and kick off searches?

At a glance that sounds like it should be working. Can you update the JS Fiddle so I can take a look?

Yes, here is a new Fiddle: https://jsfiddle.net/ros9fLtj/

Thank you so much.

Hi @mohamed, check out this fiddle:

https://jsfiddle.net/ros9fLtj/1/

Mutation should be done on the helper instance of instantsearch. Sorry, this is admittedly a little confusing.

It makes sense. Thanks for the JS Fiddle! I’ll try it out!

By the way, do you know how I can use a custom template with the refinementList widget for which you posted an animation above? The documentation shows only simple examples for the hits widget so it’s not clear how to do it for refinementlist and what placeholders it expects. The reason I ask is that I want to apply my own CSS classes to the checkboxes, facet counts, etc.

I did a search on the forum and didn’t find an answer to this.

I really appreciate all the help.