HotStuff68
HotStuff68

Reputation: 965

Modifying elasticsearch score based on nested field value

I want to modify scoring in ElasticSearch (v2+) based on the weight of a field in a nested object within an array.

For instance, using this data:

PUT index/test/0
{
    "name": "red bell pepper",
    "words": [
        {"text": "pepper", "weight": 20},
        {"text": "bell","weight": 10},
        {"text": "red","weight": 5}
    ]
}

PUT index/test/1
{
    "name": "hot red pepper",
    "words": [
        {"text": "pepper", "weight": 15},
        {"text": "hot","weight": 11},
        {"text": "red","weight": 5}
    ]
}

I want a query like {"words.text": "red pepper"} which would rank "red bell pepper" above "hot red pepper".

The way I am thinking about this problem is "first match the 'text' field, then modify scoring based on the 'weight' field". Unfortunately I don't know how to achieve this, if it's even possible, or if I have the right approach for something like this.

If proposing alternative approach, please try and keep a generalized idea where there are tons of different similar cases (eg: simply modifying the "red bell pepper" document score to be higher isn't really a suitable alternative).

Upvotes: 7

Views: 2625

Answers (1)

keety
keety

Reputation: 17461

The approach you have in mind is feasible. It can be achieved via function score in a nested query .

An example implementation is shown below :

PUT test

PUT test/test/_mapping
{
   "properties": {
      "name": {
         "type": "string"
      },
      "words": {
         "type": "nested",
         "properties": {
            "text": {
               "type": "string"
            },
            "weight": {
               "type": "long"
            }
         }
      }
   }
}


PUT test/test/0
{
    "name": "red bell pepper",
    "words": [
        {"text": "pepper", "weight": 20},
        {"text": "bell","weight": 10},
        {"text": "red","weight": 5}
    ]
}
PUT test/test/1
{
    "name": "hot red pepper",
    "words": [
        {"text": "pepper", "weight": 15},
        {"text": "hot","weight": 11},
        {"text": "red","weight": 5}
    ]
}

post test/_search
{
   "query": {
      "bool": {
         "disable_coord": true,
         "must": [
            {
               "match": {
                  "name": "red pepper"
               }
            }
         ],
         "should": [
            {
               "nested": {
                  "path": "words",
                  "query": {
                     "function_score": {
                        "functions": [
                           {
                              "field_value_factor": {
                                "field" : "words.weight",
                                "missing": 0
                              }
                           }
                        ],
                        "query": {
                           "match": {
                              "words.text": "red pepper"
                           }
                        },
                        "score_mode": "sum",
                        "boost_mode": "replace"
                     }
                  },
                  "score_mode": "total"
               }
            }
         ]
      }
   }
}

Result :

 "hits": [
         {
            "_index": "test",
            "_type": "test",
            "_id": "0",
            "_score": 26.030865,
            "_source": {
               "name": "red bell pepper",
               "words": [
                  {
                     "text": "pepper",
                     "weight": 20
                  },
                  {
                     "text": "bell",
                     "weight": 10
                  },
                  {
                     "text": "red",
                     "weight": 5
                  }
               ]
            }
         },
         {
            "_index": "test",
            "_type": "test",
            "_id": "1",
            "_score": 21.030865,
            "_source": {
               "name": "hot red pepper",
               "words": [
                  {
                     "text": "pepper",
                     "weight": 15
                  },
                  {
                     "text": "hot",
                     "weight": 11
                  },
                  {
                     "text": "red",
                     "weight": 5
                  }
               ]
            }
         }
      ]
   }

The query in a nutshell would score a document that satisfies the must clause as follows : sum up the weights of the matched nested documents with the score of the must clause.

Upvotes: 7

Related Questions