-
Notifications
You must be signed in to change notification settings - Fork 13
Hide irrelevant filters when they barely return products #1355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e182917
fa2c81c
a4351e4
9dbeb5e
d269958
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import RangeInput from 'vue-instantsearch/vue3/es/src/components/RangeInput.vue. | |
| import HierarchicalMenu from 'vue-instantsearch/vue3/es/src/components/HierarchicalMenu.vue.js' | ||
| import RefinementList from 'vue-instantsearch/vue3/es/src/components/RefinementList.vue.js' | ||
| import SortBy from 'vue-instantsearch/vue3/es/src/components/SortBy.vue.js' | ||
| import { instantsearchMiddlewares } from '../../stores/useInstantsearchMiddlewares' | ||
|
|
||
| export default { | ||
| mixins: [InstantSearchMixin], | ||
|
|
@@ -59,6 +60,7 @@ export default { | |
| searchClient: null, | ||
| destroyed: false, | ||
| utmFields: [], | ||
| instantSearchInstance: null, | ||
| }), | ||
|
|
||
| render() { | ||
|
|
@@ -127,6 +129,19 @@ export default { | |
| }, | ||
|
|
||
| methods: { | ||
| getMiddlewares() { | ||
| return [ | ||
| ({ instantSearchInstance }) => { | ||
| this.instantSearchInstance = instantSearchInstance | ||
| return { | ||
| onStateChange: () => {}, | ||
| subscribe: () => {}, | ||
| unsubscribe: () => {}, | ||
| } | ||
| }, | ||
| ...instantsearchMiddlewares, | ||
| ] | ||
| }, | ||
| async getInstantSearchClientConfig() { | ||
| const config = await InstantSearchMixin.methods.getInstantSearchClientConfig.bind(this).call() | ||
|
|
||
|
|
@@ -256,6 +271,53 @@ export default { | |
| ...item, | ||
| })) | ||
| }, | ||
|
|
||
| isRelevantFilter(filterItems, minProductPercentage = null) { | ||
| if (!filterItems?.length) { | ||
| return false | ||
| } | ||
|
|
||
| if (filterItems.some((item) => item.isRefined)) { | ||
| return true | ||
| } | ||
|
|
||
| if (!this.instantSearchInstance) { | ||
| return true | ||
| } | ||
| const totalHits = this.instantSearchInstance?.helper?.lastResults?.nbHits | ||
| if (!totalHits) { | ||
| return true | ||
| } | ||
| if (isNaN(minProductPercentage) || minProductPercentage === null) { | ||
| minProductPercentage = window.config.searchkit.min_filter_product_percentage ?? 10 | ||
| } | ||
|
|
||
| const resultCount = filterItems.reduce((sum, item) => item.count + sum, 0) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't appear to take into account range-based filters (as also shown by this change not being present in the range slider filter). This means you will get a ton of range filters and not many of the other filters, which is a specific thing I had to fix in one of our projects.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a function for range filters |
||
|
|
||
| return resultCount / totalHits > minProductPercentage / 100 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something else to be aware of is that it may be worthwhile to have a threshold after which a filter is always shown, regardless of percentage. For example, say you have a category with 5000 products. If 400 of those products have a common filter, it will be excluded from that category, but there's a good likelihood that that filter is actually useful depending on the project. I think it may be worthwhile to either add this as a default functionality, or make an easy to overwrite comparison function that's used at the end here so you can program this yourself without having to overwrite the whole function.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reckon the filter relevancy rules would be especially useful for pages with 5000 products. But for making it easy to overwrite is exactly why the function is structured the way it it. |
||
| }, | ||
| isRelevantRange(filterCode, minProductPercentage = null) { | ||
| if (Object.keys(this.instantSearchInstance?.helper?.state?.numericRefinements?.[filterCode] || {}).length) { | ||
| return true | ||
| } | ||
|
|
||
| if (!this.instantSearchInstance) { | ||
| return true | ||
| } | ||
|
|
||
| const totalHits = this.instantSearchInstance?.helper?.lastResults?.nbHits | ||
| const facetStats = this.instantSearchInstance?.helper?.lastResults?.facets_stats?.[filterCode] | ||
| if (!totalHits || !facetStats) { | ||
| return true | ||
| } | ||
| if (isNaN(minProductPercentage) || minProductPercentage === null) { | ||
| minProductPercentage = window.config.searchkit.min_filter_product_percentage ?? 10 | ||
| } | ||
|
|
||
| const resultCount = facetStats.sum / facetStats.avg | ||
|
|
||
| return resultCount / totalHits > minProductPercentage / 100 | ||
| }, | ||
| }, | ||
| } | ||
| </script> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a middleware to the instantsearch initialization gives us easy access to the instantsearchInstance.
Hacks we've used in the past were
Alternatively custom widgets could be used: https://www.algolia.com/doc/guides/building-search-ui/widgets/create-your-own-widgets/vue
That would mean every filter would need another Vue component wrapping it.
This current method gives us access to the instantSearchInstance within any Listing component.