github.com/MontFerret/ferret@v0.18.0/pkg/compiler/compiler_for_test.go (about) 1 package compiler_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "sort" 7 "testing" 8 9 . "github.com/smartystreets/goconvey/convey" 10 11 "github.com/MontFerret/ferret/pkg/compiler" 12 "github.com/MontFerret/ferret/pkg/runtime" 13 ) 14 15 func TestFor(t *testing.T) { 16 Convey("Should compile FOR i IN [] RETURN i", t, func() { 17 c := compiler.New() 18 19 p, err := c.Compile(` 20 FOR i IN [] 21 RETURN i 22 `) 23 24 So(err, ShouldBeNil) 25 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 26 27 out, err := p.Run(context.Background()) 28 29 So(err, ShouldBeNil) 30 So(string(out), ShouldEqual, "[]") 31 }) 32 33 Convey("Should compile FOR i IN [1, 2, 3] RETURN i", t, func() { 34 c := compiler.New() 35 36 p, err := c.Compile(` 37 FOR i IN [1, 2, 3] 38 RETURN i 39 `) 40 41 So(err, ShouldBeNil) 42 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 43 44 out, err := p.Run(context.Background()) 45 46 So(err, ShouldBeNil) 47 So(string(out), ShouldEqual, "[1,2,3]") 48 }) 49 50 Convey("Should not allocate memory if NONE is a return statement", t, func() { 51 c := compiler.New() 52 53 p, err := c.Compile(` 54 FOR i IN 0..100 55 RETURN NONE 56 `) 57 58 So(err, ShouldBeNil) 59 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 60 61 out, err := p.Run(context.Background()) 62 63 So(err, ShouldBeNil) 64 So(string(out), ShouldEqual, "[]") 65 }) 66 67 Convey("Should compile FOR i, k IN [1, 2, 3] RETURN k", t, func() { 68 c := compiler.New() 69 70 p, err := c.Compile(` 71 FOR i, k IN [1, 2, 3] 72 RETURN k 73 `) 74 75 So(err, ShouldBeNil) 76 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 77 78 out, err := p.Run(context.Background()) 79 80 So(err, ShouldBeNil) 81 So(string(out), ShouldEqual, "[0,1,2]") 82 }) 83 84 Convey("Should compile FOR i IN ['foo', 'bar', 'qaz'] RETURN i", t, func() { 85 c := compiler.New() 86 87 p, err := c.Compile(` 88 FOR i IN ['foo', 'bar', 'qaz'] 89 RETURN i 90 `) 91 92 So(err, ShouldBeNil) 93 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 94 95 out, err := p.Run(context.Background()) 96 97 So(err, ShouldBeNil) 98 So(string(out), ShouldEqual, "[\"foo\",\"bar\",\"qaz\"]") 99 }) 100 101 Convey("Should compile FOR i IN {a: 'bar', b: 'foo', c: 'qaz'} RETURN i.name", t, func() { 102 c := compiler.New() 103 104 p, err := c.Compile(` 105 FOR i IN {a: 'bar', b: 'foo', c: 'qaz'} 106 RETURN i 107 `) 108 109 So(err, ShouldBeNil) 110 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 111 112 out, err := p.Run(context.Background()) 113 114 So(err, ShouldBeNil) 115 116 arr := make([]string, 0, 3) 117 err = json.Unmarshal(out, &arr) 118 119 So(err, ShouldBeNil) 120 121 sort.Strings(arr) 122 123 out, err = json.Marshal(arr) 124 125 So(err, ShouldBeNil) 126 127 So(string(out), ShouldEqual, "[\"bar\",\"foo\",\"qaz\"]") 128 }) 129 130 Convey("Should compile FOR i, k IN {a: 'foo', b: 'bar', c: 'qaz'} RETURN k", t, func() { 131 c := compiler.New() 132 133 p, err := c.Compile(` 134 FOR i, k IN {a: 'foo', b: 'bar', c: 'qaz'} 135 RETURN k 136 `) 137 138 So(err, ShouldBeNil) 139 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 140 141 out, err := p.Run(context.Background()) 142 143 So(err, ShouldBeNil) 144 145 arr := make([]string, 0, 3) 146 err = json.Unmarshal(out, &arr) 147 148 So(err, ShouldBeNil) 149 150 sort.Strings(arr) 151 152 out, err = json.Marshal(arr) 153 154 So(err, ShouldBeNil) 155 So(string(out), ShouldEqual, "[\"a\",\"b\",\"c\"]") 156 }) 157 158 Convey("Should compile FOR i IN [{name: 'foo'}, {name: 'bar'}, {name: 'qaz'}] RETURN i.name", t, func() { 159 c := compiler.New() 160 161 p, err := c.Compile(` 162 FOR i IN [{name: 'foo'}, {name: 'bar'}, {name: 'qaz'}] 163 RETURN i.name 164 `) 165 166 So(err, ShouldBeNil) 167 So(p, ShouldHaveSameTypeAs, &runtime.Program{}) 168 169 out, err := p.Run(context.Background()) 170 171 So(err, ShouldBeNil) 172 So(string(out), ShouldEqual, "[\"foo\",\"bar\",\"qaz\"]") 173 }) 174 175 Convey("Should compile nested FOR operators", t, func() { 176 c := compiler.New() 177 178 p, err := c.Compile(` 179 FOR prop IN ["a"] 180 FOR val IN [1, 2, 3] 181 RETURN {[prop]: val} 182 `) 183 184 So(err, ShouldBeNil) 185 186 out, err := p.Run(context.Background()) 187 188 So(err, ShouldBeNil) 189 190 So(string(out), ShouldEqual, "[{\"a\":1},{\"a\":2},{\"a\":3}]") 191 }) 192 193 Convey("Should compile deeply nested FOR operators", t, func() { 194 c := compiler.New() 195 196 p, err := c.Compile(` 197 FOR prop IN ["a"] 198 FOR val IN [1, 2, 3] 199 FOR val2 IN [1, 2, 3] 200 RETURN { [prop]: [val, val2] } 201 `) 202 203 So(err, ShouldBeNil) 204 205 out, err := p.Run(context.Background()) 206 207 So(err, ShouldBeNil) 208 209 So(string(out), ShouldEqual, `[{"a":[1,1]},{"a":[1,2]},{"a":[1,3]},{"a":[2,1]},{"a":[2,2]},{"a":[2,3]},{"a":[3,1]},{"a":[3,2]},{"a":[3,3]}]`) 210 }) 211 212 Convey("Should compile query with a sub query", t, func() { 213 c := compiler.New() 214 215 p, err := c.Compile(` 216 FOR val IN [1, 2, 3] 217 RETURN ( 218 FOR prop IN ["a", "b", "c"] 219 RETURN { [prop]: val } 220 ) 221 `) 222 223 So(err, ShouldBeNil) 224 225 out, err := p.Run(context.Background()) 226 227 So(err, ShouldBeNil) 228 229 So(string(out), ShouldEqual, `[[{"a":1},{"b":1},{"c":1}],[{"a":2},{"b":2},{"c":2}],[{"a":3},{"b":3},{"c":3}]]`) 230 }) 231 232 Convey("Should compile query with variable in a body", t, func() { 233 c := compiler.New() 234 235 p, err := c.Compile(` 236 FOR val IN [1, 2, 3] 237 LET sub = ( 238 FOR prop IN ["a", "b", "c"] 239 RETURN { [prop]: val } 240 ) 241 242 RETURN sub 243 `) 244 245 So(err, ShouldBeNil) 246 247 out, err := p.Run(context.Background()) 248 249 So(err, ShouldBeNil) 250 251 So(string(out), ShouldEqual, `[[{"a":1},{"b":1},{"c":1}],[{"a":2},{"b":2},{"c":2}],[{"a":3},{"b":3},{"c":3}]]`) 252 }) 253 254 Convey("Should compile query with RETURN DISTINCT", t, func() { 255 c := compiler.New() 256 257 p, err := c.Compile(` 258 FOR i IN [ 1, 2, 3, 4, 1, 3 ] 259 RETURN DISTINCT i 260 `) 261 262 So(err, ShouldBeNil) 263 264 out, err := p.Run(context.Background()) 265 266 So(err, ShouldBeNil) 267 268 So(string(out), ShouldEqual, `[1,2,3,4]`) 269 }) 270 } 271 272 func BenchmarkForEmpty(b *testing.B) { 273 p := compiler.New().MustCompile(` 274 FOR i IN [] 275 RETURN i 276 `) 277 278 for n := 0; n < b.N; n++ { 279 p.Run(context.Background()) 280 } 281 } 282 283 func BenchmarkForArray(b *testing.B) { 284 p := compiler.New().MustCompile(` 285 FOR i IN [1,2,3] 286 RETURN i 287 `) 288 289 for n := 0; n < b.N; n++ { 290 p.Run(context.Background()) 291 } 292 } 293 294 func BenchmarkForObject(b *testing.B) { 295 p := compiler.New().MustCompile(` 296 FOR i IN {a: 'bar', b: 'foo', c: 'qaz'} 297 RETURN i 298 `) 299 300 for n := 0; n < b.N; n++ { 301 p.Run(context.Background()) 302 } 303 } 304 305 func BenchmarkForNested(b *testing.B) { 306 p := compiler.New().MustCompile(` 307 FOR prop IN ["a"] 308 FOR val IN [1, 2, 3] 309 RETURN {[prop]: val} 310 `) 311 312 for n := 0; n < b.N; n++ { 313 p.Run(context.Background()) 314 } 315 } 316 317 func BenchmarkForNested2(b *testing.B) { 318 p := compiler.New().MustCompile(` 319 FOR prop IN ["a"] 320 FOR val IN [1, 2, 3] 321 FOR val2 IN ["b"] 322 RETURN { [prop]: [val, val2] } 323 `) 324 325 for n := 0; n < b.N; n++ { 326 p.Run(context.Background()) 327 } 328 } 329 330 func BenchmarkForSub(b *testing.B) { 331 p := compiler.New().MustCompile(` 332 FOR val IN [1, 2, 3] 333 RETURN ( 334 FOR prop IN ["a", "b", "c"] 335 RETURN { [prop]: val } 336 ) 337 `) 338 339 for n := 0; n < b.N; n++ { 340 p.Run(context.Background()) 341 } 342 } 343 344 func BenchmarkForSub2(b *testing.B) { 345 p := compiler.New().MustCompile(` 346 FOR val IN [1, 2, 3] 347 LET sub = ( 348 FOR prop IN ["a", "b", "c"] 349 RETURN { [prop]: val } 350 ) 351 352 RETURN sub 353 `) 354 355 for n := 0; n < b.N; n++ { 356 p.Run(context.Background()) 357 } 358 } 359 360 func BenchmarkForDistinct(b *testing.B) { 361 p := compiler.New().MustCompile(` 362 FOR i IN [ 1, 2, 3, 4, 1, 3 ] 363 RETURN DISTINCT i 364 `) 365 366 for n := 0; n < b.N; n++ { 367 p.Run(context.Background()) 368 } 369 } 370 371 func BenchmarkForLimit(b *testing.B) { 372 p := compiler.New().MustCompile(` 373 FOR i IN [ 1,2,3,4,5,6,7,8 ] 374 LIMIT 2 375 RETURN i 376 `) 377 378 for n := 0; n < b.N; n++ { 379 p.Run(context.Background()) 380 } 381 } 382 383 func BenchmarkForLimitOffset(b *testing.B) { 384 p := compiler.New().MustCompile(` 385 FOR i IN [ 1,2,3,4,5,6,7,8 ] 386 LIMIT 4, 2 387 RETURN i 388 `) 389 390 for n := 0; n < b.N; n++ { 391 p.Run(context.Background()) 392 } 393 } 394 395 func BenchmarkForSort(b *testing.B) { 396 p := compiler.New().MustCompile(` 397 LET users = [ 398 { 399 active: true, 400 age: 31, 401 gender: "m" 402 }, 403 { 404 active: true, 405 age: 29, 406 gender: "f" 407 }, 408 { 409 active: true, 410 age: 36, 411 gender: "m" 412 } 413 ] 414 FOR u IN users 415 SORT u.age 416 RETURN u 417 `) 418 419 for n := 0; n < b.N; n++ { 420 p.Run(context.Background()) 421 } 422 } 423 424 func BenchmarkForSort2(b *testing.B) { 425 p := compiler.New().MustCompile(` 426 LET users = [ 427 { 428 active: true, 429 age: 31, 430 gender: "m" 431 }, 432 { 433 active: true, 434 age: 29, 435 gender: "f" 436 }, 437 { 438 active: true, 439 age: 36, 440 gender: "m" 441 } 442 ] 443 FOR u IN users 444 SORT u.age, u.gender 445 RETURN u 446 `) 447 448 for n := 0; n < b.N; n++ { 449 p.Run(context.Background()) 450 } 451 } 452 453 func BenchmarkForSortDesc(b *testing.B) { 454 p := compiler.New().MustCompile(` 455 LET users = [ 456 { 457 active: true, 458 age: 31, 459 gender: "m" 460 }, 461 { 462 active: true, 463 age: 29, 464 gender: "f" 465 }, 466 { 467 active: true, 468 age: 36, 469 gender: "m" 470 } 471 ] 472 FOR u IN users 473 SORT u.age DESC 474 RETURN u 475 `) 476 477 for n := 0; n < b.N; n++ { 478 p.Run(context.Background()) 479 } 480 }