简体   繁体   中英

ElasticSearch filtering on multiple fields (with aggregration)

I am building a faceted filtering function for a webshop, something like this:

Filter on Brand:
[ ] LG (10)
[ ] Apple (5)
[ ] HTC (3)

Filter on OS:
[ ] Android 4 (11)
[ ] Android 5 (2)
[ ] IOS (5)

I am using aggregation and filtering in elasticsearch, which is working out pretty well for me after a few days of learning ES (loving it!). But sadly I got stuck on the actual filtering now.

If i click on 'LG', the IOS filter will be disabled and (5) will change to (0) and the results on the right side will change to 13 android phones. Great, so far so good.

Now if I click on 'Android 4', only 11 phones will show on the right side. Awesome! So far so good :)

But now, if i click on 'Android 5', all results disappear. I'm not sure what I'm doing wrong. I would expect that all LG phones with both Android 4 and 5 show up.

Below is a sample query of the last case. Please note there are also some other fields included in the query which I am using to build the faceted filtering.

{
   "size":100,
   "query":{
      "filtered":{
         "query":{
            "match_all":[

            ]
         },
         "filter":{
            "bool":{
               "must":[
                  {
                     "term":{
                        "brand.untouched":"LG"
                     }
                  },
                  {
                     "term":{
                        "operating_system.untouched":"Android 4"
                     }
                  },
                  {
                     "term":{
                        "operating_system.untouched":"Android 5"
                     }
                  }
               ],
               "should":[

               ],
               "must_not":{
                  "missing":{
                     "field":"model"
                  }
               }
            }
         },
         "strategy":"query_first"
      }
   },
   "aggs":{
      "brand.untouched":{
         "terms":{
            "field":"brand.untouched"
         }
      },
      "operating_system.untouched":{
         "terms":{
            "field":"operating_system.untouched"
         }
      },
      "camera1":{
         "histogram":{
            "field":"camera1",
            "interval":5,
            "min_doc_count":0
         }
      },
      "price_seperate":{
         "histogram":{
            "field":"price_seperate",
            "interval":125,
            "min_doc_count":0
         }
      }
   }
}

Does anyone know the solution? Thanks so much.

Your query is searching for documents in which operating_system.untouched is both "Android 4" and "Android 5" which will never be the case and hence you get zero results. You can simply make use of Terms Filter so that documents where the value of operating_system.untouched is either "Android 4" or "Android 5" matches. Below is the updated query you should be using:

{
   "size":100,
   "query":{
      "filtered":{
         "filter":{
            "bool":{
               "must":[
                  {
                     "terms":{
                        "brand.untouched": [
                            "LG"
                        ]
                     }
                  },
                  {
                     "terms":{
                        "operating_system.untouched": [
                            "Android 4",
                            "Android 5"
                        ]
                     }
                  }
               ],
               "must_not":{
                  "missing":{
                     "field":"model"
                  }
               }
            }
         },
         "strategy":"query_first"
      }
   },
   "aggs":{
      "brand.untouched":{
         "terms":{
            "field":"brand.untouched"
         }
      },
      "operating_system.untouched":{
         "terms":{
            "field":"operating_system.untouched"
         }
      },
      "camera1":{
         "histogram":{
            "field":"camera1",
            "interval":5,
            "min_doc_count":0
         }
      },
      "price_seperate":{
         "histogram":{
            "field":"price_seperate",
            "interval":125,
            "min_doc_count":0
         }
      }
   }
}

If you want to add another set of categories like price range, you just need to add a bool should clause inside the bool must clause. See below for an example when you want to filter on a field price on two ranges (0, 100] and (100, 200] . What this basically means is that you can have nested must and should filters to realize any boolean logic you want to implement for filtering in Elasticsearch.

... 
"must":[
    {
        "terms":{
            "brand.untouched": [
                "LG"
            ]
        }
    },
    {
        "terms":{
            "operating_system.untouched": [
               "Android 4",
               "Android 5"
            ]
        }
    },
    "bool": {
        "should": [
            {
                "range": {
                    "price": {
                        "gt": 0,
                        "lte": 100
                    }
                }
            },
            {
                "range": {
                    "price": {
                        "gt": 100,
                        "lte": 200
                    }
                }
            }
        ]
    }
],
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM