multivalued-fields.asciidoc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. [[esql-multivalued-fields]]
  2. === {esql} multivalued fields
  3. ++++
  4. <titleabbrev>Multivalued fields</titleabbrev>
  5. ++++
  6. {esql} is fine reading from multivalued fields:
  7. [source,console,id=esql-multivalued-fields-reorders]
  8. ----
  9. POST /mv/_bulk?refresh
  10. { "index" : {} }
  11. { "a": 1, "b": [2, 1] }
  12. { "index" : {} }
  13. { "a": 2, "b": 3 }
  14. POST /_query
  15. {
  16. "query": "FROM mv | LIMIT 2"
  17. }
  18. ----
  19. Multivalued fields come back as a JSON array:
  20. [source,console-result]
  21. ----
  22. {
  23. "took": 28,
  24. "is_partial": false,
  25. "columns": [
  26. { "name": "a", "type": "long"},
  27. { "name": "b", "type": "long"}
  28. ],
  29. "values": [
  30. [1, [1, 2]],
  31. [2, 3]
  32. ]
  33. }
  34. ----
  35. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  36. The relative order of values in a multivalued field is undefined. They'll frequently be in
  37. ascending order but don't rely on that.
  38. [discrete]
  39. [[esql-multivalued-fields-dups]]
  40. ==== Duplicate values
  41. Some field types, like <<keyword-field-type,`keyword`>> remove duplicate values on write:
  42. [source,console,id=esql-multivalued-fields-kwdups]
  43. ----
  44. PUT /mv
  45. {
  46. "mappings": {
  47. "properties": {
  48. "b": {"type": "keyword"}
  49. }
  50. }
  51. }
  52. POST /mv/_bulk?refresh
  53. { "index" : {} }
  54. { "a": 1, "b": ["foo", "foo", "bar"] }
  55. { "index" : {} }
  56. { "a": 2, "b": ["bar", "bar"] }
  57. POST /_query
  58. {
  59. "query": "FROM mv | LIMIT 2"
  60. }
  61. ----
  62. And {esql} sees that removal:
  63. [source,console-result]
  64. ----
  65. {
  66. "took": 28,
  67. "is_partial": false,
  68. "columns": [
  69. { "name": "a", "type": "long"},
  70. { "name": "b", "type": "keyword"}
  71. ],
  72. "values": [
  73. [1, ["bar", "foo"]],
  74. [2, "bar"]
  75. ]
  76. }
  77. ----
  78. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  79. But other types, like `long` don't remove duplicates.
  80. [source,console,id=esql-multivalued-fields-longdups]
  81. ----
  82. PUT /mv
  83. {
  84. "mappings": {
  85. "properties": {
  86. "b": {"type": "long"}
  87. }
  88. }
  89. }
  90. POST /mv/_bulk?refresh
  91. { "index" : {} }
  92. { "a": 1, "b": [2, 2, 1] }
  93. { "index" : {} }
  94. { "a": 2, "b": [1, 1] }
  95. POST /_query
  96. {
  97. "query": "FROM mv | LIMIT 2"
  98. }
  99. ----
  100. And {esql} also sees that:
  101. [source,console-result]
  102. ----
  103. {
  104. "took": 28,
  105. "is_partial": false,
  106. "columns": [
  107. { "name": "a", "type": "long"},
  108. { "name": "b", "type": "long"}
  109. ],
  110. "values": [
  111. [1, [1, 2, 2]],
  112. [2, [1, 1]]
  113. ]
  114. }
  115. ----
  116. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  117. This is all at the storage layer. If you store duplicate `long`s and then
  118. convert them to strings the duplicates will stay:
  119. [source,console,id=esql-multivalued-fields-longdups-tostring]
  120. ----
  121. PUT /mv
  122. {
  123. "mappings": {
  124. "properties": {
  125. "b": {"type": "long"}
  126. }
  127. }
  128. }
  129. POST /mv/_bulk?refresh
  130. { "index" : {} }
  131. { "a": 1, "b": [2, 2, 1] }
  132. { "index" : {} }
  133. { "a": 2, "b": [1, 1] }
  134. POST /_query
  135. {
  136. "query": "FROM mv | EVAL b=TO_STRING(b) | LIMIT 2"
  137. }
  138. ----
  139. [source,console-result]
  140. ----
  141. {
  142. "took": 28,
  143. "is_partial": false,
  144. "columns": [
  145. { "name": "a", "type": "long"},
  146. { "name": "b", "type": "keyword"}
  147. ],
  148. "values": [
  149. [1, ["1", "2", "2"]],
  150. [2, ["1", "1"]]
  151. ]
  152. }
  153. ----
  154. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  155. [discrete]
  156. [[esql-multivalued-nulls]]
  157. ==== `null` in a list
  158. `null` values in a list are not preserved at the storage layer:
  159. [source,console,id=esql-multivalued-fields-multivalued-nulls]
  160. ----
  161. POST /mv/_doc?refresh
  162. { "a": [2, null, 1] }
  163. POST /_query
  164. {
  165. "query": "FROM mv | LIMIT 1"
  166. }
  167. ----
  168. [source,console-result]
  169. ----
  170. {
  171. "took": 28,
  172. "is_partial": false,
  173. "columns": [
  174. { "name": "a", "type": "long"},
  175. ],
  176. "values": [
  177. [[1, 2]],
  178. ]
  179. }
  180. ----
  181. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  182. [discrete]
  183. [[esql-multivalued-fields-functions]]
  184. ==== Functions
  185. Unless otherwise documented functions will return `null` when applied to a multivalued
  186. field.
  187. [source,console,id=esql-multivalued-fields-mv-into-null]
  188. ----
  189. POST /mv/_bulk?refresh
  190. { "index" : {} }
  191. { "a": 1, "b": [2, 1] }
  192. { "index" : {} }
  193. { "a": 2, "b": 3 }
  194. ----
  195. [source,console]
  196. ----
  197. POST /_query
  198. {
  199. "query": "FROM mv | EVAL b + 2, a + b | LIMIT 4"
  200. }
  201. ----
  202. // TEST[continued]
  203. // TEST[warning:Line 1:16: evaluation of [b + 2] failed, treating result as null. Only first 20 failures recorded.]
  204. // TEST[warning:Line 1:16: java.lang.IllegalArgumentException: single-value function encountered multi-value]
  205. // TEST[warning:Line 1:23: evaluation of [a + b] failed, treating result as null. Only first 20 failures recorded.]
  206. // TEST[warning:Line 1:23: java.lang.IllegalArgumentException: single-value function encountered multi-value]
  207. [source,console-result]
  208. ----
  209. {
  210. "took": 28,
  211. "is_partial": false,
  212. "columns": [
  213. { "name": "a", "type": "long"},
  214. { "name": "b", "type": "long"},
  215. { "name": "b + 2", "type": "long"},
  216. { "name": "a + b", "type": "long"}
  217. ],
  218. "values": [
  219. [1, [1, 2], null, null],
  220. [2, 3, 5, 5]
  221. ]
  222. }
  223. ----
  224. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]
  225. Work around this limitation by converting the field to single value with one of:
  226. * <<esql-mv_avg>>
  227. * <<esql-mv_concat>>
  228. * <<esql-mv_count>>
  229. * <<esql-mv_max>>
  230. * <<esql-mv_median>>
  231. * <<esql-mv_min>>
  232. * <<esql-mv_sum>>
  233. [source,console,esql-multivalued-fields-mv-into-null]
  234. ----
  235. POST /_query
  236. {
  237. "query": "FROM mv | EVAL b=MV_MIN(b) | EVAL b + 2, a + b | LIMIT 4"
  238. }
  239. ----
  240. // TEST[continued]
  241. [source,console-result]
  242. ----
  243. {
  244. "took": 28,
  245. "is_partial": false,
  246. "columns": [
  247. { "name": "a", "type": "long"},
  248. { "name": "b", "type": "long"},
  249. { "name": "b + 2", "type": "long"},
  250. { "name": "a + b", "type": "long"}
  251. ],
  252. "values": [
  253. [1, 1, 3, 2],
  254. [2, 3, 5, 5]
  255. ]
  256. }
  257. ----
  258. // TESTRESPONSE[s/"took": 28/"took": "$body.took"/]