Facet filter for an object

First up, pleasantries; I’m new to Algolia, and the forum, so hello!

I wanted to confirm my approach to our faceting filter we’re looking to implement…

So we have 5 initial filters, 2 of which I need help with… Month & Temperature. Temperature is basically a list of clothing, ranging from shorts to full winter get up. The data is stored as an object within an indexed item like so…

"name": "Devon",
"type": "county",
"ClothingObj": {
  "JanClothing": "shorts",
  "FebClothing": "shorts",
  "MarClothing": "shorts",
  "AprClothing": "shorts",
  "MayClothing": "jeans",
  "JunClothing": "jeans",
  "JulClothing": "jeans",
  "AugClothing": "jeans",
  "SepClothing": "jeans",
  "OctClothing": "jeans",
  "NovClothing": "shorts",
  "DecClothing": "shorts"
}

Now the Month filter doesn’t really exist in the index, it instead applies to this clothing object… so you select your month, and then your clothing and what is actually searched in Algolia is a combination of the two… eg. NovClothing & shorts - which obviously returns all county’s where you can wear shorts in November.

That’s all working great. The problem arises when filtering on the results page. Each item in the ClothingObj has been added as an individual facet in the Algolia admin (ClothingObj.JanClothing). This means on the front end my MenuSelect for Temperature only displays the value that’s been selected in the search, eg. “shorts”.

<MenuSelect attribute={`ClothingObj.${monthCap}Clothing`} />

${monthCap} is a var for the currently selected month.

What I need is the full list of all possible clothing options to appear under temperature and for the month filter to update the ${monthCap} var when changed. The latter I’ll have to handle with some function, but the former I think relates to how I’ve setup my facets in the admin.

I’m not sure if my problem comes down to the json structure of the index or how I’ve setup the facets in Algolia, any suggestions on the best practice approach would be really welcome.

On top of that, I’ll actually need that Month filter to be multi-select at some point, but that’s for future me to figure out.

Thanks for taking the time to read this!

Cheers

Hi there,

Thanks for taking the time to write such a detailed post!
if I understand your issue correctly, you would like to be able to display the number of items for values other than the one you selected. Is that correct? Would the following display work for you?

Temperature
  Jan (432)
  Feb (231)
  March (109) <- selected
    shorts (34)  <- selected
    pants (75)

If so, it looks like the Hierarchical Menu would be a great fit for your use case, which would require a small change in your record structure.

That being said, this doesn’t change the fact that even the Menu widget should return all facet values. Are you maybe trying to manipulate the filters and facets yourself? The IS library abstracts away a lot of the complexity of dealing with this, but that also means that it doesn’t necessarily play well when query state is being defined outside of the widgets.

Just in case that this is the actual problem, my main suggestion would be to try to leverage as much as possible the widgets themselves and only use your own controlled components when it’s not possible to use IS widgets. For your particular use case, it does seem like you want to navigate between the two hierarchically linked concepts of Month and Clothing, so changing your record structure and use the Hierarchical Menu is probably a great way to go.

I hope this will give you some ideas!
Best,

Thanks so much for getting back to me Jon…

The Hierarchical Menu looks great, but I’m not sure it’s the ideal. There will be some secondary filters on the search results page that will also rely on the months that have been chosen. To have it all in one mega hierarchy might become awkward to manage. I’ll look into it further though.

So just to formalise the needs a little more concisely…

  1. Multiselect Months - Some kind of tag or multiselect to define several months for travel
  2. Constrain Other Filters - Apply the selected months to constrain the secondary filters… eg. match the selected clothing object with the selected months (display all items that have shorts in Jan OR Feb OR Mar)
  3. Display all Facet Values - I’ll look further into this now. We’re using Next.js and so the break down of that setup goes like…

Grab the queried items from the router…

const { temp, month,} = router.query;

Then build the relevant filter syntax with…

const tempFilter = temp ? ` AND ClothingObj.${monthCap}Clothing:${temp}` : '';

Apply the query attribute to the refinement MenuSelect… (${monthCap} being, say, ‘Jan’)

<MenuSelect attribute={`ClothingObj.${monthCap}Clothing`} />

Then finally applying the filter syntax to the Configure element…

<Configure filters={`type:county${tempFilter}`} />

I feel like the Multiselect is going to be the big issue here. If need be we can hardcode the options for the other filters (temp), they wont ever change, and then could manipulate the results from there somehow? Anyway, any help or rough point in an appropriate direction would be really welcome! Thanks so much.

Thanks for clarifying!

This is very specific and I am not sure I can help you much further than this, sadly.

I think this can be done with 3 queries, but I am not 100% sure:

  1. The main query. This query applies all the filters and returns records that match those filters.
  2. The count query. This is a facet query. It computes all the facet counts without all filters applied. The risk here is that this specific combination of disjunctive and conjunctive filters is not allowed for facet queries, but I am not sure.
  3. The constraint query. This is a facet query. It computes all the facet without any filters applied, or only some.

This is a pattern that is similar to what our Menu and Hierarchical Menu do internally, so you may have to build your own widgets to handle that state for your specific structure.

I’m not sure that’s super helpful, but I hope it gives you some ideas!
Best,

Appreciate the help Jon, good man! I’ll get to looking into that further.

With regard to the facet display, from what you can see with our setup, all options for <MenuSelect attribute={ClothingObj.${monthCap}Clothing} /> should indeed display?

So just to follow up on this, I’ve had a thought about constraining other filters to a selected month. If I setup a month object and create a MenuSelect widget for it, could I then setup a Rule to add a query that checks whether that month has a particular rating in another object from the index?

So if I select the month of January, could my rule filter out the results so only items in the indicies that have RatingObj.JanRating:gold are retuned? Essentially…

if jan then RatingObj.JanRating:gold

or

if aug then RatingObj.AugRating:gold

Just need to know if that’s possible, or even better, a good approach, really?

Thanks!

"RatingObj": {
  "JanRating": "gold",
  "FebRating": "gold",
  "MarRating": "gold",
  "AprRating": "golds",
  "MayRating": "silver",
  "JunRating": "silver",
  "JulRating": "silver",
  "AugRating": "bronze",
  "SepRating": "bronze",
  "OctRating": "bronze",
  "NovRating": "bronze",
  "DecRating": "silver"
}

I’m really not sure this would really solve your use case.

First, I don’t believe that a query rule can use a facet value as a condition today.

Additionally, you’ll have issues before: I guess Gold shouldn’t be an available option if no results will match it. To have this working fine, you’ll need to handle this in the front-end anyway.

Unfortunately, I don’t see much to add than what you’ve already discussed with Jonathan.

Thanks for getting back to me Jerska. I think it comes down to the formatting of the data. Rather than an object defined as above, I need arrays like the following…

"highseason": [
    "January",
    "Febuary",
    "March",
    "April"
  ],
"shoulderseason": [
    "May",
    "June",
    "Nov",
    "Dec"
  ],
"lowseason": [
    "July",
    "August",
    "September",
    "October"
  ],

Best get to it!

1 Like

Apologies for going over this again, but things have changed slightly and I could do with some feedback on how I could structure a portion of my data. So I’ve implemented the highseason approach I outlined above, and that is working really well, perfectly in fact.

What I can’t get my head around is how to query against the temperature object below using a value from the highseason facet… it’s really simple to do via my <Configure /> element and some query vars:

<Configure filters={type:area AND highseason:{highseason} AND temperature.{highseason}:${temp}} />

${highseason} is the month value (january) and ${temp} is the clothing (shorts).

Applying that to my <MenuSelect /> seems really awkward though. Is dynamically updating my query vars and applying them to the MenuSelect a viable approach do you think?

<MenuSelect attribute={temperature.${highseason}:${temp}} />

"temperature": {
  "january": "shorts",
  "february": "shorts",
  "march": "shorts",
  "april": "shorts",
  "may": "jeans",
  "june": "jeans",
  "july": "jeans",
  "august": "jeans",
  "september": "jeans",
  "october": "jeans",
  "november": "shorts",
  "december": "shorts"
}

I’ve got to be barking up the wrong tree with my approach, but, using React Instant Search I’ve struggled to figure out the method you outlined above.

Any guidance would be really helpful… thanks!

Hi @markaugias,

Let me recap to be sure we’re on the same page: the records of your index contain a temperature object with the shape presented above and the season list with months. From what I’ve understood you want to build multiple select that depend on each other. The first step is to select the season. Once we’ve access to the value we can indeed use it as an attribute. In the example you gave:

<MenuSelect attribute={`temperature.${highseason}:${temp}`} />

It doesn’t work because we mix the concept of facet and facet value. The temp variable contains the facet value “shorts” it can’t be used as an attribute. What would be the expected list in the select with this attribute? You can use it like this:

<MenuSelect attribute={`temperature.${highseason}`} />

Now the select contains the list of clothes available for the selected month. Here is an example that implement this behavior. We leverage the control mode of React InstantSearch to get access to the current refinements. We use the selected value in the searchState to set the attribute.

The downside of this approach is that it triggers two search requests instead of one. The first request is triggered by the user interaction on the select, the second by the change on the attribute to get the facet values for the second select. We can’t easily avoid it. The alternative is to build your widget with first-class support for refinement dependencies.

Let me know if you have any questions!

Thanks @samuel.vaillant - I really appreciate you taking the time to help me out on this one.

I’ve implemented your setup and have it working really well, apart from one random bug. Sometimes when selecting certain values from the temperature MenuSelect the Season option will be totally removed from the dom. Inspecting the select element show’s no option at all for the chosen season. Oddly, the results reflected remain correct. If I change the temperature select to a different value then boom, the season I had originally selected re-appears in the dom and is shown as selected.

This varies across keys and values… so querying temperature.november:jeans will remove ‘november’ from the dom for the season MenuSelect. In other cases it’ll be temperature.january:shirt removing january from the season select.

Other highseason selections will remain regardless of what temperature you select so as I say, it’s a really random bug.

I’m trying to debug it now but if there’s anything that you think could lead to this please let me know.

Thanks so much!

–edit–

I just tested your setup with our indices and the error occurs there also… same data selections.

Hi!

I’ve tried replicating the issue but didn’t manage to. I must have missed something. Would you mind giving us the exact scenario step by step? It would be helpful to better understand the issue. A video or gif is even better!

Thank you!

I popped you a message outlining some more details with an example… thanks for helping out mate, I really appreciate it.