script-score-query.asciidoc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. [[query-dsl-script-score-query]]
  2. === Script score query
  3. ++++
  4. <titleabbrev>Script score</titleabbrev>
  5. ++++
  6. Uses a <<modules-scripting,script>> to provide a custom score for returned
  7. documents.
  8. The `script_score` query is useful if, for example, a scoring function is expensive and you only need to calculate the score of a filtered set of documents.
  9. [[script-score-query-ex-request]]
  10. ==== Example request
  11. The following `script_score` query assigns each returned document a score equal to the `my-int` field value divided by `10`.
  12. [source,console]
  13. --------------------------------------------------
  14. GET /_search
  15. {
  16. "query": {
  17. "script_score": {
  18. "query": {
  19. "match": { "message": "elasticsearch" }
  20. },
  21. "script": {
  22. "source": "doc['my-int'].value / 10 "
  23. }
  24. }
  25. }
  26. }
  27. --------------------------------------------------
  28. [[script-score-top-level-params]]
  29. ==== Top-level parameters for `script_score`
  30. `query`::
  31. (Required, query object) Query used to return documents.
  32. `script`::
  33. +
  34. --
  35. (Required, <<modules-scripting-using,script object>>) Script used to compute the score of documents returned by the `query`.
  36. IMPORTANT: Final relevance scores from the `script_score` query cannot be
  37. negative. To support certain search optimizations, Lucene requires
  38. scores be positive or `0`.
  39. --
  40. `min_score`::
  41. (Optional, float) Documents with a score lower
  42. than this floating point number are excluded from the search results.
  43. `boost`::
  44. (Optional, float) Documents' scores produced by `script` are
  45. multiplied by `boost` to produce final documents' scores. Defaults to `1.0`.
  46. [[script-score-query-notes]]
  47. ==== Notes
  48. [[script-score-access-scores]]
  49. ===== Use relevance scores in a script
  50. Within a script, you can
  51. {ref}/modules-scripting-fields.html#scripting-score[access]
  52. the `_score` variable which represents the current relevance score of a
  53. document.
  54. [[script-score-predefined-functions]]
  55. ===== Predefined functions
  56. You can use any of the available {painless}/painless-contexts.html[painless
  57. functions] in your `script`. You can also use the following predefined functions
  58. to customize scoring:
  59. * <<script-score-saturation>>
  60. * <<script-score-sigmoid>>
  61. * <<random-score-function>>
  62. * <<decay-functions-numeric-fields>>
  63. * <<decay-functions-geo-fields>>
  64. * <<decay-functions-date-fields>>
  65. * <<script-score-functions-vector-fields>>
  66. We suggest using these predefined functions instead of writing your own.
  67. These functions take advantage of efficiencies from {es}' internal mechanisms.
  68. [[script-score-saturation]]
  69. ====== Saturation
  70. `saturation(value,k) = value/(k + value)`
  71. [source,js]
  72. --------------------------------------------------
  73. "script" : {
  74. "source" : "saturation(doc['my-int'].value, 1)"
  75. }
  76. --------------------------------------------------
  77. // NOTCONSOLE
  78. [[script-score-sigmoid]]
  79. ====== Sigmoid
  80. `sigmoid(value, k, a) = value^a/ (k^a + value^a)`
  81. [source,js]
  82. --------------------------------------------------
  83. "script" : {
  84. "source" : "sigmoid(doc['my-int'].value, 2, 1)"
  85. }
  86. --------------------------------------------------
  87. // NOTCONSOLE
  88. [[random-score-function]]
  89. ====== Random score function
  90. `random_score` function generates scores that are uniformly distributed
  91. from 0 up to but not including 1.
  92. `randomScore` function has the following syntax:
  93. `randomScore(<seed>, <fieldName>)`.
  94. It has a required parameter - `seed` as an integer value,
  95. and an optional parameter - `fieldName` as a string value.
  96. [source,js]
  97. --------------------------------------------------
  98. "script" : {
  99. "source" : "randomScore(100, '_seq_no')"
  100. }
  101. --------------------------------------------------
  102. // NOTCONSOLE
  103. If the `fieldName` parameter is omitted, the internal Lucene
  104. document ids will be used as a source of randomness. This is very efficient,
  105. but unfortunately not reproducible since documents might be renumbered
  106. by merges.
  107. [source,js]
  108. --------------------------------------------------
  109. "script" : {
  110. "source" : "randomScore(100)"
  111. }
  112. --------------------------------------------------
  113. // NOTCONSOLE
  114. Note that documents that are within the same shard and have the
  115. same value for field will get the same score, so it is usually desirable
  116. to use a field that has unique values for all documents across a shard.
  117. A good default choice might be to use the `_seq_no`
  118. field, whose only drawback is that scores will change if the document is
  119. updated since update operations also update the value of the `_seq_no` field.
  120. [[decay-functions-numeric-fields]]
  121. ====== Decay functions for numeric fields
  122. You can read more about decay functions
  123. {ref}/query-dsl-function-score-query.html#function-decay[here].
  124. * `double decayNumericLinear(double origin, double scale, double offset, double decay, double docValue)`
  125. * `double decayNumericExp(double origin, double scale, double offset, double decay, double docValue)`
  126. * `double decayNumericGauss(double origin, double scale, double offset, double decay, double docValue)`
  127. [source,js]
  128. --------------------------------------------------
  129. "script" : {
  130. "source" : "decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['dval'].value)",
  131. "params": { <1>
  132. "origin": 20,
  133. "scale": 10,
  134. "decay" : 0.5,
  135. "offset" : 0
  136. }
  137. }
  138. --------------------------------------------------
  139. // NOTCONSOLE
  140. <1> Using `params` allows to compile the script only once, even if params change.
  141. [[decay-functions-geo-fields]]
  142. ====== Decay functions for geo fields
  143. * `double decayGeoLinear(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)`
  144. * `double decayGeoExp(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)`
  145. * `double decayGeoGauss(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)`
  146. [source,js]
  147. --------------------------------------------------
  148. "script" : {
  149. "source" : "decayGeoExp(params.origin, params.scale, params.offset, params.decay, doc['location'].value)",
  150. "params": {
  151. "origin": "40, -70.12",
  152. "scale": "200km",
  153. "offset": "0km",
  154. "decay" : 0.2
  155. }
  156. }
  157. --------------------------------------------------
  158. // NOTCONSOLE
  159. [[decay-functions-date-fields]]
  160. ====== Decay functions for date fields
  161. * `double decayDateLinear(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)`
  162. * `double decayDateExp(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)`
  163. * `double decayDateGauss(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)`
  164. [source,js]
  165. --------------------------------------------------
  166. "script" : {
  167. "source" : "decayDateGauss(params.origin, params.scale, params.offset, params.decay, doc['date'].value)",
  168. "params": {
  169. "origin": "2008-01-01T01:00:00Z",
  170. "scale": "1h",
  171. "offset" : "0",
  172. "decay" : 0.5
  173. }
  174. }
  175. --------------------------------------------------
  176. // NOTCONSOLE
  177. NOTE: Decay functions on dates are limited to dates in the default format
  178. and default time zone. Also calculations with `now` are not supported.
  179. [[script-score-functions-vector-fields]]
  180. ====== Functions for vector fields
  181. <<vector-functions, Functions for vector fields>> are accessible through
  182. `script_score` query.
  183. ===== Allow expensive queries
  184. Script score queries will not be executed if <<query-dsl-allow-expensive-queries, `search.allow_expensive_queries`>>
  185. is set to false.
  186. [[script-score-faster-alt]]
  187. ===== Faster alternatives
  188. The `script_score` query calculates the score for
  189. every matching document, or hit. There are faster alternative query types that
  190. can efficiently skip non-competitive hits:
  191. * If you want to boost documents on some static fields, use the
  192. <<query-dsl-rank-feature-query, `rank_feature`>> query.
  193. * If you want to boost documents closer to a date or geographic point, use the
  194. <<query-dsl-distance-feature-query, `distance_feature`>> query.
  195. [[script-score-function-score-transition]]
  196. ===== Transition from the function score query
  197. We recommend using the `script_score` query instead of
  198. <<query-dsl-function-score-query, `function_score`>> query for the simplicity
  199. of the `script_score` query.
  200. You can implement the following functions of the `function_score` query using
  201. the `script_score` query:
  202. * <<script-score>>
  203. * <<weight>>
  204. * <<random-score>>
  205. * <<field-value-factor>>
  206. * <<decay-functions>>
  207. [[script-score]]
  208. ====== `script_score`
  209. What you used in `script_score` of the Function Score query, you
  210. can copy into the Script Score query. No changes here.
  211. [[weight]]
  212. ====== `weight`
  213. `weight` function can be implemented in the Script Score query through
  214. the following script:
  215. [source,js]
  216. --------------------------------------------------
  217. "script" : {
  218. "source" : "params.weight * _score",
  219. "params": {
  220. "weight": 2
  221. }
  222. }
  223. --------------------------------------------------
  224. // NOTCONSOLE
  225. [[random-score]]
  226. ====== `random_score`
  227. Use `randomScore` function
  228. as described in <<random-score-function, random score function>>.
  229. [[field-value-factor]]
  230. ====== `field_value_factor`
  231. `field_value_factor` function can be easily implemented through script:
  232. [source,js]
  233. --------------------------------------------------
  234. "script" : {
  235. "source" : "Math.log10(doc['field'].value * params.factor)",
  236. "params" : {
  237. "factor" : 5
  238. }
  239. }
  240. --------------------------------------------------
  241. // NOTCONSOLE
  242. For checking if a document has a missing value, you can use
  243. `doc['field'].size() == 0`. For example, this script will use
  244. a value `1` if a document doesn't have a field `field`:
  245. [source,js]
  246. --------------------------------------------------
  247. "script" : {
  248. "source" : "Math.log10((doc['field'].size() == 0 ? 1 : doc['field'].value()) * params.factor)",
  249. "params" : {
  250. "factor" : 5
  251. }
  252. }
  253. --------------------------------------------------
  254. // NOTCONSOLE
  255. This table lists how `field_value_factor` modifiers can be implemented
  256. through a script:
  257. [cols="<,<",options="header",]
  258. |=======================================================================
  259. | Modifier | Implementation in Script Score
  260. | `none` | -
  261. | `log` | `Math.log10(doc['f'].value)`
  262. | `log1p` | `Math.log10(doc['f'].value + 1)`
  263. | `log2p` | `Math.log10(doc['f'].value + 2)`
  264. | `ln` | `Math.log(doc['f'].value)`
  265. | `ln1p` | `Math.log(doc['f'].value + 1)`
  266. | `ln2p` | `Math.log(doc['f'].value + 2)`
  267. | `square` | `Math.pow(doc['f'].value, 2)`
  268. | `sqrt` | `Math.sqrt(doc['f'].value)`
  269. | `reciprocal` | `1.0 / doc['f'].value`
  270. |=======================================================================
  271. [[decay-functions]]
  272. ====== `decay` functions
  273. The `script_score` query has equivalent <<decay-functions-numeric-fields, decay
  274. functions>> that can be used in scripts.
  275. include::{es-repo-dir}/vectors/vector-functions.asciidoc[]
  276. [[score-explanation]]
  277. ===== Explain request
  278. Using an <<search-explain, explain request>> provides an explanation of how the parts of a score were computed. The `script_score` query can add its own explanation by setting the `explanation` parameter:
  279. [source,console]
  280. --------------------------------------------------
  281. GET /my-index-000001/_explain/0
  282. {
  283. "query": {
  284. "script_score": {
  285. "query": {
  286. "match": { "message": "elasticsearch" }
  287. },
  288. "script": {
  289. "source": """
  290. long count = doc['count'].value;
  291. double normalizedCount = count / 10;
  292. if (explanation != null) {
  293. explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);
  294. }
  295. return normalizedCount;
  296. """
  297. }
  298. }
  299. }
  300. }
  301. --------------------------------------------------
  302. // TEST[setup:my_index]
  303. Note that the `explanation` will be null when using in a normal `_search` request, so having a conditional guard is best practice.