Instantsearch Refinementlist sorted

In our project we have several refinement lists and they’re functioning correctly. However they’re default sorting, count the alphanumeric, is being applied. In our situation I have a ‘rank’ attribute on each of my refinement list items but I can’t access it as the API is controlling what’s coming back.

According to the documentation there only appears to be three choices for sorting.

Is there a way around that?

For example, this code yields the following results.

search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#outsideDiameter',
    attributeName: 'outsideDiameter.description',
    operator: 'or',
    sortBy: ["name:asc"],
    limit: 5,
    showMore: true,
    templates: {
      header: getHeader('Outside Diameter'),
    },
  })
); 

I want the list items to be sorted by the fractional values ascendingly. In other words: 3/16", 1/4", 5/16", …, 4.5mm, 6.0mm, etc… Each values does have the ‘rank’ attribute that I mentioned before.

Is that possible?
42 AM

What you can do, to have complete control over the sorting, where you’d convert those values to real numbers, is by sorting inside transformItems. That will give you all items, over which you can .sort((a,b) => toMm(a) - toMm(b)) where they will be sorted by side.

Is that what you wanted? You can also sort differently if that’s what you want

@haroen,

Thanks for writing back. If I understand you, I could call the transformItems option and supply it with a function where I can then iterate over an array of refinement item objects. Which yields the following:

search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#outsideDiameter',
    attributeName: 'outsideDiameter.description',
    operator: 'or',
    transformItems: function(item) {
    	console.log(item);
    },
    limit: 5,
    showMore: true,
    templates: {
      header: getHeader('Outside Diameter'),
    },
  })
);  

Results:
15 PM

I still don’t have my object data that is of type ‘OutsideDiameter’. See outsideDiameter.description is the value that I want to display while outsideDiameter.rank is how I want the faceted items to be sorted. My hope is to access that while inside my refinement list. Below is an example of an ‘OutsideDiameter’ object that’s found inside the ‘search’ index. I’d like to access the ‘rank’ value.

It’s almost like I’m looking for specific control on the “Ranking Formula & Custom Ranking” that’s found under indexName > Ranking to be applicable to faceting.

45 PM

ah, unfortunately that’s not going to be possible. What you can do is the following “hack”.

  1. change your index to have outsideDiameterFilter: '20-1/5"'
  2. in transformItems, change that to split on - and access the rank for sorting, and the label for labelling.

This isn’t too great of an option, but it’s the only way, since refinement lists can only deal with a single attribute, not a whole object of attributes. For that reason the only way is to stringify (serialise) it somehow before indexing and parse (deserialise) when you want to display it.

Another option than the - you have is calling JSON.stringify before indexing, and JSON.parse in transformItems (beware search for facet values though, that’s another problem)

Does that make sense for you?

That’s good and bad information. It’s “good” that I can get to the ultimate goal and bad that I’ll have to touch all of my records again.

This answered by question. Thanks.

@haroen,

I’m back to this ticket looking to apply the “hack” to my project. Can you provide an example of what the code would look like consuming transformItems?

I have the following code thus far and am getting errors from it:

search.addWidget(
    instantsearch.widgets.refinementList({
        container: '#widget-material',
        attributeName: 'material.refinement',
        operator: 'or',
        sortBy: ["name:asc"],
        collapse: true,
        transformItems: function($items) {
            console.log($items);
            return 'justTheValue';
        },
        templates: {
            header: getHeader('Material'),
        }
    })
); 

On the console.log dump I get the following:

Screen Shot 2018-09-18 at 10.52.09 AM

you’re required to return an array from transformItems. I.e. this would be:

  transformItems: function(items) {
            console.log(items);
            return items.map(function(item) {
              // do something with the item, change the label or `highlighted`
              return item;
            });
            // if you want to sort them differently after, you can do `.sort`
            // if you don't want specific items, you can `.filter` them out
        },

I hope that’s clearer!