Script score query

Use a script_score query to customize the score calculation by using a script. For an expensive scoring function, you can use a script_score query to calculate the score only for the returned documents that have been filtered.

Example

For example, the following request creates an index containing one document:

  1. PUT testindex1/_doc/1
  2. {
  3. "name": "John Doe",
  4. "multiplier": 0.5
  5. }

copy

You can use a match query to return all documents that contain John in the name field:

  1. GET testindex1/_search
  2. {
  3. "query": {
  4. "match": {
  5. "name": "John"
  6. }
  7. }
  8. }

copy

In the response, document 1 has a score of 0.2876821:

  1. {
  2. "took": 7,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 1,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.2876821,
  16. "hits": [
  17. {
  18. "_index": "testindex1",
  19. "_id": "1",
  20. "_score": 0.2876821,
  21. "_source": {
  22. "name": "John Doe",
  23. "multiplier": 0.5
  24. }
  25. }
  26. ]
  27. }
  28. }

Now let’s change the document score by using a script that calculates the score as the value of the _score field multiplied by the value of the multiplier field. In the following query, you can access the current relevance score of a document in the _score variable and the multiplier value as doc['multiplier'].value:

  1. GET testindex1/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": {
  7. "name": "John"
  8. }
  9. },
  10. "script": {
  11. "source": "_score * doc['multiplier'].value"
  12. }
  13. }
  14. }
  15. }

copy

In the response, the score for document 1 is half of the original score:

  1. {
  2. "took": 8,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 1,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.14384104,
  16. "hits": [
  17. {
  18. "_index": "testindex1",
  19. "_id": "1",
  20. "_score": 0.14384104,
  21. "_source": {
  22. "name": "John Doe",
  23. "multiplier": 0.5
  24. }
  25. }
  26. ]
  27. }
  28. }

Parameters

The script_score query supports the following top-level parameters.

ParameterData typeDescription
queryObjectThe query used for search. Required.
scriptObjectThe script used to calculate the score of the documents returned by the query. Required.
min_scoreFloatExcludes documents with a score lower than min_score from the results. Optional.
boostFloatBoosts the documents’ scores by the given multiplier. Values less than 1.0 decrease relevance, and values greater than 1.0 increase relevance. Default is 1.0.

The relevance scores calculated by the script_score query cannot be negative.

Customizing score calculation with built-in functions

To customize score calculation, you can use one of the built-in Painless functions. For every function, OpenSearch provides one or more Painless methods you can access in the script score context. You can call the Painless methods listed in the following sections directly without using a class name or instance name qualifier.

Saturation

The saturation function calculates saturation as score = value /(value + pivot), where value is the field value and pivot is chosen so that the score is greater than 0.5 if value is greater than pivot and less than 0.5 if value is less than pivot. The score is in the (0, 1) range. To apply a saturation function, call the following Painless method:

  • double saturation(double <field-value>, double <pivot>)

Example

The following example query searches for the text neural search in the articles index. It combines the original document relevance score with the article_rank value, which is first transformed with a saturation function:

  1. GET articles/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": { "article_name": "neural search" }
  7. },
  8. "script" : {
  9. "source" : "_score + saturation(doc['article_rank'].value, 11)"
  10. }
  11. }
  12. }
  13. }

copy

Sigmoid

Similarly to the saturation function, the sigmoid function calculates the score as score = value^exp/ (value^exp + pivot^exp), where value is the field value, exp is an exponent scaling factor, and pivot is chosen so that the score is greater than 0.5 if value is greater than pivot and less than 0.5 if value is less than pivot. To apply a sigmoid function, call the following Painless method:

  • double sigmoid(double <field-value>, double <pivot>, double <exp>)

Example

The following example query searches for the text neural search in the articles index. It combines the original document relevance score with the article_rank value, which is first transformed with a sigmoid function:

  1. GET articles/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": { "article_name": "neural search" }
  7. },
  8. "script" : {
  9. "source" : "_score + sigmoid(doc['article_rank'].value, 11, 2)"
  10. }
  11. }
  12. }
  13. }

copy

Random score

The random score function generates uniformly distributed random scores in the [0, 1) range. To learn how the function works, see The random score function. To apply a random score function, call one of the following Painless methods:

  • double randomScore(int <seed>): Uses the internal Lucene document IDs as seed values.
  • double randomScore(int <seed>, String <field-name>)

Example

The following query uses the random_score function with a seed and a field:

  1. GET articles/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": { "article_name": "neural search" }
  7. },
  8. "script" : {
  9. "source" : "randomScore(20, '_seq_no')"
  10. }
  11. }
  12. }
  13. }

copy

Decay functions

With decay functions, you can score results based on proximity or recency. To learn more, see Decay functions. You can calculate scores using an exponential, Gaussian, or linear decay curve. To apply a decay function, call one of the following Painless methods, depending on the field type:

  • Numeric fields:
    • double decayNumericGauss(double <origin>, double <scale>, double <offset>, double <decay>, double <field-value>)
    • double decayNumericExp(double <origin>, double <scale>, double <offset>, double <decay>, double <field-value>)
    • double decayNumericLinear(double <origin>, double <scale>, double <offset>, double <decay>, double <field-value>)
  • Geopoint fields:
    • double decayGeoGauss(String <origin>, String <scale>, String <offset>, double <decay>, GeoPoint <field-value>)
    • double decayGeoExp(String <origin>, String <scale>, String <offset>, double <decay>, GeoPoint <field-value>)
    • double decayGeoLinear(String <origin>, String <scale>, String <offset>, double <decay>, GeoPoint <field-value>)
  • Date fields:
    • double decayDateGauss(String <origin>, String <scale>, String <offset>, double <decay>, JodaCompatibleZonedDateTime <field-value>)
    • double decayDateExp(String <origin>, String <scale>, String <offset>, double <decay>, JodaCompatibleZonedDateTime <field-value>)
    • double decayDateLinear(String <origin>, String <scale>, String <offset>, double <decay>, JodaCompatibleZonedDateTime <field-value>)

Example: Numeric fields

The following query uses the exponential decay function on a numeric field:

  1. GET articles/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": {
  7. "article_name": "neural search"
  8. }
  9. },
  10. "script": {
  11. "source": "decayNumericExp(params.origin, params.scale, params.offset, params.decay, doc['article_rank'].value)",
  12. "params": {
  13. "origin": 50,
  14. "scale": 20,
  15. "offset": 30,
  16. "decay": 0.5
  17. }
  18. }
  19. }
  20. }
  21. }

copy

Example: Geopoint fields

The following query uses the Gaussian decay function on a geopoint field:

  1. GET hotels/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": {
  7. "name": "hotel"
  8. }
  9. },
  10. "script": {
  11. "source": "decayGeoGauss(params.origin, params.scale, params.offset, params.decay, doc['location'].value)",
  12. "params": {
  13. "origin": "40.71,74.00",
  14. "scale": "300ft",
  15. "offset": "200ft",
  16. "decay": 0.25
  17. }
  18. }
  19. }
  20. }
  21. }

copy

Example: Date fields

The following query uses the linear decay function on a date field:

  1. GET blogs/_search
  2. {
  3. "query": {
  4. "script_score": {
  5. "query": {
  6. "match": {
  7. "name": "opensearch"
  8. }
  9. },
  10. "script": {
  11. "source": "decayDateLinear(params.origin, params.scale, params.offset, params.decay, doc['date_posted'].value)",
  12. "params": {
  13. "origin": "2022-04-24",
  14. "scale": "6d",
  15. "offset": "1d",
  16. "decay": 0.25
  17. }
  18. }
  19. }
  20. }
  21. }

copy

Term frequency functions

Term frequency functions expose term-level statistics in the score script source. You can use these statistics to implement custom information retrieval and ranking algorithms, like query-time multiplicative or additive score boosting by popularity. To apply a term frequency function, call one of the following Painless methods:

  • int termFreq(String <field-name>, String <term>): Retrieves the term frequency within a field for a specific term.
  • long totalTermFreq(String <field-name>, String <term>): Retrieves the total term frequency within a field for a specific term.
  • long sumTotalTermFreq(String <field-name>): Retrieves the sum of total term frequencies within a field.

Example

The following query calculates the score as the total term frequency for each field in the fields list multiplied by the multiplier value:

  1. GET /demo_index_v1/_search
  2. {
  3. "query": {
  4. "function_score": {
  5. "query": {
  6. "match_all": {}
  7. },
  8. "script_score": {
  9. "script": {
  10. "source": """
  11. for (int x = 0; x < params.fields.length; x++) {
  12. String field = params.fields[x];
  13. if (field != null) {
  14. return params.multiplier * totalTermFreq(field, params.term);
  15. }
  16. }
  17. return params.default_value;
  18. """,
  19. "params": {
  20. "fields": ["title", "description"],
  21. "term": "ai",
  22. "multiplier": 2,
  23. "default_value": 1
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }

copy

If search.allow_expensive_queries is set to false, script_score queries are not executed.