misc_func_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. // Copyright 2021 EMQ Technologies Co., Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package operator
  15. import (
  16. "fmt"
  17. "github.com/lf-edge/ekuiper/internal/conf"
  18. "github.com/lf-edge/ekuiper/internal/testx"
  19. "github.com/lf-edge/ekuiper/internal/topo/context"
  20. "github.com/lf-edge/ekuiper/internal/xsql"
  21. "github.com/lf-edge/ekuiper/pkg/cast"
  22. "reflect"
  23. "strings"
  24. "testing"
  25. )
  26. func TestMiscFunc_Apply1(t *testing.T) {
  27. var tests = []struct {
  28. sql string
  29. data *xsql.Tuple
  30. result []map[string]interface{}
  31. }{
  32. {
  33. sql: "SELECT md5(a) AS a FROM test",
  34. data: &xsql.Tuple{
  35. Emitter: "test",
  36. Message: xsql.Message{
  37. "a": "The quick brown fox jumps over the lazy dog",
  38. "b": "myb",
  39. "c": "myc",
  40. },
  41. },
  42. result: []map[string]interface{}{{
  43. "a": strings.ToLower("9E107D9D372BB6826BD81D3542A419D6"),
  44. }},
  45. },
  46. {
  47. sql: "SELECT md5(d) AS a FROM test",
  48. data: &xsql.Tuple{
  49. Emitter: "test",
  50. Message: xsql.Message{
  51. "a": "The quick brown fox jumps over the lazy dog",
  52. "b": "myb",
  53. "c": "myc",
  54. },
  55. },
  56. result: []map[string]interface{}{{}},
  57. },
  58. {
  59. sql: "SELECT sha1(a) AS a FROM test",
  60. data: &xsql.Tuple{
  61. Emitter: "test",
  62. Message: xsql.Message{
  63. "a": "The quick brown fox jumps over the lazy dog",
  64. "b": "myb",
  65. "c": "myc",
  66. },
  67. },
  68. result: []map[string]interface{}{{
  69. "a": strings.ToLower("2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12"),
  70. }},
  71. },
  72. {
  73. sql: "SELECT sha256(a) AS a FROM test",
  74. data: &xsql.Tuple{
  75. Emitter: "test",
  76. Message: xsql.Message{
  77. "a": "The quick brown fox jumps over the lazy dog",
  78. "b": "myb",
  79. "c": "myc",
  80. },
  81. },
  82. result: []map[string]interface{}{{
  83. "a": strings.ToLower("D7A8FBB307D7809469CA9ABCB0082E4F8D5651E46D3CDB762D02D0BF37C9E592"),
  84. }},
  85. },
  86. {
  87. sql: "SELECT sha384(a) AS a FROM test",
  88. data: &xsql.Tuple{
  89. Emitter: "test",
  90. Message: xsql.Message{
  91. "a": "The quick brown fox jumps over the lazy dog",
  92. "b": "myb",
  93. "c": "myc",
  94. },
  95. },
  96. result: []map[string]interface{}{{
  97. "a": strings.ToLower("CA737F1014A48F4C0B6DD43CB177B0AFD9E5169367544C494011E3317DBF9A509CB1E5DC1E85A941BBEE3D7F2AFBC9B1"),
  98. }},
  99. },
  100. {
  101. sql: "SELECT sha512(a) AS a FROM test",
  102. data: &xsql.Tuple{
  103. Emitter: "test",
  104. Message: xsql.Message{
  105. "a": "The quick brown fox jumps over the lazy dog",
  106. "b": "myb",
  107. "c": "myc",
  108. },
  109. },
  110. result: []map[string]interface{}{{
  111. "a": strings.ToLower("07E547D9586F6A73F73FBAC0435ED76951218FB7D0C8D788A309D785436BBB642E93A252A954F23912547D1E8A3B5ED6E1BFD7097821233FA0538F3DB854FEE6"),
  112. }},
  113. },
  114. {
  115. sql: "SELECT mqtt(topic) AS a FROM test",
  116. data: &xsql.Tuple{
  117. Emitter: "test",
  118. Message: xsql.Message{},
  119. Metadata: xsql.Metadata{
  120. "topic": "devices/device_001/message",
  121. },
  122. },
  123. result: []map[string]interface{}{{
  124. "a": "devices/device_001/message",
  125. }},
  126. },
  127. {
  128. sql: "SELECT mqtt(topic) AS a FROM test",
  129. data: &xsql.Tuple{
  130. Emitter: "test",
  131. Message: xsql.Message{},
  132. Metadata: xsql.Metadata{
  133. "topic": "devices/device_001/message",
  134. },
  135. },
  136. result: []map[string]interface{}{{
  137. "a": "devices/device_001/message",
  138. }},
  139. },
  140. {
  141. sql: "SELECT topic, mqtt(topic) AS a FROM test",
  142. data: &xsql.Tuple{
  143. Emitter: "test",
  144. Message: xsql.Message{
  145. "topic": "fff",
  146. },
  147. Metadata: xsql.Metadata{
  148. "topic": "devices/device_001/message",
  149. },
  150. },
  151. result: []map[string]interface{}{{
  152. "topic": "fff",
  153. "a": "devices/device_001/message",
  154. }},
  155. },
  156. {
  157. sql: "SELECT cardinality(arr) as r FROM test",
  158. data: &xsql.Tuple{
  159. Emitter: "test",
  160. Message: xsql.Message{
  161. "temperature": 43.2,
  162. "arr": []int{},
  163. },
  164. },
  165. result: []map[string]interface{}{{
  166. "r": 0,
  167. }},
  168. },
  169. {
  170. sql: "SELECT cardinality(arr) as r FROM test",
  171. data: &xsql.Tuple{
  172. Emitter: "test",
  173. Message: xsql.Message{
  174. "temperature": 43.2,
  175. "arr": []int{1, 2, 3, 4, 5},
  176. },
  177. },
  178. result: []map[string]interface{}{{
  179. "r": 5,
  180. }},
  181. },
  182. {
  183. sql: "SELECT isNull(arr) as r FROM test",
  184. data: &xsql.Tuple{
  185. Emitter: "test",
  186. Message: xsql.Message{
  187. "temperature": 43.2,
  188. "arr": []int{},
  189. },
  190. },
  191. result: []map[string]interface{}{{
  192. "r": false,
  193. }},
  194. },
  195. {
  196. sql: "SELECT isNull(arr) as r FROM test",
  197. data: &xsql.Tuple{
  198. Emitter: "test",
  199. Message: xsql.Message{
  200. "temperature": 43.2,
  201. "arr": []float64(nil),
  202. },
  203. },
  204. result: []map[string]interface{}{{
  205. "r": true,
  206. }},
  207. },
  208. {
  209. sql: "SELECT isNull(rec) as r FROM test",
  210. data: &xsql.Tuple{
  211. Emitter: "test",
  212. Message: xsql.Message{
  213. "temperature": 43.2,
  214. "rec": map[string]interface{}(nil),
  215. },
  216. },
  217. result: []map[string]interface{}{{
  218. "r": true,
  219. }},
  220. },
  221. {
  222. sql: "SELECT cast(a * 1000, \"datetime\") AS a FROM test",
  223. data: &xsql.Tuple{
  224. Emitter: "test",
  225. Message: xsql.Message{
  226. "a": 1.62000273e+09,
  227. "b": "ya",
  228. "c": "myc",
  229. },
  230. },
  231. result: []map[string]interface{}{{
  232. "a": cast.TimeFromUnixMilli(1.62000273e+12),
  233. }},
  234. },
  235. }
  236. fmt.Printf("The test bucket size is %d.\n\n", len(tests))
  237. contextLogger := conf.Log.WithField("rule", "TestMiscFunc_Apply1")
  238. ctx := context.WithValue(context.Background(), context.LoggerKey, contextLogger)
  239. for i, tt := range tests {
  240. stmt, err := xsql.NewParser(strings.NewReader(tt.sql)).Parse()
  241. if err != nil || stmt == nil {
  242. t.Errorf("parse sql %s error %v", tt.sql, err)
  243. }
  244. pp := &ProjectOp{Fields: stmt.Fields}
  245. fv, afv := xsql.NewFunctionValuersForOp(nil)
  246. result := pp.Apply(ctx, tt.data, fv, afv)
  247. if !reflect.DeepEqual(tt.result, result) {
  248. t.Errorf("%d. %q\n\nresult mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.sql, tt.result, result)
  249. }
  250. }
  251. }
  252. func TestMqttFunc_Apply2(t *testing.T) {
  253. var tests = []struct {
  254. sql string
  255. data *xsql.JoinTupleSets
  256. result []map[string]interface{}
  257. }{
  258. {
  259. sql: "SELECT id1, mqtt(src1.topic) AS a, mqtt(src2.topic) as b FROM src1 LEFT JOIN src2 ON src1.id1 = src2.id1",
  260. data: &xsql.JoinTupleSets{
  261. Content: []xsql.JoinTuple{
  262. {
  263. Tuples: []xsql.Tuple{
  264. {Emitter: "src1", Message: xsql.Message{"id1": "1", "f1": "v1"}, Metadata: xsql.Metadata{"topic": "devices/type1/device001"}},
  265. {Emitter: "src2", Message: xsql.Message{"id2": "1", "f2": "w1"}, Metadata: xsql.Metadata{"topic": "devices/type2/device001"}},
  266. },
  267. },
  268. },
  269. },
  270. result: []map[string]interface{}{{
  271. "id1": "1",
  272. "a": "devices/type1/device001",
  273. "b": "devices/type2/device001",
  274. }},
  275. },
  276. }
  277. fmt.Printf("The test bucket size is %d.\n\n", len(tests))
  278. contextLogger := conf.Log.WithField("rule", "TestMqttFunc_Apply2")
  279. ctx := context.WithValue(context.Background(), context.LoggerKey, contextLogger)
  280. for i, tt := range tests {
  281. stmt, err := xsql.NewParser(strings.NewReader(tt.sql)).Parse()
  282. if err != nil || stmt == nil {
  283. t.Errorf("parse sql %s error %v", tt.sql, err)
  284. }
  285. pp := &ProjectOp{Fields: stmt.Fields}
  286. fv, afv := xsql.NewFunctionValuersForOp(nil)
  287. result := pp.Apply(ctx, tt.data, fv, afv)
  288. if !reflect.DeepEqual(tt.result, result) {
  289. t.Errorf("%d. %q\n\nresult mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.sql, tt.result, result)
  290. }
  291. }
  292. }
  293. func TestMetaFunc_Apply1(t *testing.T) {
  294. var tests = []struct {
  295. sql string
  296. data interface{}
  297. result interface{}
  298. }{
  299. {
  300. sql: "SELECT topic, meta(topic) AS a FROM test",
  301. data: &xsql.Tuple{
  302. Emitter: "test",
  303. Message: xsql.Message{
  304. "topic": "fff",
  305. },
  306. Metadata: xsql.Metadata{
  307. "topic": "devices/device_001/message",
  308. },
  309. },
  310. result: []map[string]interface{}{{
  311. "topic": "fff",
  312. "a": "devices/device_001/message",
  313. }},
  314. },
  315. {
  316. sql: "SELECT meta(device) as d, meta(temperature->device) as r FROM test",
  317. data: &xsql.Tuple{
  318. Emitter: "test",
  319. Message: xsql.Message{
  320. "temperature": 43.2,
  321. },
  322. Metadata: xsql.Metadata{
  323. "temperature": map[string]interface{}{
  324. "id": "dfadfasfas",
  325. "device": "device2",
  326. },
  327. "device": "gateway",
  328. },
  329. },
  330. result: []map[string]interface{}{{
  331. "d": "gateway",
  332. "r": "device2",
  333. }},
  334. },
  335. {
  336. sql: "SELECT meta(*) as r FROM test",
  337. data: &xsql.Tuple{
  338. Emitter: "test",
  339. Message: xsql.Message{
  340. "temperature": 43.2,
  341. },
  342. Metadata: xsql.Metadata{
  343. "temperature": map[string]interface{}{
  344. "id": "dfadfasfas",
  345. "device": "device2",
  346. },
  347. "device": "gateway",
  348. },
  349. },
  350. result: []map[string]interface{}{{
  351. "r": map[string]interface{}{
  352. "temperature": map[string]interface{}{
  353. "id": "dfadfasfas",
  354. "device": "device2",
  355. },
  356. "device": "gateway",
  357. },
  358. }},
  359. },
  360. {
  361. sql: "SELECT topic, meta(`Light-diming`->device) AS a FROM test",
  362. data: &xsql.Tuple{
  363. Emitter: "test",
  364. Message: xsql.Message{
  365. "topic": "fff",
  366. },
  367. Metadata: xsql.Metadata{
  368. "Light-diming": map[string]interface{}{
  369. "device": "device2",
  370. },
  371. },
  372. },
  373. result: []map[string]interface{}{{
  374. "topic": "fff",
  375. "a": "device2",
  376. }},
  377. },
  378. }
  379. fmt.Printf("The test bucket size is %d.\n\n", len(tests))
  380. contextLogger := conf.Log.WithField("rule", "TestMetaFunc_Apply1")
  381. ctx := context.WithValue(context.Background(), context.LoggerKey, contextLogger)
  382. for i, tt := range tests {
  383. stmt, err := xsql.NewParser(strings.NewReader(tt.sql)).Parse()
  384. if err != nil || stmt == nil {
  385. t.Errorf("parse sql %s error %v", tt.sql, err)
  386. }
  387. pp := &ProjectOp{Fields: stmt.Fields}
  388. fv, afv := xsql.NewFunctionValuersForOp(nil)
  389. result := pp.Apply(ctx, tt.data, fv, afv)
  390. if !reflect.DeepEqual(tt.result, result) {
  391. t.Errorf("%d. %q\n\nresult mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.sql, tt.result, result)
  392. }
  393. }
  394. }
  395. func TestJsonPathFunc_Apply1(t *testing.T) {
  396. var tests = []struct {
  397. sql string
  398. data interface{}
  399. result interface{}
  400. err string
  401. }{
  402. {
  403. sql: `SELECT json_path_query(equipment, "$.arm_right") AS a FROM test`,
  404. data: &xsql.Tuple{
  405. Emitter: "test",
  406. Message: xsql.Message{
  407. "class": "warrior",
  408. "equipment": map[string]interface{}{
  409. "rings": []map[string]interface{}{
  410. {
  411. "name": "ring of despair",
  412. "weight": 0.1,
  413. }, {
  414. "name": "ring of strength",
  415. "weight": 2.4,
  416. },
  417. },
  418. "arm_right": "Sword of flame",
  419. "arm_left": "Shield of faith",
  420. },
  421. },
  422. },
  423. result: []map[string]interface{}{{
  424. "a": "Sword of flame",
  425. }},
  426. }, {
  427. sql: `SELECT json_path_query(equipment, "$.rings[*].weight") AS a FROM test`,
  428. data: &xsql.Tuple{
  429. Emitter: "test",
  430. Message: xsql.Message{
  431. "class": "warrior",
  432. "equipment": map[string]interface{}{
  433. "rings": []interface{}{
  434. map[string]interface{}{
  435. "name": "ring of despair",
  436. "weight": 0.1,
  437. }, map[string]interface{}{
  438. "name": "ring of strength",
  439. "weight": 2.4,
  440. },
  441. },
  442. "arm_right": "Sword of flame",
  443. "arm_left": "Shield of faith",
  444. },
  445. },
  446. },
  447. result: []map[string]interface{}{{
  448. "a": []interface{}{
  449. 0.1, 2.4,
  450. },
  451. }},
  452. }, {
  453. sql: `SELECT json_path_query_first(equipment, "$.rings[*].weight") AS a FROM test`,
  454. data: &xsql.Tuple{
  455. Emitter: "test",
  456. Message: xsql.Message{
  457. "class": "warrior",
  458. "equipment": map[string]interface{}{
  459. "rings": []interface{}{
  460. map[string]interface{}{
  461. "name": "ring of despair",
  462. "weight": 0.1,
  463. }, map[string]interface{}{
  464. "name": "ring of strength",
  465. "weight": 2.4,
  466. },
  467. },
  468. "arm_right": "Sword of flame",
  469. "arm_left": "Shield of faith",
  470. },
  471. },
  472. },
  473. result: []map[string]interface{}{{
  474. "a": 0.1,
  475. }},
  476. }, {
  477. sql: `SELECT json_path_query(equipment, "$.rings[? @.weight>1]") AS a FROM test`,
  478. data: &xsql.Tuple{
  479. Emitter: "test",
  480. Message: xsql.Message{
  481. "class": "warrior",
  482. "equipment": map[string]interface{}{
  483. "rings": []interface{}{
  484. map[string]interface{}{
  485. "name": "ring of despair",
  486. "weight": 0.1,
  487. }, map[string]interface{}{
  488. "name": "ring of strength",
  489. "weight": 2.4,
  490. },
  491. },
  492. "arm_right": "Sword of flame",
  493. "arm_left": "Shield of faith",
  494. },
  495. },
  496. },
  497. result: []map[string]interface{}{{
  498. "a": []interface{}{
  499. map[string]interface{}{
  500. "name": "ring of strength",
  501. "weight": 2.4,
  502. },
  503. },
  504. }},
  505. }, {
  506. sql: `SELECT json_path_query(equipment, "$.rings[? @.weight>1].name") AS a FROM test`,
  507. data: &xsql.Tuple{
  508. Emitter: "test",
  509. Message: xsql.Message{
  510. "class": "warrior",
  511. "equipment": map[string]interface{}{
  512. "rings": []interface{}{
  513. map[string]interface{}{
  514. "name": "ring of despair",
  515. "weight": 0.1,
  516. }, map[string]interface{}{
  517. "name": "ring of strength",
  518. "weight": 2.4,
  519. },
  520. },
  521. "arm_right": "Sword of flame",
  522. "arm_left": "Shield of faith",
  523. },
  524. },
  525. },
  526. result: []map[string]interface{}{{
  527. "a": []interface{}{
  528. "ring of strength",
  529. },
  530. }},
  531. }, {
  532. sql: `SELECT json_path_exists(equipment, "$.rings[? @.weight>5]") AS a FROM test`,
  533. data: &xsql.Tuple{
  534. Emitter: "test",
  535. Message: xsql.Message{
  536. "class": "warrior",
  537. "equipment": map[string]interface{}{
  538. "rings": []interface{}{
  539. map[string]interface{}{
  540. "name": "ring of despair",
  541. "weight": 0.1,
  542. }, map[string]interface{}{
  543. "name": "ring of strength",
  544. "weight": 2.4,
  545. },
  546. },
  547. "arm_right": "Sword of flame",
  548. "arm_left": "Shield of faith",
  549. },
  550. },
  551. },
  552. result: []map[string]interface{}{{
  553. "a": false,
  554. }},
  555. }, {
  556. sql: `SELECT json_path_exists(equipment, "$.ring1") AS a FROM test`,
  557. data: &xsql.Tuple{
  558. Emitter: "test",
  559. Message: xsql.Message{
  560. "class": "warrior",
  561. "equipment": map[string]interface{}{
  562. "rings": []interface{}{
  563. map[string]interface{}{
  564. "name": "ring of despair",
  565. "weight": 0.1,
  566. }, map[string]interface{}{
  567. "name": "ring of strength",
  568. "weight": 2.4,
  569. },
  570. },
  571. "arm_right": "Sword of flame",
  572. "arm_left": "Shield of faith",
  573. },
  574. },
  575. },
  576. result: []map[string]interface{}{{
  577. "a": false,
  578. }},
  579. }, {
  580. sql: `SELECT json_path_exists(equipment, "$.rings") AS a FROM test`,
  581. data: &xsql.Tuple{
  582. Emitter: "test",
  583. Message: xsql.Message{
  584. "class": "warrior",
  585. "equipment": map[string]interface{}{
  586. "rings": []interface{}{
  587. map[string]interface{}{
  588. "name": "ring of despair",
  589. "weight": 0.1,
  590. }, map[string]interface{}{
  591. "name": "ring of strength",
  592. "weight": 2.4,
  593. },
  594. },
  595. "arm_right": "Sword of flame",
  596. "arm_left": "Shield of faith",
  597. },
  598. },
  599. },
  600. result: []map[string]interface{}{{
  601. "a": true,
  602. }},
  603. }, {
  604. sql: `SELECT json_path_query(equipment, "$.rings[? (@.weight>1)].name") AS a FROM test`,
  605. data: &xsql.Tuple{
  606. Emitter: "test",
  607. Message: xsql.Message{
  608. "class": "warrior",
  609. "equipment": map[string]interface{}{
  610. "rings": []map[string]interface{}{
  611. {
  612. "name": "ring of despair",
  613. "weight": 0.1,
  614. }, {
  615. "name": "ring of strength",
  616. "weight": 2.4,
  617. },
  618. },
  619. "arm_right": "Sword of flame",
  620. "arm_left": "Shield of faith",
  621. },
  622. },
  623. },
  624. result: []map[string]interface{}{{
  625. "a": []interface{}{
  626. "ring of strength",
  627. },
  628. }},
  629. }, {
  630. sql: `SELECT json_path_query(equipment, "$.rings[*]") AS a FROM test`,
  631. data: &xsql.Tuple{
  632. Emitter: "test",
  633. Message: xsql.Message{
  634. "class": "warrior",
  635. "equipment": map[string]interface{}{
  636. "rings": []float64{
  637. 0.1, 2.4,
  638. },
  639. "arm_right": "Sword of flame",
  640. "arm_left": "Shield of faith",
  641. },
  642. },
  643. },
  644. result: []map[string]interface{}{{
  645. "a": []interface{}{
  646. 0.1, 2.4,
  647. },
  648. }},
  649. }, {
  650. sql: `SELECT json_path_query(equipment, "$.rings") AS a FROM test`,
  651. data: &xsql.Tuple{
  652. Emitter: "test",
  653. Message: xsql.Message{
  654. "class": "warrior",
  655. "equipment": map[string]interface{}{
  656. "rings": []float64{
  657. 0.1, 2.4,
  658. },
  659. "arm_right": "Sword of flame",
  660. "arm_left": "Shield of faith",
  661. },
  662. },
  663. },
  664. result: []map[string]interface{}{{
  665. "a": []interface{}{
  666. 0.1, 2.4,
  667. },
  668. }},
  669. }, {
  670. sql: `SELECT json_path_query(equipment, "$[0].rings[1]") AS a FROM test`,
  671. data: &xsql.Tuple{
  672. Emitter: "test",
  673. Message: xsql.Message{
  674. "class": "warrior",
  675. "equipment": []map[string]interface{}{
  676. {
  677. "rings": []float64{
  678. 0.1, 2.4,
  679. },
  680. "arm_right": "Sword of flame",
  681. "arm_left": "Shield of faith",
  682. },
  683. },
  684. },
  685. },
  686. result: []map[string]interface{}{{
  687. "a": 2.4,
  688. }},
  689. }, {
  690. sql: "SELECT json_path_query(equipment, \"$[0][\\\"arm.left\\\"]\") AS a FROM test",
  691. data: &xsql.Tuple{
  692. Emitter: "test",
  693. Message: xsql.Message{
  694. "class": "warrior",
  695. "equipment": []map[string]interface{}{
  696. {
  697. "rings": []float64{
  698. 0.1, 2.4,
  699. },
  700. "arm.right": "Sword of flame",
  701. "arm.left": "Shield of faith",
  702. },
  703. },
  704. },
  705. },
  706. result: []map[string]interface{}{{
  707. "a": "Shield of faith",
  708. }},
  709. }, {
  710. sql: "SELECT json_path_query(equipment, \"$[\\\"arm.left\\\"]\") AS a FROM test",
  711. data: &xsql.Tuple{
  712. Emitter: "test",
  713. Message: xsql.Message{
  714. "class": "warrior",
  715. "equipment": `{"rings": [0.1, 2.4],"arm.right": "Sword of flame","arm.left": "Shield of faith"}`,
  716. },
  717. },
  718. result: []map[string]interface{}{{
  719. "a": "Shield of faith",
  720. }},
  721. }, {
  722. sql: "SELECT json_path_query(equipment, \"$[0][\\\"arm.left\\\"]\") AS a FROM test",
  723. data: &xsql.Tuple{
  724. Emitter: "test",
  725. Message: xsql.Message{
  726. "class": "warrior",
  727. "equipment": `[{"rings": [0.1, 2.4],"arm.right": "Sword of flame","arm.left": "Shield of faith"}]`,
  728. },
  729. },
  730. result: []map[string]interface{}{{
  731. "a": "Shield of faith",
  732. }},
  733. }, {
  734. sql: `SELECT all[poi[-1] + 1]->ts as powerOnTs FROM test`,
  735. data: &xsql.Tuple{
  736. Emitter: "test",
  737. Message: xsql.Message{
  738. "all": []map[string]interface{}{
  739. {"SystemPowerMode": 0, "VehicleSpeed": 0, "FLWdwPosition": 0, "FrontWiperSwitchStatus": float64(1), "ts": 0},
  740. {"SystemPowerMode": 0, "VehicleSpeed": 0, "FLWdwPosition": 0, "FrontWiperSwitchStatus": float64(4), "ts": 500},
  741. {"SystemPowerMode": 2, "VehicleSpeed": 0, "FLWdwPosition": 0, "FrontWiperSwitchStatus": 0, "ts": 1000},
  742. {"SystemPowerMode": 2, "VehicleSpeed": 10, "FLWdwPosition": 20, "FrontWiperSwitchStatus": 0, "ts": 60000},
  743. {"SystemPowerMode": 2, "VehicleSpeed": 10, "FLWdwPosition": 20, "FrontWiperSwitchStatus": 0, "ts": 89500},
  744. {"SystemPowerMode": 2, "VehicleSpeed": 20, "FLWdwPosition": 50, "FrontWiperSwitchStatus": 5, "ts": 90000},
  745. {"SystemPowerMode": 2, "VehicleSpeed": 40, "FLWdwPosition": 60, "FrontWiperSwitchStatus": 5, "ts": 121000},
  746. },
  747. "poi": []interface{}{0, 1},
  748. },
  749. },
  750. result: []map[string]interface{}{{
  751. "powerOnTs": 1000,
  752. }},
  753. }, {
  754. sql: `SELECT json_path_query(equipment, "$.arm_right") AS a FROM test`,
  755. data: &xsql.Tuple{
  756. Emitter: "test",
  757. Message: xsql.Message{
  758. "class": "warrior",
  759. "equipment2": map[string]interface{}{
  760. "rings": []map[string]interface{}{
  761. {
  762. "name": "ring of despair",
  763. "weight": 0.1,
  764. }, {
  765. "name": "ring of strength",
  766. "weight": 2.4,
  767. },
  768. },
  769. "arm_right": "Sword of flame",
  770. "arm_left": "Shield of faith",
  771. },
  772. },
  773. },
  774. err: "run Select error: call func json_path_query error: json_path_query function error: invalid data nil for jsonpath",
  775. },
  776. }
  777. fmt.Printf("The test bucket size is %d.\n\n", len(tests))
  778. contextLogger := conf.Log.WithField("rule", "TestJsonFunc_Apply1")
  779. ctx := context.WithValue(context.Background(), context.LoggerKey, contextLogger)
  780. for i, tt := range tests {
  781. stmt, err := xsql.NewParser(strings.NewReader(tt.sql)).Parse()
  782. if err != nil || stmt == nil {
  783. t.Errorf("parse sql %s error %v", tt.sql, err)
  784. }
  785. pp := &ProjectOp{Fields: stmt.Fields}
  786. fv, afv := xsql.NewFunctionValuersForOp(ctx)
  787. result := pp.Apply(ctx, tt.data, fv, afv)
  788. switch rt := result.(type) {
  789. case []map[string]interface{}:
  790. if tt.err == "" {
  791. if !reflect.DeepEqual(tt.result, result) {
  792. t.Errorf("%d. %q\n\nresult mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.sql, tt.result, result)
  793. }
  794. } else {
  795. t.Errorf("%d: invalid result:\n exp error %s\n got=%s\n\n", i, tt.err, result)
  796. }
  797. case error:
  798. if tt.err == "" {
  799. t.Errorf("%d: got error:\n exp=%s\n got=%s\n\n", i, tt.result, rt)
  800. } else if !reflect.DeepEqual(tt.err, testx.Errstring(rt)) {
  801. t.Errorf("%d: error mismatch:\n exp=%s\n got=%s\n\n", i, tt.err, rt)
  802. }
  803. default:
  804. t.Errorf("%d: Invalid returned result found %v", i, result)
  805. }
  806. }
  807. }