range-aggregation.asciidoc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. [[search-aggregations-bucket-range-aggregation]]
  2. === Range Aggregation
  3. A multi-bucket value source based aggregation that enables the user to define a set of ranges - each representing a bucket. During the aggregation process, the values extracted from each document will be checked against each bucket range and "bucket" the relevant/matching document.
  4. Note that this aggregation includes the `from` value and excludes the `to` value for each range.
  5. Example:
  6. [source,js]
  7. --------------------------------------------------
  8. GET /_search
  9. {
  10. "aggs" : {
  11. "price_ranges" : {
  12. "range" : {
  13. "field" : "price",
  14. "ranges" : [
  15. { "to" : 100.0 },
  16. { "from" : 100.0, "to" : 200.0 },
  17. { "from" : 200.0 }
  18. ]
  19. }
  20. }
  21. }
  22. }
  23. --------------------------------------------------
  24. // CONSOLE
  25. // TEST[setup:sales]
  26. // TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
  27. Response:
  28. [source,js]
  29. --------------------------------------------------
  30. {
  31. ...
  32. "aggregations": {
  33. "price_ranges" : {
  34. "buckets": [
  35. {
  36. "key": "*-100.0",
  37. "to": 100.0,
  38. "doc_count": 2
  39. },
  40. {
  41. "key": "100.0-200.0",
  42. "from": 100.0,
  43. "to": 200.0,
  44. "doc_count": 2
  45. },
  46. {
  47. "key": "200.0-*",
  48. "from": 200.0,
  49. "doc_count": 3
  50. }
  51. ]
  52. }
  53. }
  54. }
  55. --------------------------------------------------
  56. // TESTRESPONSE[s/\.\.\.//]
  57. ==== Keyed Response
  58. Setting the `keyed` flag to `true` will associate a unique string key with each bucket and return the ranges as a hash rather than an array:
  59. [source,js]
  60. --------------------------------------------------
  61. GET /_search
  62. {
  63. "aggs" : {
  64. "price_ranges" : {
  65. "range" : {
  66. "field" : "price",
  67. "keyed" : true,
  68. "ranges" : [
  69. { "to" : 100 },
  70. { "from" : 100, "to" : 200 },
  71. { "from" : 200 }
  72. ]
  73. }
  74. }
  75. }
  76. }
  77. --------------------------------------------------
  78. // CONSOLE
  79. // TEST[setup:sales]
  80. // TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
  81. Response:
  82. [source,js]
  83. --------------------------------------------------
  84. {
  85. ...
  86. "aggregations": {
  87. "price_ranges" : {
  88. "buckets": {
  89. "*-100.0": {
  90. "to": 100.0,
  91. "doc_count": 2
  92. },
  93. "100.0-200.0": {
  94. "from": 100.0,
  95. "to": 200.0,
  96. "doc_count": 2
  97. },
  98. "200.0-*": {
  99. "from": 200.0,
  100. "doc_count": 3
  101. }
  102. }
  103. }
  104. }
  105. }
  106. --------------------------------------------------
  107. // TESTRESPONSE[s/\.\.\.//]
  108. It is also possible to customize the key for each range:
  109. [source,js]
  110. --------------------------------------------------
  111. GET /_search
  112. {
  113. "aggs" : {
  114. "price_ranges" : {
  115. "range" : {
  116. "field" : "price",
  117. "keyed" : true,
  118. "ranges" : [
  119. { "key" : "cheap", "to" : 100 },
  120. { "key" : "average", "from" : 100, "to" : 200 },
  121. { "key" : "expensive", "from" : 200 }
  122. ]
  123. }
  124. }
  125. }
  126. }
  127. --------------------------------------------------
  128. // CONSOLE
  129. // TEST[setup:sales]
  130. // TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
  131. Response:
  132. [source,js]
  133. --------------------------------------------------
  134. {
  135. ...
  136. "aggregations": {
  137. "price_ranges" : {
  138. "buckets": {
  139. "cheap": {
  140. "to": 100.0,
  141. "doc_count": 2
  142. },
  143. "average": {
  144. "from": 100.0,
  145. "to": 200.0,
  146. "doc_count": 2
  147. },
  148. "expensive": {
  149. "from": 200.0,
  150. "doc_count": 3
  151. }
  152. }
  153. }
  154. }
  155. }
  156. --------------------------------------------------
  157. // TESTRESPONSE[s/\.\.\.//]
  158. ==== Script
  159. Range aggregation accepts a `script` parameter. This parameter allows to defined an inline `script` that
  160. will be executed during aggregation execution.
  161. The following example shows how to use an `inline` script with the `painless` script language and no script parameters:
  162. [source,js]
  163. --------------------------------------------------
  164. GET /_search
  165. {
  166. "aggs" : {
  167. "price_ranges" : {
  168. "range" : {
  169. "script" : {
  170. "lang": "painless",
  171. "source": "doc['price'].value"
  172. },
  173. "ranges" : [
  174. { "to" : 100 },
  175. { "from" : 100, "to" : 200 },
  176. { "from" : 200 }
  177. ]
  178. }
  179. }
  180. }
  181. }
  182. --------------------------------------------------
  183. // CONSOLE
  184. It is also possible to use stored scripts. Here is a simple stored script:
  185. [source,js]
  186. --------------------------------------------------
  187. POST /_scripts/convert_currency
  188. {
  189. "script": {
  190. "lang": "painless",
  191. "source": "doc[params.field].value * params.conversion_rate"
  192. }
  193. }
  194. --------------------------------------------------
  195. // CONSOLE
  196. // TEST[setup:sales]
  197. And this new stored script can be used in the range aggregation like this:
  198. [source,js]
  199. --------------------------------------------------
  200. GET /_search
  201. {
  202. "aggs" : {
  203. "price_ranges" : {
  204. "range" : {
  205. "script" : {
  206. "id": "convert_currency", <1>
  207. "params": { <2>
  208. "field": "price",
  209. "conversion_rate": 0.835526591
  210. }
  211. },
  212. "ranges" : [
  213. { "from" : 0, "to" : 100 },
  214. { "from" : 100 }
  215. ]
  216. }
  217. }
  218. }
  219. }
  220. --------------------------------------------------
  221. // CONSOLE
  222. // TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
  223. // TEST[continued]
  224. <1> Id of the stored script
  225. <2> Parameters to use when executing the stored script
  226. //////////////////////////
  227. [source,js]
  228. --------------------------------------------------
  229. {
  230. "aggregations": {
  231. "price_ranges" : {
  232. "buckets": [
  233. {
  234. "key" : "0.0-100.0",
  235. "from" : 0.0,
  236. "to" : 100.0,
  237. "doc_count" : 2
  238. },
  239. {
  240. "key" : "100.0-*",
  241. "from" : 100.0,
  242. "doc_count" : 5
  243. }
  244. ]
  245. }
  246. }
  247. }
  248. --------------------------------------------------
  249. // TESTRESPONSE
  250. //////////////////////////
  251. ==== Value Script
  252. Lets say the product prices are in USD but we would like to get the price ranges in EURO. We can use value script to convert the prices prior the aggregation (assuming conversion rate of 0.8)
  253. [source,js]
  254. --------------------------------------------------
  255. GET /sales/_search
  256. {
  257. "aggs" : {
  258. "price_ranges" : {
  259. "range" : {
  260. "field" : "price",
  261. "script" : {
  262. "source": "_value * params.conversion_rate",
  263. "params" : {
  264. "conversion_rate" : 0.8
  265. }
  266. },
  267. "ranges" : [
  268. { "to" : 35 },
  269. { "from" : 35, "to" : 70 },
  270. { "from" : 70 }
  271. ]
  272. }
  273. }
  274. }
  275. }
  276. --------------------------------------------------
  277. // CONSOLE
  278. // TEST[setup:sales]
  279. ==== Sub Aggregations
  280. The following example, not only "bucket" the documents to the different buckets but also computes statistics over the prices in each price range
  281. [source,js]
  282. --------------------------------------------------
  283. GET /_search
  284. {
  285. "aggs" : {
  286. "price_ranges" : {
  287. "range" : {
  288. "field" : "price",
  289. "ranges" : [
  290. { "to" : 100 },
  291. { "from" : 100, "to" : 200 },
  292. { "from" : 200 }
  293. ]
  294. },
  295. "aggs" : {
  296. "price_stats" : {
  297. "stats" : { "field" : "price" }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. --------------------------------------------------
  304. // CONSOLE
  305. // TEST[setup:sales]
  306. // TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
  307. Response:
  308. [source,js]
  309. --------------------------------------------------
  310. {
  311. ...
  312. "aggregations": {
  313. "price_ranges": {
  314. "buckets": [
  315. {
  316. "key": "*-100.0",
  317. "to": 100.0,
  318. "doc_count": 2,
  319. "price_stats": {
  320. "count": 2,
  321. "min": 10.0,
  322. "max": 50.0,
  323. "avg": 30.0,
  324. "sum": 60.0
  325. }
  326. },
  327. {
  328. "key": "100.0-200.0",
  329. "from": 100.0,
  330. "to": 200.0,
  331. "doc_count": 2,
  332. "price_stats": {
  333. "count": 2,
  334. "min": 150.0,
  335. "max": 175.0,
  336. "avg": 162.5,
  337. "sum": 325.0
  338. }
  339. },
  340. {
  341. "key": "200.0-*",
  342. "from": 200.0,
  343. "doc_count": 3,
  344. "price_stats": {
  345. "count": 3,
  346. "min": 200.0,
  347. "max": 200.0,
  348. "avg": 200.0,
  349. "sum": 600.0
  350. }
  351. }
  352. ]
  353. }
  354. }
  355. }
  356. --------------------------------------------------
  357. // TESTRESPONSE[s/\.\.\.//]
  358. If a sub aggregation is also based on the same value source as the range aggregation (like the `stats` aggregation in the example above) it is possible to leave out the value source definition for it. The following will return the same response as above:
  359. [source,js]
  360. --------------------------------------------------
  361. GET /_search
  362. {
  363. "aggs" : {
  364. "price_ranges" : {
  365. "range" : {
  366. "field" : "price",
  367. "ranges" : [
  368. { "to" : 100 },
  369. { "from" : 100, "to" : 200 },
  370. { "from" : 200 }
  371. ]
  372. },
  373. "aggs" : {
  374. "price_stats" : {
  375. "stats" : {} <1>
  376. }
  377. }
  378. }
  379. }
  380. }
  381. --------------------------------------------------
  382. // CONSOLE
  383. <1> We don't need to specify the `price` as we "inherit" it by default from the parent `range` aggregation