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 `likes` 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['likes'].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['likes'].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['likes'].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 are deprecating the <<query-dsl-function-score-query, `function_score`>>
  198. query. We recommend using the `script_score` query instead.
  199. You can implement the following functions from the `function_score` query using
  200. the `script_score` query:
  201. * <<script-score>>
  202. * <<weight>>
  203. * <<random-score>>
  204. * <<field-value-factor>>
  205. * <<decay-functions>>
  206. [[script-score]]
  207. ====== `script_score`
  208. What you used in `script_score` of the Function Score query, you
  209. can copy into the Script Score query. No changes here.
  210. [[weight]]
  211. ====== `weight`
  212. `weight` function can be implemented in the Script Score query through
  213. the following script:
  214. [source,js]
  215. --------------------------------------------------
  216. "script" : {
  217. "source" : "params.weight * _score",
  218. "params": {
  219. "weight": 2
  220. }
  221. }
  222. --------------------------------------------------
  223. // NOTCONSOLE
  224. [[random-score]]
  225. ====== `random_score`
  226. Use `randomScore` function
  227. as described in <<random-score-function, random score function>>.
  228. [[field-value-factor]]
  229. ====== `field_value_factor`
  230. `field_value_factor` function can be easily implemented through script:
  231. [source,js]
  232. --------------------------------------------------
  233. "script" : {
  234. "source" : "Math.log10(doc['field'].value * params.factor)",
  235. "params" : {
  236. "factor" : 5
  237. }
  238. }
  239. --------------------------------------------------
  240. // NOTCONSOLE
  241. For checking if a document has a missing value, you can use
  242. `doc['field'].size() == 0`. For example, this script will use
  243. a value `1` if a document doesn't have a field `field`:
  244. [source,js]
  245. --------------------------------------------------
  246. "script" : {
  247. "source" : "Math.log10((doc['field'].size() == 0 ? 1 : doc['field'].value()) * params.factor)",
  248. "params" : {
  249. "factor" : 5
  250. }
  251. }
  252. --------------------------------------------------
  253. // NOTCONSOLE
  254. This table lists how `field_value_factor` modifiers can be implemented
  255. through a script:
  256. [cols="<,<",options="header",]
  257. |=======================================================================
  258. | Modifier | Implementation in Script Score
  259. | `none` | -
  260. | `log` | `Math.log10(doc['f'].value)`
  261. | `log1p` | `Math.log10(doc['f'].value + 1)`
  262. | `log2p` | `Math.log10(doc['f'].value + 2)`
  263. | `ln` | `Math.log(doc['f'].value)`
  264. | `ln1p` | `Math.log(doc['f'].value + 1)`
  265. | `ln2p` | `Math.log(doc['f'].value + 2)`
  266. | `square` | `Math.pow(doc['f'].value, 2)`
  267. | `sqrt` | `Math.sqrt(doc['f'].value)`
  268. | `reciprocal` | `1.0 / doc['f'].value`
  269. |=======================================================================
  270. [[decay-functions]]
  271. ====== `decay` functions
  272. The `script_score` query has equivalent <<decay-functions, decay functions>>
  273. that can be used in script.
  274. include::{es-repo-dir}/vectors/vector-functions.asciidoc[]
  275. [[score-explanation]]
  276. ====== Explain request
  277. 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:
  278. [source,console]
  279. --------------------------------------------------
  280. GET /twitter/_explain/0
  281. {
  282. "query": {
  283. "script_score": {
  284. "query": {
  285. "match": { "message": "elasticsearch" }
  286. },
  287. "script": {
  288. "source": """
  289. long likes = doc['likes'].value;
  290. double normalizedLikes = likes / 10;
  291. if (explanation != null) {
  292. explanation.set('normalized likes = likes / 10 = ' + likes + ' / 10 = ' + normalizedLikes);
  293. }
  294. return normalizedLikes;
  295. """
  296. }
  297. }
  298. }
  299. }
  300. --------------------------------------------------
  301. // TEST[setup:twitter]
  302. Note that the `explanation` will be null when using in a normal `_search` request, so having a conditional guard is best practice.