YousefED / ElasticUI

AngularJS directives for Elasticsearch
http://www.elasticui.com
Other
526 stars 135 forks source link

Multiple selection filter #62

Open devads opened 8 years ago

devads commented 8 years ago

I have a problem when I tried to use aggs with multiple selection.

I have a list

<ul id="mySelect" eui-aggregation="ejs.TermsAggregation('brand').field('brand.raw').order('_term','ASC')" eui-filter-self="false">
    <li class="col s12" ng-repeat="bucket in aggResult.buckets">
        <label class="checkbox" eui-filter="ejs.TermsFilter('brand.raw', bucket.key)">
            <input type="checkbox" id="brand{{bucket.key}}" ng-model="filter.enabled">
            <label for="brand{{bucket.key}}">{{bucket.key}}</label>
        </label>
    </li>                 
</ul>

and when I choose several items from list, filters looks like this "filter":{"bool":{"must":[{"terms":{"brand.raw":["Brand 1"]}},{"terms":{"brand.raw":["Brand 2"]}}]}}

but nothing is returned from ES, for working it must be like that "filter":{"bool":{"must":[{"terms":{"brand.raw":["Brand 1","Brand 2"]}}]}}

Do you think it's possible ?

Thanks.

devads commented 8 years ago

I tried with eui-or-filter and it works well ! But there are some problems with filters selected but hide when you choose others filters. Is it possible to extract filters selected for deleting them ? Thanks

YousefED commented 8 years ago

I think what you are looking for is eui-filter-self="false":

https://github.com/YousefED/ElasticUI/blob/master/docs/components.md

devads commented 8 years ago

Thanks for your response, like you can see in my first comment, I already use eui-filter-self="false" but it's not the problem. Example : I have two TermsAggregation with TermsFilter, one for brand and another for model. If I choose two brands (Kia and Honda) and in models I choose (Picanto) if I unselected Kia from brand list, I always have Picanto in results from elastic but it's hidden in the select (because I unselected Kia). So the results are wrong.

I hope this is more understanding.

Thanks,

YousefED commented 8 years ago

Ah. If I understand you correctly you're selecting a filter, which later gets removed from the interface after another selection changes.

When the filter is removed from the interface, you can't "unselected" it anymore, so it stays active in Elasticsearch


I think a good solution would be to make sure the filter is removed automatically when the interface element is removed.

We should be able to achieve this by watching "this.scope.$on('$destroy'" in FilterController, similar to what's already in AggregationController.

Please let me know whether my analysis seems ok

devads commented 8 years ago

Yes, it's exactly this.

I already tried to modify FilterController init by watching "this.scope.$on('$destroy'" but without success

this.scope.$on('$destroy', function() {
    if (_this.scope.filter.enabled)
        _this.scope.filters.remove(_this.scope.filter.filter);
});

It removes all filters even if they are yet in the interface.

If you can help me on this. Thanks @YousefED

YousefED commented 8 years ago

Hmm your code looks fine to me. If you could provide a simple JSFiddle / online example I'd love to debug it (for example, change the demo)

devads commented 8 years ago

Of course no problem.

There is the fiddle http://jsfiddle.net/gonado/1hLr6kzd/20/

And maybe you need the index example for ES

PUT /cars_ex/

PUT cars_ex/transactions/_mapping/
{
  "transactions": {
   "properties": {
    "brand": {
      "type": "string",
      "fields": {
        "raw":   { "type": "string", "index": "not_analyzed" }
      }},
     "model": {
      "type": "string",
      "fields": {
        "raw":   { "type": "string", "index": "not_analyzed" }
      }}
    }
  }
}
}

POST /cars_ex/transactions/_bulk
{ "index": {}}
{ "price" : 12100, "title" : "Honda Civic 1.6 120ch i-DTEC Business", "brand" : "Honda", "model" : "Civic" }
{ "index": {}}
{ "price" : 15290, "title" : "Honda CR-V 1.6 i-DTEC 160 Exclusive Navi 4WD AT", "brand" : "Honda", "model" : "CR-V" }
{ "index": {}}
{ "price" : 18000, "title" : "BMW SERIE 1 114D 95CH LOUNGE START EDITION 5P", "brand" : "BMW", "model" : "Série 1" }
{ "index": {}}
{ "price" : 25000, "title" : "BMW SÉRIE 3 316DA 116CH LOUNGE START EDITION", "brand" : "BMW", "model" : "Série 3" }
{ "index": {}}
{ "price" : 7990, "title" : "OPEL KARL 1.0 75CH ESSENTIA", "brand" : "Opel", "model" : "Karl" }
{ "index": {}}
{ "price" : 8990, "title" : "OPEL CORSA 1.2 70CH ESSENTIA 3P", "brand" : "Opel", "model" : "Corsa" }
{ "index": {}}
{ "price" : 14990, "title" : "KIA CEED 1.4 CRDI 90CH MOTION", "brand" : "Kia", "model" : "Ceed" }
{ "index": {}}
{ "price" : 22490, "title" : "KIA CARENS 1.7 CRDI 115CH ACTIVE ISG 5 PLACES", "brand" : "Kia", "model" : "Carens" }
{ "index": {}}
{ "price" : 25900, "title" : "VOLKSWAGEN GOLF SPORTSVAN", "brand" : "Volkswagen", "model" : "Golf Sportsvan" }

Thanks !

devads commented 8 years ago

The modification is here

FilterController.prototype.init = function () {
                ...
                this.scope.$on('$destroy', function() {
                    if (_this.scope.filter.enabled)
                        _this.scope.filters.remove(_this.scope.filter.filter);
                });
            };
YousefED commented 8 years ago

Thanks a lot for this. The solution so far doesn't work because the filters always get destroyed and recreated. This makes sense, but unfortunately this means a quick-fix is not readily available (requires more architectural changes).

Another solution might be to create something similar to eui-filter-self="false", but then eui-never-filter="true". This would mean an aggregation (in this case "brands") would never be filtered (would need to update AggregationController.getAggregation()


A quick workaround for your scenario would be to just statically define the brands, instead of via an aggregation. for example something like this for every brand:

<label class="checkbox" eui-filter="ejs.TermsFilter('model.raw', 'BMW')">
            <input type="checkbox" ng-model="filter.enabled">
            BMW
        </label>

Hope this helps for now

devads commented 8 years ago

Thanks for your response !

I've already implemented the eui-never-filter, it works great if you want to keep all brands visible. But you always have the problem with filters get destroyed and present in ES.

This is always a big problem for my scenario, maybe I have to use elastic.js only, but I like to use elasticui, it's so easy to use :-)

Do you think you will make theses changes in the future to adapt elasticui ?

Thanks.

YousefED commented 8 years ago

What is the problem you keep having after implementing this ("always have the problem with filters get destroyed)?

At this moment, I don't have any plans myself to implement this unfortunately.

devads commented 8 years ago

Ok no problem I understand !

Implementing the eui-never-filter keep well all brands even if you choose a model, but If you have a model checked and you unchecked a brand of this model it keeps this model in ES, so it's not working !

I have to find a way to do it.

Thanks for you time.