search_request_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package es
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/grafana/grafana/pkg/components/simplejson"
  6. . "github.com/smartystreets/goconvey/convey"
  7. )
  8. func TestSearchRequest(t *testing.T) {
  9. Convey("Test elasticsearch search request", t, func() {
  10. timeField := "@timestamp"
  11. Convey("Given new search request builder for es version 5", func() {
  12. b := NewSearchRequestBuilder(5)
  13. Convey("When building search request", func() {
  14. sr, err := b.Build()
  15. So(err, ShouldBeNil)
  16. Convey("Should have size of zero", func() {
  17. So(sr.Size, ShouldEqual, 0)
  18. })
  19. Convey("Should have no sorting", func() {
  20. So(sr.Sort, ShouldHaveLength, 0)
  21. })
  22. Convey("When marshal to JSON should generate correct json", func() {
  23. body, err := json.Marshal(sr)
  24. So(err, ShouldBeNil)
  25. json, err := simplejson.NewJson([]byte(body))
  26. So(err, ShouldBeNil)
  27. So(json.Get("size").MustInt(500), ShouldEqual, 0)
  28. So(json.Get("sort").Interface(), ShouldBeNil)
  29. So(json.Get("aggs").Interface(), ShouldBeNil)
  30. So(json.Get("query").Interface(), ShouldBeNil)
  31. })
  32. })
  33. Convey("When adding size, sort, filters", func() {
  34. b.Size(200)
  35. b.SortDesc(timeField, "boolean")
  36. filters := b.Query().Bool().Filter()
  37. filters.AddDateRangeFilter(timeField, "$timeTo", "$timeFrom", DateFormatEpochMS)
  38. filters.AddQueryStringFilter("test", true)
  39. Convey("When building search request", func() {
  40. sr, err := b.Build()
  41. So(err, ShouldBeNil)
  42. Convey("Should have correct size", func() {
  43. So(sr.Size, ShouldEqual, 200)
  44. })
  45. Convey("Should have correct sorting", func() {
  46. sort, ok := sr.Sort[timeField].(map[string]string)
  47. So(ok, ShouldBeTrue)
  48. So(sort["order"], ShouldEqual, "desc")
  49. So(sort["unmapped_type"], ShouldEqual, "boolean")
  50. })
  51. Convey("Should have range filter", func() {
  52. f, ok := sr.Query.Bool.Filters[0].(*RangeFilter)
  53. So(ok, ShouldBeTrue)
  54. So(f.Gte, ShouldEqual, "$timeFrom")
  55. So(f.Lte, ShouldEqual, "$timeTo")
  56. So(f.Format, ShouldEqual, "epoch_millis")
  57. })
  58. Convey("Should have query string filter", func() {
  59. f, ok := sr.Query.Bool.Filters[1].(*QueryStringFilter)
  60. So(ok, ShouldBeTrue)
  61. So(f.Query, ShouldEqual, "test")
  62. So(f.AnalyzeWildcard, ShouldBeTrue)
  63. })
  64. Convey("When marshal to JSON should generate correct json", func() {
  65. body, err := json.Marshal(sr)
  66. So(err, ShouldBeNil)
  67. json, err := simplejson.NewJson([]byte(body))
  68. So(err, ShouldBeNil)
  69. So(json.Get("size").MustInt(0), ShouldEqual, 200)
  70. sort := json.GetPath("sort", timeField)
  71. So(sort.Get("order").MustString(), ShouldEqual, "desc")
  72. So(sort.Get("unmapped_type").MustString(), ShouldEqual, "boolean")
  73. timeRangeFilter := json.GetPath("query", "bool", "filter").GetIndex(0).Get("range").Get(timeField)
  74. So(timeRangeFilter.Get("gte").MustString(""), ShouldEqual, "$timeFrom")
  75. So(timeRangeFilter.Get("lte").MustString(""), ShouldEqual, "$timeTo")
  76. So(timeRangeFilter.Get("format").MustString(""), ShouldEqual, DateFormatEpochMS)
  77. queryStringFilter := json.GetPath("query", "bool", "filter").GetIndex(1).Get("query_string")
  78. So(queryStringFilter.Get("analyze_wildcard").MustBool(false), ShouldEqual, true)
  79. So(queryStringFilter.Get("query").MustString(""), ShouldEqual, "test")
  80. })
  81. })
  82. })
  83. Convey("When adding doc value field", func() {
  84. b.AddDocValueField(timeField)
  85. Convey("should set correct props", func() {
  86. So(b.customProps["fields"], ShouldBeNil)
  87. scriptFields, ok := b.customProps["script_fields"].(map[string]interface{})
  88. So(ok, ShouldBeTrue)
  89. So(scriptFields, ShouldHaveLength, 0)
  90. docValueFields, ok := b.customProps["docvalue_fields"].([]string)
  91. So(ok, ShouldBeTrue)
  92. So(docValueFields, ShouldHaveLength, 1)
  93. So(docValueFields[0], ShouldEqual, timeField)
  94. })
  95. Convey("When building search request", func() {
  96. sr, err := b.Build()
  97. So(err, ShouldBeNil)
  98. Convey("When marshal to JSON should generate correct json", func() {
  99. body, err := json.Marshal(sr)
  100. So(err, ShouldBeNil)
  101. json, err := simplejson.NewJson([]byte(body))
  102. So(err, ShouldBeNil)
  103. scriptFields, err := json.Get("script_fields").Map()
  104. So(err, ShouldBeNil)
  105. So(scriptFields, ShouldHaveLength, 0)
  106. _, err = json.Get("fields").StringArray()
  107. So(err, ShouldNotBeNil)
  108. docValueFields, err := json.Get("docvalue_fields").StringArray()
  109. So(err, ShouldBeNil)
  110. So(docValueFields, ShouldHaveLength, 1)
  111. So(docValueFields[0], ShouldEqual, timeField)
  112. })
  113. })
  114. })
  115. Convey("and adding multiple top level aggs", func() {
  116. aggBuilder := b.Agg()
  117. aggBuilder.Terms("1", "@hostname", nil)
  118. aggBuilder.DateHistogram("2", "@timestamp", nil)
  119. Convey("When building search request", func() {
  120. sr, err := b.Build()
  121. So(err, ShouldBeNil)
  122. Convey("Should have 2 top level aggs", func() {
  123. aggs := sr.Aggs
  124. So(aggs, ShouldHaveLength, 2)
  125. So(aggs[0].Key, ShouldEqual, "1")
  126. So(aggs[0].Aggregation.Type, ShouldEqual, "terms")
  127. So(aggs[1].Key, ShouldEqual, "2")
  128. So(aggs[1].Aggregation.Type, ShouldEqual, "date_histogram")
  129. })
  130. Convey("When marshal to JSON should generate correct json", func() {
  131. body, err := json.Marshal(sr)
  132. So(err, ShouldBeNil)
  133. json, err := simplejson.NewJson([]byte(body))
  134. So(err, ShouldBeNil)
  135. So(json.Get("aggs").MustMap(), ShouldHaveLength, 2)
  136. So(json.GetPath("aggs", "1", "terms", "field").MustString(), ShouldEqual, "@hostname")
  137. So(json.GetPath("aggs", "2", "date_histogram", "field").MustString(), ShouldEqual, "@timestamp")
  138. })
  139. })
  140. })
  141. Convey("and adding top level agg with child agg", func() {
  142. aggBuilder := b.Agg()
  143. aggBuilder.Terms("1", "@hostname", func(a *TermsAggregation, ib AggBuilder) {
  144. ib.DateHistogram("2", "@timestamp", nil)
  145. })
  146. Convey("When building search request", func() {
  147. sr, err := b.Build()
  148. So(err, ShouldBeNil)
  149. Convey("Should have 1 top level agg and one child agg", func() {
  150. aggs := sr.Aggs
  151. So(aggs, ShouldHaveLength, 1)
  152. topAgg := aggs[0]
  153. So(topAgg.Key, ShouldEqual, "1")
  154. So(topAgg.Aggregation.Type, ShouldEqual, "terms")
  155. So(topAgg.Aggregation.Aggs, ShouldHaveLength, 1)
  156. childAgg := aggs[0].Aggregation.Aggs[0]
  157. So(childAgg.Key, ShouldEqual, "2")
  158. So(childAgg.Aggregation.Type, ShouldEqual, "date_histogram")
  159. })
  160. Convey("When marshal to JSON should generate correct json", func() {
  161. body, err := json.Marshal(sr)
  162. So(err, ShouldBeNil)
  163. json, err := simplejson.NewJson([]byte(body))
  164. So(err, ShouldBeNil)
  165. So(json.Get("aggs").MustMap(), ShouldHaveLength, 1)
  166. firstLevelAgg := json.GetPath("aggs", "1")
  167. secondLevelAgg := firstLevelAgg.GetPath("aggs", "2")
  168. So(firstLevelAgg.GetPath("terms", "field").MustString(), ShouldEqual, "@hostname")
  169. So(secondLevelAgg.GetPath("date_histogram", "field").MustString(), ShouldEqual, "@timestamp")
  170. })
  171. })
  172. })
  173. Convey("and adding two top level aggs with child agg", func() {
  174. aggBuilder := b.Agg()
  175. aggBuilder.Histogram("1", "@hostname", func(a *HistogramAgg, ib AggBuilder) {
  176. ib.DateHistogram("2", "@timestamp", nil)
  177. })
  178. aggBuilder.Filters("3", func(a *FiltersAggregation, ib AggBuilder) {
  179. ib.Terms("4", "@test", nil)
  180. })
  181. Convey("When building search request", func() {
  182. sr, err := b.Build()
  183. So(err, ShouldBeNil)
  184. Convey("Should have 2 top level aggs with one child agg each", func() {
  185. aggs := sr.Aggs
  186. So(aggs, ShouldHaveLength, 2)
  187. topAggOne := aggs[0]
  188. So(topAggOne.Key, ShouldEqual, "1")
  189. So(topAggOne.Aggregation.Type, ShouldEqual, "histogram")
  190. So(topAggOne.Aggregation.Aggs, ShouldHaveLength, 1)
  191. topAggOnechildAgg := topAggOne.Aggregation.Aggs[0]
  192. So(topAggOnechildAgg.Key, ShouldEqual, "2")
  193. So(topAggOnechildAgg.Aggregation.Type, ShouldEqual, "date_histogram")
  194. topAggTwo := aggs[1]
  195. So(topAggTwo.Key, ShouldEqual, "3")
  196. So(topAggTwo.Aggregation.Type, ShouldEqual, "filters")
  197. So(topAggTwo.Aggregation.Aggs, ShouldHaveLength, 1)
  198. topAggTwochildAgg := topAggTwo.Aggregation.Aggs[0]
  199. So(topAggTwochildAgg.Key, ShouldEqual, "4")
  200. So(topAggTwochildAgg.Aggregation.Type, ShouldEqual, "terms")
  201. })
  202. Convey("When marshal to JSON should generate correct json", func() {
  203. body, err := json.Marshal(sr)
  204. So(err, ShouldBeNil)
  205. json, err := simplejson.NewJson([]byte(body))
  206. So(err, ShouldBeNil)
  207. topAggOne := json.GetPath("aggs", "1")
  208. So(topAggOne.GetPath("histogram", "field").MustString(), ShouldEqual, "@hostname")
  209. topAggOnechildAgg := topAggOne.GetPath("aggs", "2")
  210. So(topAggOnechildAgg.GetPath("date_histogram", "field").MustString(), ShouldEqual, "@timestamp")
  211. topAggTwo := json.GetPath("aggs", "3")
  212. topAggTwochildAgg := topAggTwo.GetPath("aggs", "4")
  213. So(topAggTwo.GetPath("filters").MustArray(), ShouldHaveLength, 0)
  214. So(topAggTwochildAgg.GetPath("terms", "field").MustString(), ShouldEqual, "@test")
  215. })
  216. })
  217. })
  218. Convey("and adding top level agg with child agg with child agg", func() {
  219. aggBuilder := b.Agg()
  220. aggBuilder.Terms("1", "@hostname", func(a *TermsAggregation, ib AggBuilder) {
  221. ib.Terms("2", "@app", func(a *TermsAggregation, ib AggBuilder) {
  222. ib.DateHistogram("3", "@timestamp", nil)
  223. })
  224. })
  225. Convey("When building search request", func() {
  226. sr, err := b.Build()
  227. So(err, ShouldBeNil)
  228. Convey("Should have 1 top level agg with one child having a child", func() {
  229. aggs := sr.Aggs
  230. So(aggs, ShouldHaveLength, 1)
  231. topAgg := aggs[0]
  232. So(topAgg.Key, ShouldEqual, "1")
  233. So(topAgg.Aggregation.Type, ShouldEqual, "terms")
  234. So(topAgg.Aggregation.Aggs, ShouldHaveLength, 1)
  235. childAgg := topAgg.Aggregation.Aggs[0]
  236. So(childAgg.Key, ShouldEqual, "2")
  237. So(childAgg.Aggregation.Type, ShouldEqual, "terms")
  238. childChildAgg := childAgg.Aggregation.Aggs[0]
  239. So(childChildAgg.Key, ShouldEqual, "3")
  240. So(childChildAgg.Aggregation.Type, ShouldEqual, "date_histogram")
  241. })
  242. Convey("When marshal to JSON should generate correct json", func() {
  243. body, err := json.Marshal(sr)
  244. So(err, ShouldBeNil)
  245. json, err := simplejson.NewJson([]byte(body))
  246. So(err, ShouldBeNil)
  247. topAgg := json.GetPath("aggs", "1")
  248. So(topAgg.GetPath("terms", "field").MustString(), ShouldEqual, "@hostname")
  249. childAgg := topAgg.GetPath("aggs", "2")
  250. So(childAgg.GetPath("terms", "field").MustString(), ShouldEqual, "@app")
  251. childChildAgg := childAgg.GetPath("aggs", "3")
  252. So(childChildAgg.GetPath("date_histogram", "field").MustString(), ShouldEqual, "@timestamp")
  253. })
  254. })
  255. })
  256. Convey("and adding bucket and metric aggs", func() {
  257. aggBuilder := b.Agg()
  258. aggBuilder.Terms("1", "@hostname", func(a *TermsAggregation, ib AggBuilder) {
  259. ib.Terms("2", "@app", func(a *TermsAggregation, ib AggBuilder) {
  260. ib.Metric("4", "avg", "@value", nil)
  261. ib.DateHistogram("3", "@timestamp", func(a *DateHistogramAgg, ib AggBuilder) {
  262. ib.Metric("4", "avg", "@value", nil)
  263. ib.Metric("5", "max", "@value", nil)
  264. })
  265. })
  266. })
  267. Convey("When building search request", func() {
  268. sr, err := b.Build()
  269. So(err, ShouldBeNil)
  270. Convey("Should have 1 top level agg with one child having a child", func() {
  271. aggs := sr.Aggs
  272. So(aggs, ShouldHaveLength, 1)
  273. topAgg := aggs[0]
  274. So(topAgg.Key, ShouldEqual, "1")
  275. So(topAgg.Aggregation.Type, ShouldEqual, "terms")
  276. So(topAgg.Aggregation.Aggs, ShouldHaveLength, 1)
  277. childAgg := topAgg.Aggregation.Aggs[0]
  278. So(childAgg.Key, ShouldEqual, "2")
  279. So(childAgg.Aggregation.Type, ShouldEqual, "terms")
  280. childChildOneAgg := childAgg.Aggregation.Aggs[0]
  281. So(childChildOneAgg.Key, ShouldEqual, "4")
  282. So(childChildOneAgg.Aggregation.Type, ShouldEqual, "avg")
  283. childChildTwoAgg := childAgg.Aggregation.Aggs[1]
  284. So(childChildTwoAgg.Key, ShouldEqual, "3")
  285. So(childChildTwoAgg.Aggregation.Type, ShouldEqual, "date_histogram")
  286. childChildTwoChildOneAgg := childChildTwoAgg.Aggregation.Aggs[0]
  287. So(childChildTwoChildOneAgg.Key, ShouldEqual, "4")
  288. So(childChildTwoChildOneAgg.Aggregation.Type, ShouldEqual, "avg")
  289. childChildTwoChildTwoAgg := childChildTwoAgg.Aggregation.Aggs[1]
  290. So(childChildTwoChildTwoAgg.Key, ShouldEqual, "5")
  291. So(childChildTwoChildTwoAgg.Aggregation.Type, ShouldEqual, "max")
  292. })
  293. Convey("When marshal to JSON should generate correct json", func() {
  294. body, err := json.Marshal(sr)
  295. So(err, ShouldBeNil)
  296. json, err := simplejson.NewJson([]byte(body))
  297. So(err, ShouldBeNil)
  298. termsAgg := json.GetPath("aggs", "1")
  299. So(termsAgg.GetPath("terms", "field").MustString(), ShouldEqual, "@hostname")
  300. termsAggTwo := termsAgg.GetPath("aggs", "2")
  301. So(termsAggTwo.GetPath("terms", "field").MustString(), ShouldEqual, "@app")
  302. termsAggTwoAvg := termsAggTwo.GetPath("aggs", "4")
  303. So(termsAggTwoAvg.GetPath("avg", "field").MustString(), ShouldEqual, "@value")
  304. dateHistAgg := termsAggTwo.GetPath("aggs", "3")
  305. So(dateHistAgg.GetPath("date_histogram", "field").MustString(), ShouldEqual, "@timestamp")
  306. avgAgg := dateHistAgg.GetPath("aggs", "4")
  307. So(avgAgg.GetPath("avg", "field").MustString(), ShouldEqual, "@value")
  308. maxAgg := dateHistAgg.GetPath("aggs", "5")
  309. So(maxAgg.GetPath("max", "field").MustString(), ShouldEqual, "@value")
  310. })
  311. })
  312. })
  313. })
  314. Convey("Given new search request builder for es version 2", func() {
  315. b := NewSearchRequestBuilder(2)
  316. Convey("When adding doc value field", func() {
  317. b.AddDocValueField(timeField)
  318. Convey("should set correct props", func() {
  319. fields, ok := b.customProps["fields"].([]string)
  320. So(ok, ShouldBeTrue)
  321. So(fields, ShouldHaveLength, 2)
  322. So(fields[0], ShouldEqual, "*")
  323. So(fields[1], ShouldEqual, "_source")
  324. scriptFields, ok := b.customProps["script_fields"].(map[string]interface{})
  325. So(ok, ShouldBeTrue)
  326. So(scriptFields, ShouldHaveLength, 0)
  327. fieldDataFields, ok := b.customProps["fielddata_fields"].([]string)
  328. So(ok, ShouldBeTrue)
  329. So(fieldDataFields, ShouldHaveLength, 1)
  330. So(fieldDataFields[0], ShouldEqual, timeField)
  331. })
  332. Convey("When building search request", func() {
  333. sr, err := b.Build()
  334. So(err, ShouldBeNil)
  335. Convey("When marshal to JSON should generate correct json", func() {
  336. body, err := json.Marshal(sr)
  337. So(err, ShouldBeNil)
  338. json, err := simplejson.NewJson([]byte(body))
  339. So(err, ShouldBeNil)
  340. scriptFields, err := json.Get("script_fields").Map()
  341. So(err, ShouldBeNil)
  342. So(scriptFields, ShouldHaveLength, 0)
  343. fields, err := json.Get("fields").StringArray()
  344. So(err, ShouldBeNil)
  345. So(fields, ShouldHaveLength, 2)
  346. So(fields[0], ShouldEqual, "*")
  347. So(fields[1], ShouldEqual, "_source")
  348. fieldDataFields, err := json.Get("fielddata_fields").StringArray()
  349. So(err, ShouldBeNil)
  350. So(fieldDataFields, ShouldHaveLength, 1)
  351. So(fieldDataFields[0], ShouldEqual, timeField)
  352. })
  353. })
  354. })
  355. })
  356. })
  357. }
  358. func TestMultiSearchRequest(t *testing.T) {
  359. Convey("Test elasticsearch multi search request", t, func() {
  360. Convey("Given new multi search request builder", func() {
  361. b := NewMultiSearchRequestBuilder(0)
  362. Convey("When adding one search request", func() {
  363. b.Search()
  364. Convey("When building search request should contain one search request", func() {
  365. mr, err := b.Build()
  366. So(err, ShouldBeNil)
  367. So(mr.Requests, ShouldHaveLength, 1)
  368. })
  369. })
  370. Convey("When adding two search requests", func() {
  371. b.Search()
  372. b.Search()
  373. Convey("When building search request should contain two search requests", func() {
  374. mr, err := b.Build()
  375. So(err, ShouldBeNil)
  376. So(mr.Requests, ShouldHaveLength, 2)
  377. })
  378. })
  379. })
  380. })
  381. }