multivalued-fields.asciidoc 5.3 KB

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