Search code examples
elasticsearchkibanaelasticsearch-7

Elastic search filter based on array of object


Below is mapping which i have

{
  "defaultBoostValue":1.01,
  "boostDetails": [
    {
      "Type": "Type1",
      "value": 1.0001
    },
    {
      "Type": "Type2",
      "value": 1.002
    },
    {
      "Type": "Type3",
      "value": 1.0005
    }
  ]
}

I want to apply boost type based on type , so if boostType is Type3 then boostFactor should be 1.0005, and if it does not have that boostType, it should apply "defaultBoostValue" as boost below is the query which i have tried

{
  "query": {
    "function_score": {
      "boost_mode": "multiply",
      "functions": [
        {
          "filter": {
            "match": {
              "boostDetails.Type": "Type1"
            }
          },
          "field_value_factor": {
            "field": "boostDetails.value",
            "factor": 1,
            "missing": 1
          }
        }
      ]
    }
  }
}

it is not working as expected as boostDetails is array of object, how can we apply filter on such case


Solution

  • You could use a Script Score function_score query

    {
      "query": {
        "function_score": {
          "boost_mode": "multiply",
          "functions": [
            {
              "filter": {
                "match": {
                  "boostDetails.Type": "Type1"
                }
              },
              "script_score": {
                "script": {
                  "source": """
                    double findBoost(Map params_copy) {
                        for (def group : params_copy._source.boostDetails) {
                            if (group['Type'] == params_copy.preferredBoostType ) {
                                return group['value'];
                            }
                        }
                        return params_copy._source['defaultBoostValue'];
                    }
                    
                    return findBoost(params)
                  """,
                  "params": {
                    "preferredBoostType": "Type1"
                  }
                }
              }
            }
          ]
        }
      }
    }
    

    but keep in mind that if this particular boost type (Type1) does not exist in a given document, the filter of your function_score query will prevent this script from activation -> the defaultBoostValue won't be defaulted to at all.

    So what I'd recommend instead is using a match_all filter instead and of course keeping the preferredBoostType:

    {
      "query": {
        "function_score": {
          "boost_mode": "multiply",
          "functions": [
            {
              "filter": {
                "match_all": {}                          <---
              },
              "script_score": {
                "script": {
                  "source": """
    
                    ...
    
                  """,
                  "params": {
                    "preferredBoostType": "Type1"
                  }
                }
              }
            }
          ]
        }
      }
    }
    

    BTW, if your boostDetails array is not of type nested, you'll probably encounter unexpected and seemingly results as explained here and here.