query_test.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. package elasticsearch
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/grafana/grafana/pkg/components/simplejson"
  6. "github.com/grafana/grafana/pkg/models"
  7. "github.com/grafana/grafana/pkg/tsdb"
  8. . "github.com/smartystreets/goconvey/convey"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "testing"
  13. )
  14. func testElasticSearchResponse(requestJSON string, expectedElasticSearchRequestJSON string) {
  15. var queryExpectedJSONInterface, queryJSONInterface interface{}
  16. parser := ElasticSearchQueryParser{}
  17. model := &Query{}
  18. err := json.Unmarshal([]byte(requestJSON), model)
  19. So(err, ShouldBeNil)
  20. jsonDate, _ := simplejson.NewJson([]byte(`{"esVersion":2}`))
  21. dsInfo := &models.DataSource{
  22. Database: "grafana-test",
  23. JsonData: jsonDate,
  24. }
  25. testTimeRange := tsdb.NewTimeRange("5m", "now")
  26. req, _ := simplejson.NewJson([]byte(requestJSON))
  27. query, err := parser.Parse(req, dsInfo)
  28. s, err := query.Build(&tsdb.TsdbQuery{TimeRange: testTimeRange}, dsInfo)
  29. queryJSON := strings.Split(s, "\n")[1]
  30. err = json.Unmarshal([]byte(queryJSON), &queryJSONInterface)
  31. So(err, ShouldBeNil)
  32. expectedElasticSearchRequestJSON = strings.Replace(
  33. expectedElasticSearchRequestJSON,
  34. "<FROM_TIMESTAMP>",
  35. strconv.FormatInt(testTimeRange.GetFromAsMsEpoch(), 10),
  36. -1,
  37. )
  38. expectedElasticSearchRequestJSON = strings.Replace(
  39. expectedElasticSearchRequestJSON,
  40. "<TO_TIMESTAMP>",
  41. strconv.FormatInt(testTimeRange.GetToAsMsEpoch(), 10),
  42. -1,
  43. )
  44. err = json.Unmarshal([]byte(expectedElasticSearchRequestJSON), &queryExpectedJSONInterface)
  45. So(err, ShouldBeNil)
  46. result := reflect.DeepEqual(queryExpectedJSONInterface, queryJSONInterface)
  47. if !result {
  48. fmt.Printf("ERROR: %s \n != \n %s", expectedElasticSearchRequestJSON, queryJSON)
  49. }
  50. So(result, ShouldBeTrue)
  51. }
  52. func TestElasticSearchQueryBuilder(t *testing.T) {
  53. Convey("Elasticsearch QueryBuilder query testing", t, func() {
  54. Convey("Build test average metric with moving average", func() {
  55. var testElasticsearchModelRequestJSON = `
  56. {
  57. "bucketAggs": [
  58. {
  59. "field": "timestamp",
  60. "id": "2",
  61. "settings": {
  62. "interval": "auto",
  63. "min_doc_count": 0,
  64. "trimEdges": 0
  65. },
  66. "type": "date_histogram"
  67. }
  68. ],
  69. "dsType": "elasticsearch",
  70. "metrics": [
  71. {
  72. "field": "value",
  73. "id": "1",
  74. "inlineScript": "_value * 2",
  75. "meta": {},
  76. "settings": {
  77. "script": {
  78. "inline": "_value * 2"
  79. }
  80. },
  81. "type": "avg"
  82. },
  83. {
  84. "field": "1",
  85. "id": "3",
  86. "meta": {},
  87. "pipelineAgg": "1",
  88. "settings": {
  89. "minimize": false,
  90. "model": "simple",
  91. "window": 5
  92. },
  93. "type": "moving_avg"
  94. }
  95. ],
  96. "query": "(test:query) AND (name:sample)",
  97. "refId": "A",
  98. "timeField": "timestamp"
  99. }
  100. `
  101. var expectedElasticsearchQueryJSON = `
  102. {
  103. "size": 0,
  104. "query": {
  105. "bool": {
  106. "filter": [
  107. {
  108. "range": {
  109. "timestamp": {
  110. "gte": "<FROM_TIMESTAMP>",
  111. "lte": "<TO_TIMESTAMP>",
  112. "format": "epoch_millis"
  113. }
  114. }
  115. },
  116. {
  117. "query_string": {
  118. "analyze_wildcard": true,
  119. "query": "(test:query) AND (name:sample)"
  120. }
  121. }
  122. ]
  123. }
  124. },
  125. "aggs": {
  126. "2": {
  127. "date_histogram": {
  128. "interval": "200ms",
  129. "field": "timestamp",
  130. "min_doc_count": 0,
  131. "extended_bounds": {
  132. "min": "<FROM_TIMESTAMP>",
  133. "max": "<TO_TIMESTAMP>"
  134. },
  135. "format": "epoch_millis"
  136. },
  137. "aggs": {
  138. "1": {
  139. "avg": {
  140. "field": "value",
  141. "script": {
  142. "inline": "_value * 2"
  143. }
  144. }
  145. },
  146. "3": {
  147. "moving_avg": {
  148. "buckets_path": "1",
  149. "window": 5,
  150. "model": "simple",
  151. "minimize": false
  152. }
  153. }
  154. }
  155. }
  156. }
  157. }`
  158. testElasticSearchResponse(testElasticsearchModelRequestJSON, expectedElasticsearchQueryJSON)
  159. })
  160. Convey("Test Wildcards and Quotes", func() {
  161. testElasticsearchModelRequestJSON := `
  162. {
  163. "alias": "New",
  164. "bucketAggs": [
  165. {
  166. "field": "timestamp",
  167. "id": "2",
  168. "type": "date_histogram"
  169. }
  170. ],
  171. "dsType": "elasticsearch",
  172. "metrics": [
  173. {
  174. "type": "sum",
  175. "field": "value",
  176. "id": "1"
  177. }
  178. ],
  179. "query": "scope:$location.leagueconnect.api AND name:*CreateRegistration AND name:\"*.201-responses.rate\"",
  180. "refId": "A",
  181. "timeField": "timestamp"
  182. }`
  183. expectedElasticsearchQueryJSON := `
  184. {
  185. "size": 0,
  186. "query": {
  187. "bool": {
  188. "filter": [
  189. {
  190. "range": {
  191. "timestamp": {
  192. "gte": "<FROM_TIMESTAMP>",
  193. "lte": "<TO_TIMESTAMP>",
  194. "format": "epoch_millis"
  195. }
  196. }
  197. },
  198. {
  199. "query_string": {
  200. "analyze_wildcard": true,
  201. "query": "scope:$location.leagueconnect.api AND name:*CreateRegistration AND name:\"*.201-responses.rate\""
  202. }
  203. }
  204. ]
  205. }
  206. },
  207. "aggs": {
  208. "2": {
  209. "aggs": {
  210. "1": {
  211. "sum": {
  212. "field": "value"
  213. }
  214. }
  215. },
  216. "date_histogram": {
  217. "extended_bounds": {
  218. "max": "<TO_TIMESTAMP>",
  219. "min": "<FROM_TIMESTAMP>"
  220. },
  221. "field": "timestamp",
  222. "format": "epoch_millis",
  223. "min_doc_count": 0
  224. }
  225. }
  226. }
  227. }`
  228. testElasticSearchResponse(testElasticsearchModelRequestJSON, expectedElasticsearchQueryJSON)
  229. })
  230. Convey("Test Term Aggregates", func() {
  231. testElasticsearchModelRequestJSON := `
  232. {
  233. "bucketAggs": [{
  234. "field": "name_raw",
  235. "id": "4",
  236. "settings": {
  237. "order": "desc",
  238. "orderBy": "_term",
  239. "size": "10"
  240. },
  241. "type": "terms"
  242. }, {
  243. "field": "timestamp",
  244. "id": "2",
  245. "settings": {
  246. "interval": "1m",
  247. "min_doc_count": 0,
  248. "trimEdges": 0
  249. },
  250. "type": "date_histogram"
  251. }],
  252. "dsType": "elasticsearch",
  253. "filters": [{
  254. "boolOp": "AND",
  255. "not": false,
  256. "type": "rfc190Scope",
  257. "value": "*.hmp.metricsd"
  258. }, {
  259. "boolOp": "AND",
  260. "not": false,
  261. "type": "name_raw",
  262. "value": "builtin.general.*_instance_count"
  263. }],
  264. "metricObject": {},
  265. "metrics": [{
  266. "field": "value",
  267. "id": "1",
  268. "meta": {},
  269. "options": {},
  270. "settings": {},
  271. "type": "sum"
  272. }],
  273. "mode": 0,
  274. "numToGraph": 10,
  275. "prependHostName": false,
  276. "query": "(scope:*.hmp.metricsd) AND (name_raw:builtin.general.*_instance_count)",
  277. "refId": "A",
  278. "regexAlias": false,
  279. "selectedApplication": "",
  280. "selectedHost": "",
  281. "selectedLocation": "",
  282. "timeField": "timestamp",
  283. "useFullHostName": "",
  284. "useQuery": false
  285. }`
  286. expectedElasticsearchQueryJSON := `
  287. {
  288. "size": 0,
  289. "query": {
  290. "bool": {
  291. "filter": [
  292. {
  293. "range": {
  294. "timestamp": {
  295. "gte": "<FROM_TIMESTAMP>",
  296. "lte": "<TO_TIMESTAMP>",
  297. "format": "epoch_millis"
  298. }
  299. }
  300. },
  301. {
  302. "query_string": {
  303. "analyze_wildcard": true,
  304. "query": "(scope:*.hmp.metricsd) AND (name_raw:builtin.general.*_instance_count)"
  305. }
  306. }
  307. ]
  308. }
  309. },
  310. "aggs": {"4":{"aggs":{"2":{"aggs":{"1":{"sum":{"field":"value"}}},"date_histogram":{"extended_bounds":{"max":"<TO_TIMESTAMP>","min":"<FROM_TIMESTAMP>"},"field":"timestamp","format":"epoch_millis","interval":"1m","min_doc_count":0}}},"terms":{"field":"name_raw","order":{"_term":"desc"},"size":10}}}
  311. }`
  312. testElasticSearchResponse(testElasticsearchModelRequestJSON, expectedElasticsearchQueryJSON)
  313. })
  314. Convey("Test Filters Aggregates", func() {
  315. testElasticsearchModelRequestJSON := `
  316. {
  317. "bucketAggs": [
  318. {
  319. "id": "3",
  320. "settings": {
  321. "filters": [{
  322. "label": "hello",
  323. "query": "host:\"67.65.185.232\""
  324. }]
  325. },
  326. "type": "filters"
  327. },
  328. {
  329. "field": "time",
  330. "id": "2",
  331. "settings": {
  332. "interval": "auto",
  333. "min_doc_count": 0,
  334. "trimEdges": 0
  335. },
  336. "type": "date_histogram"
  337. }
  338. ],
  339. "metrics": [
  340. {
  341. "pipelineAgg": "select metric",
  342. "field": "bytesSent",
  343. "id": "1",
  344. "meta": {},
  345. "settings": {},
  346. "type": "count"
  347. }
  348. ],
  349. "query": "*",
  350. "refId": "A",
  351. "timeField": "time"
  352. }`
  353. expectedElasticsearchQueryJSON := `{
  354. "size": 0,
  355. "query": {
  356. "bool": {
  357. "filter": [
  358. {
  359. "range": {
  360. "time": {
  361. "gte": "<FROM_TIMESTAMP>",
  362. "lte": "<TO_TIMESTAMP>",
  363. "format": "epoch_millis"
  364. }
  365. }
  366. },
  367. {
  368. "query_string": {
  369. "analyze_wildcard": true,
  370. "query": "*"
  371. }
  372. }
  373. ]
  374. }
  375. },
  376. "aggs": {
  377. "3": {
  378. "filters": {
  379. "filters": {
  380. "hello": {
  381. "query_string": {
  382. "query": "host:\"67.65.185.232\"",
  383. "analyze_wildcard": true
  384. }
  385. }
  386. }
  387. },
  388. "aggs": {
  389. "2": {
  390. "date_histogram": {
  391. "interval": "200ms",
  392. "field": "time",
  393. "min_doc_count": 0,
  394. "extended_bounds": {
  395. "min": "<FROM_TIMESTAMP>",
  396. "max": "<TO_TIMESTAMP>"
  397. },
  398. "format": "epoch_millis"
  399. },
  400. "aggs": {}
  401. }
  402. }
  403. }
  404. }
  405. }
  406. `
  407. testElasticSearchResponse(testElasticsearchModelRequestJSON, expectedElasticsearchQueryJSON)
  408. })
  409. })
  410. }