1
votes

Combine elasticsearch bool query with range boost

I have a complex bool query as follows. I use a bogus search term dgbdrtgndgfndrtgb to fabricate my example, which should not match anything.

{
  "from": 0,
  "size": 10,
  "query": {
    "function_score": {
      "boost_mode": "replace",
      "query": {
        "filtered": {
          "filter": {
            # ...
          },
          "query": {
            "bool": {
              "should": [
                {
                  "match": {
                    "name.suggest_ngrams": {
                      "query": "dgbdrtgndgfndrtgb",
                      "fuzziness": "AUTO",
                      "prefix_length": 1,
                      "operator": "AND",
                      "boost": 10
                    }
                  }
                },
                {
                  "multi_match": {
                    "query": "dgbdrtgndgfndrtgb",
                    "fields": [
                      "name.untouched_lowercase"
                    ],
                    "boost": 5
                  }
                },
                {
                  "query_string": {
                    "fields": [
                      "name.suggest"
                    ],
                    "query": "dgbdrtgndgfndrtgb*",
                    "boost": 10
                  }
                },
                {
                  "query_string": {
                    "fields": [
                      "name.suggest"
                    ],
                    "query": "dgbdrtgndgfndrtgb",
                    "boost": 10
                  }
                },
                {
                  "match": {
                    "first_word": {
                      "query": "dgbdrtgndgfndrtgb",
                      "operator": "AND",
                      "boost": 10
                    }
                  }
                },
                {
                  "match": {
                    "name": {
                      "query": "dgbdrtgndgfndrtgb",
                      "operator": "AND",
                      "boost": 5
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}

This works well. Now, for any of those matches, I want to add a boost where the name field has fewer than 2 words. In other words, boost single-word matches or sort them to the top of the result set.

So I tried adding a range boost like this:

{
  "from": 0,
  "size": 10,
  "query": {
    "function_score": {
      "boost_mode": "replace",
      "query": {
        "filtered": {
          "filter": {
            # ...
          },
          "query": {
            "bool": {
              "should": [
                {
                  "match": {
                    "name.suggest_ngrams": {
                      "query": "dgbdrtgndgfndrtgb",
                      "fuzziness": "AUTO",
                      "prefix_length": 1,
                      "operator": "AND",
                      "boost": 10
                    }
                  }
                },
                {
                  "multi_match": {
                    "query": "dgbdrtgndgfndrtgb",
                    "fields": [
                      "name.untouched_lowercase"
                    ],
                    "boost": 5
                  }
                },
                {
                  "query_string": {
                    "fields": [
                      "name.suggest"
                    ],
                    "query": "dgbdrtgndgfndrtgb*",
                    "boost": 10
                  }
                },
                {
                  "query_string": {
                    "fields": [
                      "name.suggest"
                    ],
                    "query": "dgbdrtgndgfndrtgb",
                    "boost": 10
                  }
                },
                {
                  "match": {
                    "first_word": {
                      "query": "dgbdrtgndgfndrtgb",
                      "operator": "AND",
                      "boost": 10
                    }
                  }
                },
                {
                  "match": {
                    "name": {
                      "query": "dgbdrtgndgfndrtgb",
                      "operator": "AND",
                      "boost": 5
                    }
                  }
                },
                { 
                  "range": {
                    "name.word_count": {
                      "lt": 2,
                      "boost": 40
                    }
                  }
                }               
              ]
            }
          }
        }
      }
    }
  }
}

This sorts things like I want, but it also returns single-word matches which do not match the search term dgbdrtgndgfndrtgb.

Is there a way to only boost single-word matches, which also match the search term? I've tried lowering the boost value, which breaks the desired sorting when using a valid (found) search term.

It seems like there should be a way to AND the entire bool query with the range boost. I've tried various permutations to achieve this with no luck and the docs are less than helpful.

One caveat: I cannot use scripting as the index is hosted on AWS which doesn't support it.

Any advice is appreciated.

1

1 Answers

0
votes

After sleeping on the problem, it hit me that it is just Boolean logic. So, I came up with this solution that works perfectly, wherein I wrapped the working query logic in a must tag and put the range boost in a should tag.

{
  "from": 0,
  "size": 10,
  "query": {
    "function_score": {
      "boost_mode": "replace",
      "query": {
        "filtered": {
          "filter": {
            # ...
          },
          "query": {
            "bool": {
              "must": {
                "bool": {
                  "should": [
                    {
                      "match": {
                        "name.suggest_ngrams": {
                          "query": "dgbdrtgndgfndrtgb",
                          "fuzziness": "AUTO",
                          "prefix_length": 1,
                          "operator": "AND",
                          "boost": 10
                        }
                      }
                    },
                    {
                      "multi_match": {
                        "query": "dgbdrtgndgfndrtgb",
                        "fields": [
                          "name.untouched_lowercase"
                        ],
                        "boost": 5
                      }
                    },
                    {
                      "query_string": {
                        "fields": [
                          "name.suggest"
                        ],
                        "query": "dgbdrtgndgfndrtgb*",
                        "boost": 10
                      }
                    },
                    {
                      "query_string": {
                        "fields": [
                          "name.suggest"
                        ],
                        "query": "dgbdrtgndgfndrtgb",
                        "boost": 10
                      }
                    },
                    {
                      "match": {
                        "first_word": {
                          "query": "dgbdrtgndgfndrtgb",
                          "operator": "AND",
                          "boost": 10
                        }
                      }
                    },
                    {
                      "match": {
                        "name": {
                          "query": "dgbdrtgndgfndrtgb",
                          "operator": "AND",
                          "boost": 5
                        }
                      }
                    }
                  ]
                }
              },
              "should": {
                "range": {
                  "name.word_count": {
                    "lt": 2,
                    "boost": 40
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Yay!