Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(413)

Side by Side Diff: filter/txnBuf/txnbuf_test.go

Issue 1309803004: Add transaction buffer filter. (Closed) Base URL: https://github.com/luci/gae.git@add_query_support
Patch Set: make data flow clearer, implement Count Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package txnBuf
6
7 import (
8 "bytes"
9 "fmt"
10 "math/rand"
11 "testing"
12
13 "github.com/luci/gae/filter/count"
14 "github.com/luci/gae/impl/memory"
15 "github.com/luci/gae/service/datastore"
16 "github.com/luci/luci-go/common/cmpbin"
17 "github.com/luci/luci-go/common/errors"
18 . "github.com/luci/luci-go/common/testing/assertions"
19 . "github.com/smartystreets/goconvey/convey"
20 "golang.org/x/net/context"
21 )
22
23 type Foo struct {
24 ID int64 `gae:"$id"`
25 Parent *datastore.Key `gae:"$parent"`
26
27 Value []int64
28 ValueNI []byte `gae:",noindex"`
29 Sort []string
30 }
31
32 func toIntSlice(stuff []interface{}) []int64 {
33 vals, ok := stuff[0].([]int64)
34 if !ok {
35 vals = make([]int64, len(stuff))
36 for i := range vals {
37 vals[i] = int64(stuff[i].(int))
38 }
39 }
40 return vals
41 }
42
43 func toInt64(thing interface{}) int64 {
44 switch x := thing.(type) {
45 case int:
46 return int64(x)
47 case int64:
48 return x
49 default:
50 panic(fmt.Errorf("wat r it? %v", x))
51 }
52 }
53
54 func fooShouldHave(ds datastore.Interface) func(interface{}, ...interface{}) str ing {
55 return func(id interface{}, values ...interface{}) string {
56 f := &Foo{ID: toInt64(id)}
57 err := ds.Get(f)
58 if len(values) == 0 {
59 return ShouldEqual(err, datastore.ErrNoSuchEntity)
60 }
61
62 ret := ShouldBeNil(err)
63 if ret == "" {
64 if data, ok := values[0].([]byte); ok {
65 ret = ShouldResemble(f.ValueNI, data)
66 } else {
67 ret = ShouldResemble(f.Value, toIntSlice(values) )
68 }
69 }
70 return ret
71 }
72 }
73
74 func fooSetTo(ds datastore.Interface) func(interface{}, ...interface{}) string {
75 return func(id interface{}, values ...interface{}) string {
76 f := &Foo{ID: toInt64(id)}
77 if len(values) == 0 {
78 return ShouldBeNil(ds.Delete(ds.KeyForObj(f)))
79 }
80 if data, ok := values[0].([]byte); ok {
81 f.ValueNI = data
82 } else {
83 f.Value = toIntSlice(values)
84 }
85 return ShouldBeNil(ds.Put(f))
86 }
87 }
88
89 func TestTransactionBuffers(t *testing.T) {
90 t.Parallel()
91
92 cb := func(i int64) string {
93 buf := &bytes.Buffer{}
94 cmpbin.WriteInt(buf, i)
95 return buf.String()
96 }
97
98 rs := rand.NewSource(0)
99 dataMultiRoot := make([]*Foo, 20)
100 dataSingleRoot := make([]*Foo, 20)
101 root := datastore.MakeKey("something~else", "", "Parent", 1)
102 nums := make([]string, 20)
103 for i := range dataMultiRoot {
104 id := int64(i + 1)
105 nums[i] = cb(id)
106
107 val := make([]int64, rs.Int63()%20)
108 for j := range val {
109 r := rs.Int63()
110 val[j] = r
111 }
112
113 dataMultiRoot[i] = &Foo{ID: id, Value: val}
114 dataSingleRoot[i] = &Foo{ID: id, Parent: root, Value: val}
115 }
116
117 hugeField := make([]byte, DefaultSizeBudget/8)
118 for i := range hugeField {
119 hugeField[i] = byte(i)
120 }
121
122 hugeData := make([]*Foo, 11)
123 for i := range hugeData {
124 hugeData[i] = &Foo{ID: int64(i + 1), ValueNI: hugeField}
125 }
126
127 mkds := func(data []*Foo) (under, over *count.DSCounter, ds datastore.In terface) {
128 c := memory.UseWithAppID(context.Background(), "something~else")
129 ds = datastore.Get(c)
130 _, err := ds.AllocateIDs(ds.KeyForObj(data[0]), 100)
131 if err != nil {
132 panic(err)
133 }
134 if err := ds.PutMulti(data); err != nil {
135 panic(err)
136 }
137
138 c, under = count.FilterRDS(c)
139 c = FilterRDS(c)
140 c, over = count.FilterRDS(c)
141 ds = datastore.Get(c)
142 return
143 }
144
145 Convey("Get/Put/Delete", t, func() {
146 under, over, ds := mkds(dataMultiRoot)
147
148 So(under.PutMulti.Total(), ShouldEqual, 0)
149 So(over.PutMulti.Total(), ShouldEqual, 0)
150
151 Convey("Good", func() {
152 Convey("read-only", func() {
153 So(ds.RunInTransaction(func(c context.Context) e rror {
154 ds := datastore.Get(c)
155
156 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
157 return nil
158 }, nil), ShouldBeNil)
159 })
160
161 Convey("single-level read/write", func() {
162 So(ds.RunInTransaction(func(c context.Context) e rror {
163 ds := datastore.Get(c)
164
165 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
166
167 So(4, fooSetTo(ds), 1, 2, 3, 4)
168
169 So(3, fooSetTo(ds), 1, 2, 3, 4)
170
171 // look! it remembers :)
172 So(4, fooShouldHave(ds), 1, 2, 3, 4)
173 return nil
174 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
175
176 So(under.PutMulti.Total(), ShouldEqual, 1)
177
178 So(3, fooShouldHave(ds), 1, 2, 3, 4)
179 So(4, fooShouldHave(ds), 1, 2, 3, 4)
180 })
181
182 Convey("multi-level read/write", func() {
183 So(ds.RunInTransaction(func(c context.Context) e rror {
184 ds := datastore.Get(c)
185
186 So(3, fooShouldHave(ds), dataMultiRoot[2 ].Value)
187
188 So(3, fooSetTo(ds), 1, 2, 3, 4)
189 So(7, fooSetTo(ds))
190
191 vals := []*Foo{
192 {ID: 793},
193 {ID: 7},
194 {ID: 3},
195 {ID: 4},
196 }
197 So(ds.GetMulti(vals), ShouldResemble, er rors.MultiError{
198 datastore.ErrNoSuchEntity,
199 datastore.ErrNoSuchEntity,
200 nil,
201 nil,
202 })
203
204 So(vals[0].Value, ShouldBeNil)
205 So(vals[1].Value, ShouldBeNil)
206 So(vals[2].Value, ShouldResemble, []int6 4{1, 2, 3, 4})
207 So(vals[3].Value, ShouldResemble, dataSi ngleRoot[3].Value)
208
209 // inner, failing, transaction
210 So(ds.RunInTransaction(func(c context.Co ntext) error {
211 ds := datastore.Get(c)
212
213 // we can see stuff written in t he outer txn
214 So(7, fooShouldHave(ds))
215 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
216
217 So(3, fooSetTo(ds), 10, 20, 30, 40)
218
219 // disaster strikes!
220 return errors.New("whaaaa")
221 }, nil), ShouldErrLike, "whaaaa")
222
223 So(3, fooShouldHave(ds), 1, 2, 3, 4)
224
225 // inner, successful, transaction
226 So(ds.RunInTransaction(func(c context.Co ntext) error {
227 ds := datastore.Get(c)
228 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
229 So(3, fooSetTo(ds), 10, 20, 30, 40)
230 return nil
231 }, nil), ShouldBeNil)
232
233 // now we see it
234 So(3, fooShouldHave(ds), 10, 20, 30, 40)
235 return nil
236 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
237
238 So(under.PutMulti.Total(), ShouldEqual, 1)
239 So(under.DeleteMulti.Total(), ShouldEqual, 1)
240
241 // 'over' Put operations are amplified because t he inner transaction
242 // commits go through the 'over' filter on the o uter transaction. So it's
243 // # Puts + # inner txns.
244 So(over.PutMulti.Total(), ShouldEqual, 5)
245
246 So(7, fooShouldHave(ds))
247 So(3, fooShouldHave(ds), 10, 20, 30, 40)
248 })
249
250 Convey("can allocate IDs from an inner transaction", fun c() {
251 nums := []int64{4, 8, 15, 16, 23, 42}
252 k := (*datastore.Key)(nil)
253 So(ds.RunInTransaction(func(c context.Context) e rror {
254 ds := datastore.Get(c)
255
256 So(ds.RunInTransaction(func(c context.Co ntext) error {
257 ds := datastore.Get(c)
258 f := &Foo{Value: nums}
259 So(ds.Put(f), ShouldBeNil)
260 k = ds.KeyForObj(f)
261 return nil
262 }, nil), ShouldBeNil)
263
264 So(k.IntID(), fooShouldHave(ds), nums)
265
266 return nil
267 }, nil), ShouldBeNil)
268
269 So(k.IntID(), fooShouldHave(ds), nums)
270 })
271
272 Convey("inner txn too big allows outer txn", func() {
273 So(ds.RunInTransaction(func(c context.Context) e rror {
274 ds := datastore.Get(c)
275
276 So(18, fooSetTo(ds), hugeField)
277
278 So(ds.RunInTransaction(func(c context.Co ntext) error {
279 ds := datastore.Get(c)
280 So(ds.PutMulti(hugeData), Should BeNil)
281 return nil
282 }, nil), ShouldErrLike, ErrTransactionTo oLarge)
283
284 return nil
285 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
286
287 So(18, fooShouldHave(ds), hugeField)
288 })
289
290 Convey("outer txn too big prevents inner txn", func() {
291 So(ds.RunInTransaction(func(c context.Context) e rror {
292 ds := datastore.Get(c)
293
294 So(ds.PutMulti(hugeData), ShouldBeNil)
295
296 So(ds.RunInTransaction(func(c context.Co ntext) error {
297 panic("never!")
298 }, nil), ShouldErrLike, ErrTransactionTo oLarge)
299
300 return nil
301 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
302
303 So(1, fooShouldHave(ds), hugeField)
304 })
305
306 })
307
308 Convey("Bad", func() {
309
310 Convey("too many roots", func() {
311 So(ds.RunInTransaction(func(c context.Context) e rror {
312 ds := datastore.Get(c)
313
314 f := &Foo{ID: 7}
315 So(ds.Get(f), ShouldBeNil)
316 So(f, ShouldResemble, dataMultiRoot[6])
317
318 So(ds.RunInTransaction(func(c context.Co ntext) error {
319 return datastore.Get(c).Get(&Foo {ID: 6})
320 }, nil), ShouldErrLike, "too many entity groups")
321
322 f.Value = []int64{9}
323 So(ds.Put(f), ShouldBeNil)
324
325 return nil
326 }, nil), ShouldBeNil)
327
328 f := &Foo{ID: 7}
329 So(ds.Get(f), ShouldBeNil)
330 So(f.Value, ShouldResemble, []int64{9})
331 })
332
333 Convey("buffered errors never reach the datastore", func () {
334 So(ds.RunInTransaction(func(c context.Context) e rror {
335 ds := datastore.Get(c)
336
337 So(ds.Put(&Foo{ID: 1, Value: []int64{1, 2, 3, 4}}), ShouldBeNil)
338 return errors.New("boop")
339 }, nil), ShouldErrLike, "boop")
340 So(under.PutMulti.Total(), ShouldEqual, 0)
341 So(over.PutMulti.Successes(), ShouldEqual, 1)
342 })
343
344 })
345
346 })
347
348 Convey("Queries", t, func() {
349 Convey("Good", func() {
350 q := datastore.NewQuery("Foo").Ancestor(root)
351
352 Convey("normal", func() {
353 _, _, ds := mkds(dataSingleRoot)
354 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
355 Kind: "Foo",
356 Ancestor: true,
357 SortBy: []datastore.IndexColumn{
358 {Property: "Value"},
359 },
360 })
361
362 So(ds.RunInTransaction(func(c context.Context) e rror {
363 ds := datastore.Get(c)
364
365 q = q.Lt("Value", 400000000000000000)
366
367 vals := []*Foo{}
368 So(ds.GetAll(q, &vals), ShouldBeNil)
369 So(len(vals), ShouldEqual, 8)
370
371 count, err := ds.Count(q)
372 So(err, ShouldBeNil)
373 So(count, ShouldEqual, 8)
374
375 f := &Foo{ID: 1, Parent: root}
376 So(ds.Get(f), ShouldBeNil)
377 f.Value = append(f.Value, 100)
378 So(ds.Put(f), ShouldBeNil)
379
380 // Wowee, zowee, merged queries!
381 vals2 := []*Foo{}
382 So(ds.GetAll(q, &vals2), ShouldBeNil)
383 So(len(vals2), ShouldEqual, 9)
384 So(vals2[0], ShouldResemble, f)
385
386 vals2 = []*Foo{}
387 So(ds.GetAll(q.Limit(2).Offset(1), &vals 2), ShouldBeNil)
388 So(len(vals2), ShouldEqual, 2)
389 So(vals2, ShouldResemble, vals[:2])
390
391 return nil
392 }, nil), ShouldBeNil)
393 })
394
395 Convey("keysOnly", func() {
396 _, _, ds := mkds([]*Foo{
397 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
398 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
399 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
400 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
401 })
402
403 So(ds.RunInTransaction(func(c context.Context) e rror {
404 ds := datastore.Get(c)
405
406 q = q.Eq("Value", 1).KeysOnly(true)
407 vals := []*datastore.Key{}
408 So(ds.GetAll(q, &vals), ShouldBeNil)
409 So(len(vals), ShouldEqual, 3)
410 So(vals[2], ShouldResemble, ds.MakeKey(" Parent", 1, "Foo", 5))
411
412 // can remove keys
413 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
414 vals = []*datastore.Key{}
415 So(ds.GetAll(q, &vals), ShouldBeNil)
416 So(len(vals), ShouldEqual, 2)
417
418 // and add new ones
419 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{1, 7, 100}}), ShouldBeNil)
420 So(ds.Put(&Foo{ID: 7, Parent: root, Valu e: []int64{20, 1}}), ShouldBeNil)
421 vals = []*datastore.Key{}
422 So(ds.GetAll(q, &vals), ShouldBeNil)
423 So(len(vals), ShouldEqual, 4)
424
425 So(vals[0].IntID(), ShouldEqual, 1)
426 So(vals[1].IntID(), ShouldEqual, 4)
427 So(vals[2].IntID(), ShouldEqual, 5)
428 So(vals[3].IntID(), ShouldEqual, 7)
429
430 return nil
431 }, nil), ShouldBeNil)
432 })
433
434 Convey("project", func() {
435 _, _, ds := mkds([]*Foo{
436 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
437 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
438 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
439 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
440 })
441
442 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
443 Kind: "Foo",
444 Ancestor: true,
445 SortBy: []datastore.IndexColumn{
446 {Property: "Value"},
447 },
448 })
449
450 So(ds.RunInTransaction(func(c context.Context) e rror {
451 ds := datastore.Get(c)
452
453 count, err := ds.Count(q.Project("Value" ))
454 So(err, ShouldBeNil)
455 So(count, ShouldEqual, 24)
456
457 q = q.Project("Value").Offset(4).Limit(1 0)
458
459 vals := []datastore.PropertyMap{}
460 So(ds.GetAll(q, &vals), ShouldBeNil)
461 So(len(vals), ShouldEqual, 10)
462
463 expect := []struct {
464 id int64
465 val int64
466 }{
467 {2, 3},
468 {3, 3},
469 {4, 3},
470 {2, 4},
471 {3, 4},
472 {2, 5},
473 {3, 5},
474 {4, 5},
475 {2, 6},
476 {3, 6},
477 }
478
479 for i, pm := range vals {
480 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
481 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
482 }
483
484 // should remove 4 entries, but there ar e plenty more to fill
485 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
486
487 vals = []datastore.PropertyMap{}
488 So(ds.GetAll(q, &vals), ShouldBeNil)
489 So(len(vals), ShouldEqual, 10)
490
491 expect = []struct {
492 id int64
493 val int64
494 }{
495 // note (3, 3) and (4, 3) are co rrectly missing because deleting
496 // 2 removed two entries which a re hidden by the Offset(4).
497 {3, 4},
498 {3, 5},
499 {4, 5},
500 {3, 6},
501 {3, 7},
502 {4, 7},
503 {3, 8},
504 {3, 9},
505 {4, 9},
506 {4, 11},
507 }
508
509 for i, pm := range vals {
510 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
511 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
512 }
513
514 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{3, 9}}), ShouldBeNil)
515
516 vals = []datastore.PropertyMap{}
517 So(ds.GetAll(q, &vals), ShouldBeNil)
518 So(len(vals), ShouldEqual, 10)
519
520 expect = []struct {
521 id int64
522 val int64
523 }{
524 // 'invisible' {1, 3} entry bump s the {4, 3} into view.
525 {4, 3},
526 {3, 4},
527 {3, 5},
528 {4, 5},
529 {3, 6},
530 {3, 7},
531 {4, 7},
532 {3, 8},
533 {1, 9},
534 {3, 9},
535 {4, 9},
536 }
537
538 for i, pm := range vals {
539 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
540 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
541 }
542
543 return nil
544 }, nil), ShouldBeNil)
545
546 })
547
548 Convey("project+distinct", func() {
549 _, _, ds := mkds([]*Foo{
550 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
551 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
552 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
553 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
554 })
555
556 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
557 Kind: "Foo",
558 Ancestor: true,
559 SortBy: []datastore.IndexColumn{
560 {Property: "Value"},
561 },
562 })
563
564 So(ds.RunInTransaction(func(c context.Context) e rror {
565 ds := datastore.Get(c)
566
567 q = q.Project("Value").Distinct(true)
568
569 vals := []datastore.PropertyMap{}
570 So(ds.GetAll(q, &vals), ShouldBeNil)
571 So(len(vals), ShouldEqual, 13)
572
573 expect := []struct {
574 id int64
575 val int64
576 }{
577 {2, 1},
578 {2, 2},
579 {2, 3},
580 {2, 4},
581 {2, 5},
582 {2, 6},
583 {2, 7},
584 {3, 8},
585 {3, 9},
586 {4, 11},
587 {5, 70},
588 {4, 100},
589 {5, 101},
590 }
591
592 for i, pm := range vals {
593 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
594 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
595 }
596
597 return nil
598 }, nil), ShouldBeNil)
599 })
600
601 Convey("overwrite", func() {
602 data := []*Foo{
603 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
604 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
605 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}},
606 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
607 }
608
609 _, _, ds := mkds(data)
610
611 q = q.Eq("Value", 2, 3)
612
613 So(ds.RunInTransaction(func(c context.Context) e rror {
614 ds := datastore.Get(c)
615
616 vals := []*Foo{}
617 So(ds.GetAll(q, &vals), ShouldBeNil)
618 So(len(vals), ShouldEqual, 2)
619
620 So(vals[0], ShouldResemble, data[0])
621 So(vals[1], ShouldResemble, data[2])
622
623 foo2 := &Foo{ID: 2, Parent: root, Value: []int64{2, 3}}
624 So(ds.Put(foo2), ShouldBeNil)
625
626 vals = []*Foo{}
627 So(ds.GetAll(q, &vals), ShouldBeNil)
628 So(len(vals), ShouldEqual, 2)
629
630 So(vals[0], ShouldResemble, foo2)
631 So(vals[1], ShouldResemble, data[2])
632
633 foo1 := &Foo{ID: 1, Parent: root, Value: []int64{2, 3}}
634 So(ds.Put(foo1), ShouldBeNil)
635
636 vals = []*Foo{}
637 So(ds.GetAll(q, &vals), ShouldBeNil)
638 So(len(vals), ShouldEqual, 3)
639
640 So(vals[0], ShouldResemble, foo1)
641 So(vals[1], ShouldResemble, foo2)
642 So(vals[2], ShouldResemble, data[2])
643
644 return nil
645 }, nil), ShouldBeNil)
646 })
647
648 projectData := []*Foo{
649 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}, Sort: []string{"x", "z"}},
650 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}, Sort: []string{"b"}},
651 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}, Sort: []string{"aa", "a"}},
652 {ID: 5, Parent: root, Value: []int64{1, 70, 101} , Sort: []string{"c"}},
653 }
654
655 Convey("project+extra orders", func() {
656
657 _, _, ds := mkds(projectData)
658 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
659 Kind: "Foo",
660 Ancestor: true,
661 SortBy: []datastore.IndexColumn{
662 {Property: "Sort", Descending: t rue},
663 {Property: "Value", Descending: true},
664 },
665 })
666
667 q = q.Project("Value").Order("-Sort", "-Value"). Distinct(true)
668 So(ds.RunInTransaction(func(c context.Context) e rror {
669 ds = datastore.Get(c)
670
671 So(ds.Put(&Foo{
672 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
673 Sort: []string{"zz"}}), ShouldBe Nil)
674
675 vals := []datastore.PropertyMap{}
676 So(ds.GetAll(q, &vals), ShouldBeNil)
677
678 expect := []struct {
679 id int64
680 val int64
681 }{
682 {1, 1000},
683 {1, 1},
684 {1, 0},
685 {2, 7},
686 {2, 6},
687 {2, 5},
688 {2, 4},
689 {2, 3},
690 {2, 2},
691 {5, 101},
692 {5, 70},
693 {3, 9},
694 {3, 8},
695 {4, 100},
696 {4, 11},
697 }
698
699 for i, pm := range vals {
700 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
701 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
702 }
703
704 return nil
705 }, nil), ShouldBeNil)
706 })
707
708 Convey("buffered entity sorts before ineq, but after fir st parent entity", func() {
709 // If we got this wrong, we'd see Foo,3 come bef ore Foo,2. This might
710 // happen because we calculate the comparison st ring for each entity
711 // based on the whole entity, but we forgot to l imit the comparison
712 // string generation by the inequality criteria.
713 data := []*Foo{
714 {ID: 2, Parent: root, Value: []int64{2, 3, 5, 6}, Sort: []string{"z"}},
715 }
716
717 _, _, ds := mkds(data)
718 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
719 Kind: "Foo",
720 Ancestor: true,
721 SortBy: []datastore.IndexColumn{
722 {Property: "Value"},
723 },
724 })
725
726 q = q.Gt("Value", 2).Limit(2)
727
728 So(ds.RunInTransaction(func(c context.Context) e rror {
729 ds = datastore.Get(c)
730
731 foo1 := &Foo{ID: 3, Parent: root, Value: []int64{0, 2, 3, 4}}
732 So(ds.Put(foo1), ShouldBeNil)
733
734 vals := []*Foo{}
735 So(ds.GetAll(q, &vals), ShouldBeNil)
736 So(len(vals), ShouldEqual, 2)
737
738 So(vals[0], ShouldResemble, data[0])
739 So(vals[1], ShouldResemble, foo1)
740
741 return nil
742 }, nil), ShouldBeNil)
743 })
744
745 Convey("keysOnly+extra orders", func() {
746 _, _, ds := mkds(projectData)
747 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
748 Kind: "Foo",
749 Ancestor: true,
750 SortBy: []datastore.IndexColumn{
751 {Property: "Sort"},
752 },
753 })
754
755 q = q.Order("Sort").KeysOnly(true)
756
757 So(ds.RunInTransaction(func(c context.Context) e rror {
758 ds = datastore.Get(c)
759
760 So(ds.Put(&Foo{
761 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
762 Sort: []string{"x", "zz"}}), Sho uldBeNil)
763
764 So(ds.Put(&Foo{
765 ID: 2, Parent: root, Value: []in t64{0, 1, 1000},
766 Sort: []string{"zz", "zzz", "zzz z"}}), ShouldBeNil)
767
768 vals := []*datastore.Key{}
769 So(ds.GetAll(q, &vals), ShouldBeNil)
770 So(len(vals), ShouldEqual, 5)
771
772 So(vals, ShouldResemble, []*datastore.Ke y{
773 ds.MakeKey("Parent", 1, "Foo", 4 ),
774 ds.MakeKey("Parent", 1, "Foo", 3 ),
775 ds.MakeKey("Parent", 1, "Foo", 5 ),
776 ds.MakeKey("Parent", 1, "Foo", 1 ),
777 ds.MakeKey("Parent", 1, "Foo", 2 ),
778 })
779
780 return nil
781 }, nil), ShouldBeNil)
782 })
783
784 Convey("query accross nested transactions", func() {
785 _, _, ds := mkds(projectData)
786 q = q.Eq("Value", 2, 3)
787
788 foo1 := &Foo{ID: 1, Parent: root, Value: []int64 {2, 3}}
789 foo7 := &Foo{ID: 7, Parent: root, Value: []int64 {2, 3}}
790
791 So(ds.RunInTransaction(func(c context.Context) e rror {
792 ds := datastore.Get(c)
793
794 So(ds.Put(foo1), ShouldBeNil)
795
796 vals := []*Foo{}
797 So(ds.GetAll(q, &vals), ShouldBeNil)
798 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], projectData[2]})
799
800 So(ds.RunInTransaction(func(c context.Co ntext) error {
801 ds := datastore.Get(c)
802
803 vals := []*Foo{}
804 So(ds.GetAll(q, &vals), ShouldBe Nil)
805 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], projectData[2]})
806
807 So(ds.Delete(ds.MakeKey("Parent" , 1, "Foo", 4)), ShouldBeNil)
808 So(ds.Put(foo7), ShouldBeNil)
809
810 vals = []*Foo{}
811 So(ds.GetAll(q, &vals), ShouldBe Nil)
812 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], foo7})
813
814 return nil
815 }, nil), ShouldBeNil)
816
817 vals = []*Foo{}
818 So(ds.GetAll(q, &vals), ShouldBeNil)
819 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], foo7})
820
821 return nil
822 }, nil), ShouldBeNil)
823
824 vals := []*Foo{}
825 So(ds.GetAll(q, &vals), ShouldBeNil)
826 So(vals, ShouldResemble, []*Foo{foo1, projectDat a[0], foo7})
827
828 })
829
830 })
831
832 })
833
834 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698