Skip to content
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

Allowing user to exclude facets from search using refinementList #1232

Closed
billvieux opened this issue Sep 1, 2016 · 19 comments
Closed

Allowing user to exclude facets from search using refinementList #1232

billvieux opened this issue Sep 1, 2016 · 19 comments
Labels
Feedback Automatically sends feedback to the Product team (do not rename) Type: Question

Comments

@billvieux
Copy link

I want to support a scenario where a user can exclude tags from the search. Ideally, it would be using the existing refinementList (or extending it).

Here is a CodePen that basically behaves I want. For example, click the red 'x' next to 'Used' to exclude records tagged 'Used'.
http://codepen.io/billvieux/pen/QKWWqy

However, this is a terrible implementation. I modified the refinementList item template to include a link in the label along with the tag name. Then I get the tag name during the click:
$(document).on('click','.ais-refinement-list--label a', function(e){ search.helper.addFacetRefinement('tags', '-' + $(e.target)[0].dataset.tag); search.helper.search(); });

I had to use the -tagname syntax with addFacetRefinement to ensure that the tag was removed from the refinementList. When I tried to use addFacetExclusion the hits were correct, but the refinementList and currentRefinementValues widgets didn't work as expected.

I also had to keep the link inside the <label> as moving it outside the label seemed to always result in both the tagname and -tagname refinements being added. (I saw refinementList has to do some work as-is to avoid double responding to a click.)

Cons: This uses a global document bind to watch for click events, has a small target area to click for exclusions (and that link can't be outside the <label>, and currently allows the user to select a tag & then exclude it, thus getting 0 search results.

For a scenario like this, I would really welcome any suggestions on better ways to approach the implementation or achieve this kind of behavior.

@bobylito
Copy link
Contributor

bobylito commented Sep 1, 2016

Hi @billvieux

Thanks for posting this issue. And your codepen is very helpful too :)

This kind of behaviour should be in the API as it is kinda tricky to implement over existing widgets. Maybe have an option to be able to activate this kind of exclusion link when using and as an operator? WDYT @vvo

@vvo
Copy link
Contributor

vvo commented Sep 1, 2016

Yes it could be in the API (the instantsearch.js API), but should a refinementList widget allows users to both refine and remove (exclude)? The codepen demo has both actions in the same widget, it's powerful but is it easy to understand?

@billvieux
Copy link
Author

billvieux commented Sep 1, 2016

Thanks for quick response!

In my scenario, I could live with a 2nd refinementList widget on the page that called addFacetExclusion for any facet that was checked. In some ways that could be more confusing for the user. It certainly takes more screen real estate as I would have both a "Tags" and an "Excluded Tags" refinementList being displayed.

In my codepen implementation, I use a currentRefinedValues widget, so the user can see what has been removed. In an instantsearch.js API-level implementation (in the refinementList), I would think you'd want to implement this feature in a way that wouldn't require the currentRefinedValues widget for the user to keep track of exclusions. Ideally, I think the excluded facet would still show in the refinementList, but support being styled differently (e.g., strikethrough).

Do you have any suggestions / concerns about they way I used -tagname syntax with addFacetRefinement instead of directly using addFacetExclusion? I wasn't seeing the expected behavior when I used addFacetExclusion.

@bobylito
Copy link
Contributor

bobylito commented Sep 2, 2016

AddFacetExclusion is using the - syntax are you: https://github.com/algolia/algoliasearch-helper-js/blob/develop/src/requestBuilder.js#L188-L192 . So this is weird to experience a different behaviour.

The way you did the prototype is not really orthodox :) And leads to some weird behaviour. This is likely to be cause by the way this widget is implemented. There are two alternatives here, either you create a proper custom widget or you wait for such a feature to be implemented, given our bandwidth and the complexity of making a nice API for that, I'm not sure that we will implement it in the coming days.

I'm pretty confident that you can make a straightforward refinement list custom widget that will fit your need.

@bobylito bobylito closed this as completed Sep 2, 2016
@billvieux
Copy link
Author

Thanks @bobylito. Regarding the custom widget - is there anyway recommended way to extend the refinementList widget? It seems a shame to copy that entire widget just to support an addFacetExclusion mode. Also, the current custom widget documentation is pretty sparse for building something as complicated as a refinementList widget from scratch.

@vvo
Copy link
Contributor

vvo commented Sep 2, 2016

@billvieux I detailed a bit more doing a custom widget in another thread: #868 (comment).

Once you get the three methods, the helper and you are able to do a bit of JS/jQuery you should be good.

Ask any other question in other issues about building custom widgets if you have.

Thanks!

@xPaw
Copy link

xPaw commented Oct 30, 2017

@billvieux What solution did you end up with? I am currently facing the same problem.

@billvieux
Copy link
Author

Not the cleanest, but I just added another link next to the refinement with a red 'x' and when clicked, it excludes that refinement.

When creating the widget, I specify the item template as follows

    templates: {
        item: '<label class="{{cssClasses.label}}">' +
        '<input type="checkbox" class="{{cssClasses.checkbox}}" value="{{value}}" {{#isRefined}}checked{{/isRefined}} />{{value}} {{#helpers.formatExclusion}}{{value}}{{/helpers.formatExclusion}}' +
        '<span class="{{cssClasses.count}}">{{#helpers.formatNumber}}{{count}}{{/helpers.formatNumber}}</span></label>'
      }

My formatExclusion helper adds the link with a data attribute storing the name of the tag

search.templatesConfig.helpers.formatExclusion = function(text, render) {
    return ' <a class="ais-refinement-list--exclude" href="#" data-tag="' + text + '">x</a> '
  };

When that link is clicked, I pull the data attribute and add a facet refinement and re-do the search.

  $(document).on('click','.ais-refinement-list--label a', function(e) {
    search.helper.addFacetRefinement('_tags', '-' + $(e.target)[0].dataset.tag);
    search.helper.search();
  });

I also have a currentRefinedValues widget with a custom item template that can display the word NOT in front of excluded refinements.

Looks like this:
image

@xPaw
Copy link

xPaw commented Oct 30, 2017

@billvieux I tried doing what you're doing in codepen, but I think something changed in instantsearch v2 and clicking on label gets caught before your custom click listener, and thus it does not work :(

@billvieux
Copy link
Author

My solution requires clicking directly on the red x to exclude. Clicking on the label just selects the label for inclusion. My scenario required that users be able to choose to include or exclude. I'm using v2.

@xPaw
Copy link

xPaw commented Oct 30, 2017

Yeah I was clicking on the 'x', it still didn't catch the click, not sure why. I basically have the same scenario as you do.

@Haroenv
Copy link
Contributor

Haroenv commented Oct 31, 2017

Can you open a new issue with the problems you have @xPaw

@xPaw
Copy link

xPaw commented Nov 26, 2019

I decided to look at this again, and there's still no easy way of achieving this.

Can you re-open this issue because I think refinementList should have a proper way of excluding items.

@Haroenv
Copy link
Contributor

Haroenv commented Nov 26, 2019

Hi @xPaw, this issue is very old, and I have lost the context. Do you want to prevent an item from being shown (if so, filter in transformItems), or it still to be shown, but not refine (map in transformItems where you remove the 'value' on the item)

@xPaw
Copy link

xPaw commented Nov 26, 2019

I want to exclude items from search results (refine with -)

For example, if you look on https://steamdb.info/sales/ and click 'Filter by user tags', you can see [+] and [-]. Right now refinementList acts only as a [+] when refining, and there's no way of excluding items from the search results.

The example above doesn't use Algolia, you can see the refinement widget here: https://steamdb.info/instantsearch/

@Haroenv
Copy link
Contributor

Haroenv commented Nov 26, 2019

ah okay, exclusion in this way. You can create the custom UI with connectRefinementList, and call either refine(value) or refine('-'+value)

An example here does something similar, although only excludes, not a mix https://codesandbox.io/s/4xvz9wmj00

@xPaw
Copy link

xPaw commented Nov 26, 2019

I think I can get it working with the example, yeah. As I understand it, refine toggles, but is there a way to disable/enable refiniment instead of just toggling based on current state?

Also, do you think it's worth investigating whether this feature should be added to instantsearch.js? I think it's an important filtering mechanism.

@Haroenv Haroenv added the Feedback Automatically sends feedback to the Product team (do not rename) label Nov 27, 2019
@Haroenv
Copy link
Contributor

Haroenv commented Nov 27, 2019

Can you open a fresh issue with the full use case you have? Old issues like this aren't really monitored. thanks!

@xPaw
Copy link

xPaw commented Jan 30, 2022

Can you open a fresh issue with the full use case you have?

I think this issue explains the use case well, no reason to duplicate it. Can you simply reopen it?

Here's another issue I ran into with my exclusions widget that I think would be possible to solve if implemented properly:

If you want to do OR searches, but make exclusions still work, they would have to be applied as AND. For example -windows AND (macos OR linux).

EDIT: Forgot I made a discussion for this: #4754

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feedback Automatically sends feedback to the Product team (do not rename) Type: Question
Projects
None yet
Development

No branches or pull requests

5 participants