|
- // Copyright 2017 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package firestore
-
- import (
- "math"
- "sort"
- "testing"
-
- "golang.org/x/net/context"
-
- "cloud.google.com/go/internal/pretty"
- pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
-
- tspb "github.com/golang/protobuf/ptypes/timestamp"
- "github.com/golang/protobuf/ptypes/wrappers"
- )
-
- func TestFilterToProto(t *testing.T) {
- for _, test := range []struct {
- in filter
- want *pb.StructuredQuery_Filter
- }{
- {
- filter{[]string{"a"}, ">", 1},
- &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_FieldFilter{
- FieldFilter: &pb.StructuredQuery_FieldFilter{
- Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
- Op: pb.StructuredQuery_FieldFilter_GREATER_THAN,
- Value: intval(1),
- },
- }},
- },
- {
- filter{[]string{"a"}, "==", nil},
- &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{
- UnaryFilter: &pb.StructuredQuery_UnaryFilter{
- OperandType: &pb.StructuredQuery_UnaryFilter_Field{
- Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
- },
- Op: pb.StructuredQuery_UnaryFilter_IS_NULL,
- },
- }},
- },
- {
- filter{[]string{"a"}, "==", math.NaN()},
- &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{
- UnaryFilter: &pb.StructuredQuery_UnaryFilter{
- OperandType: &pb.StructuredQuery_UnaryFilter_Field{
- Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
- },
- Op: pb.StructuredQuery_UnaryFilter_IS_NAN,
- },
- }},
- },
- } {
- got, err := test.in.toProto()
- if err != nil {
- t.Fatal(err)
- }
- if !testEqual(got, test.want) {
- t.Errorf("%+v:\ngot\n%v\nwant\n%v", test.in, pretty.Value(got), pretty.Value(test.want))
- }
- }
- }
-
- func TestQueryToProto(t *testing.T) {
- filtr := func(path []string, op string, val interface{}) *pb.StructuredQuery_Filter {
- f, err := filter{path, op, val}.toProto()
- if err != nil {
- t.Fatal(err)
- }
- return f
- }
-
- c := &Client{projectID: "P", databaseID: "DB"}
- coll := c.Collection("C")
- q := coll.Query
- type S struct {
- A int `firestore:"a"`
- }
- docsnap := &DocumentSnapshot{
- Ref: coll.Doc("D"),
- proto: &pb.Document{
- Fields: map[string]*pb.Value{"a": intval(7), "b": intval(8)},
- },
- }
- for _, test := range []struct {
- desc string
- in Query
- want *pb.StructuredQuery
- }{
- {
- desc: "q.Select()",
- in: q.Select(),
- want: &pb.StructuredQuery{
- Select: &pb.StructuredQuery_Projection{
- Fields: []*pb.StructuredQuery_FieldReference{fref1("__name__")},
- },
- },
- },
- {
- desc: `q.Select("a", "b")`,
- in: q.Select("a", "b"),
- want: &pb.StructuredQuery{
- Select: &pb.StructuredQuery_Projection{
- Fields: []*pb.StructuredQuery_FieldReference{fref1("a"), fref1("b")},
- },
- },
- },
- {
- desc: `q.Select("a", "b").Select("c")`,
- in: q.Select("a", "b").Select("c"), // last wins
- want: &pb.StructuredQuery{
- Select: &pb.StructuredQuery_Projection{
- Fields: []*pb.StructuredQuery_FieldReference{fref1("c")},
- },
- },
- },
- {
- desc: `q.SelectPaths([]string{"*"}, []string{"/"})`,
- in: q.SelectPaths([]string{"*"}, []string{"/"}),
- want: &pb.StructuredQuery{
- Select: &pb.StructuredQuery_Projection{
- Fields: []*pb.StructuredQuery_FieldReference{fref1("*"), fref1("/")},
- },
- },
- },
- {
- desc: `q.Where("a", ">", 5)`,
- in: q.Where("a", ">", 5),
- want: &pb.StructuredQuery{Where: filtr([]string{"a"}, ">", 5)},
- },
- {
- desc: `q.Where("a", "==", NaN)`,
- in: q.Where("a", "==", float32(math.NaN())),
- want: &pb.StructuredQuery{Where: filtr([]string{"a"}, "==", math.NaN())},
- },
- {
- desc: `q.Where("a", ">", 5).Where("b", "<", "foo")`,
- in: q.Where("a", ">", 5).Where("b", "<", "foo"),
- want: &pb.StructuredQuery{
- Where: &pb.StructuredQuery_Filter{
- FilterType: &pb.StructuredQuery_Filter_CompositeFilter{
- &pb.StructuredQuery_CompositeFilter{
- Op: pb.StructuredQuery_CompositeFilter_AND,
- Filters: []*pb.StructuredQuery_Filter{
- filtr([]string{"a"}, ">", 5), filtr([]string{"b"}, "<", "foo"),
- },
- },
- },
- },
- },
- },
- {
- desc: ` q.WherePath([]string{"/", "*"}, ">", 5)`,
- in: q.WherePath([]string{"/", "*"}, ">", 5),
- want: &pb.StructuredQuery{Where: filtr([]string{"/", "*"}, ">", 5)},
- },
- {
- desc: `q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc)`,
- in: q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
- {Field: fref1("~"), Direction: pb.StructuredQuery_ASCENDING},
- },
- },
- },
- {
- desc: `q.Offset(2).Limit(3)`,
- in: q.Offset(2).Limit(3),
- want: &pb.StructuredQuery{
- Offset: 2,
- Limit: &wrappers.Int32Value{Value: 3},
- },
- },
- {
- desc: `q.Offset(2).Limit(3).Limit(4).Offset(5)`,
- in: q.Offset(2).Limit(3).Limit(4).Offset(5), // last wins
- want: &pb.StructuredQuery{
- Offset: 5,
- Limit: &wrappers.Int32Value{Value: 4},
- },
- },
- {
- desc: `q.OrderBy("a", Asc).StartAt(7).EndBefore(9)`,
- in: q.OrderBy("a", Asc).StartAt(7).EndBefore(9),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7)},
- Before: true,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{intval(9)},
- Before: true,
- },
- },
- },
- {
- desc: `q.OrderBy("a", Asc).StartAt(7).EndAt(9)`,
- in: q.OrderBy("a", Asc).StartAt(7).EndAt(9),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7)},
- Before: true,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{intval(9)},
- Before: false,
- },
- },
- },
- {
- desc: `q.OrderBy("a", Asc).StartAfter(7).EndAt(9)`,
- in: q.OrderBy("a", Asc).StartAfter(7).EndAt(9),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7)},
- Before: false,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{intval(9)},
- Before: false,
- },
- },
- },
- {
- desc: `q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar")`,
- in: q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar"),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{refval(coll.parentPath + "/documents/C/foo")},
- Before: false,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{refval(coll.parentPath + "/documents/C/bar")},
- Before: true,
- },
- },
- },
- {
- desc: `q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10)`,
- in: q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("b"), Direction: pb.StructuredQuery_DESCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), intval(8)},
- Before: false,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{intval(9), intval(10)},
- Before: false,
- },
- },
- },
- {
- // last of StartAt/After wins, same for End
- desc: `q.OrderBy("a", Asc).StartAfter(1).StartAt(2).EndAt(3).EndBefore(4)`,
- in: q.OrderBy("a", Asc).
- StartAfter(1).StartAt(2).
- EndAt(3).EndBefore(4),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(2)},
- Before: true,
- },
- EndAt: &pb.Cursor{
- Values: []*pb.Value{intval(4)},
- Before: true,
- },
- },
- },
- // Start/End with DocumentSnapshot
- // These tests are from the "Document Snapshot Cursors" doc.
- {
- desc: `q.StartAt(docsnap)`,
- in: q.StartAt(docsnap),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- {
- desc: `q.OrderBy("a", Asc).StartAt(docsnap)`,
- in: q.OrderBy("a", Asc).StartAt(docsnap),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
-
- {
- desc: `q.OrderBy("a", Desc).StartAt(docsnap)`,
- in: q.OrderBy("a", Desc).StartAt(docsnap),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_DESCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- {
- desc: `q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap)`,
- in: q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap),
- want: &pb.StructuredQuery{
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
- {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), intval(8), refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- {
- desc: `q.Where("a", "==", 3).StartAt(docsnap)`,
- in: q.Where("a", "==", 3).StartAt(docsnap),
- want: &pb.StructuredQuery{
- Where: filtr([]string{"a"}, "==", 3),
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- {
- desc: `q.Where("a", "<", 3).StartAt(docsnap)`,
- in: q.Where("a", "<", 3).StartAt(docsnap),
- want: &pb.StructuredQuery{
- Where: filtr([]string{"a"}, "<", 3),
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- {
- desc: `q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap)`,
- in: q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap),
- want: &pb.StructuredQuery{
- Where: &pb.StructuredQuery_Filter{
- FilterType: &pb.StructuredQuery_Filter_CompositeFilter{
- &pb.StructuredQuery_CompositeFilter{
- Op: pb.StructuredQuery_CompositeFilter_AND,
- Filters: []*pb.StructuredQuery_Filter{
- filtr([]string{"b"}, "==", 1),
- filtr([]string{"a"}, "<", 3),
- },
- },
- },
- },
- OrderBy: []*pb.StructuredQuery_Order{
- {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
- {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
- },
- StartAt: &pb.Cursor{
- Values: []*pb.Value{intval(7), refval(coll.parentPath + "/documents/C/D")},
- Before: true,
- },
- },
- },
- } {
- got, err := test.in.toProto()
- if err != nil {
- t.Errorf("%s: %v", test.desc, err)
- continue
- }
- test.want.From = []*pb.StructuredQuery_CollectionSelector{{CollectionId: "C"}}
- if !testEqual(got, test.want) {
- t.Errorf("%s:\ngot\n%v\nwant\n%v", test.desc, pretty.Value(got), pretty.Value(test.want))
- }
- }
- }
-
- func fref1(s string) *pb.StructuredQuery_FieldReference {
- return fref([]string{s})
- }
-
- func TestQueryToProtoErrors(t *testing.T) {
- st := map[string]interface{}{"a": ServerTimestamp}
- del := map[string]interface{}{"a": Delete}
- c := &Client{projectID: "P", databaseID: "DB"}
- coll := c.Collection("C")
- docsnap := &DocumentSnapshot{
- Ref: coll.Doc("D"),
- proto: &pb.Document{
- Fields: map[string]*pb.Value{"a": intval(7)},
- },
- }
- q := coll.Query
- for _, query := range []Query{
- {}, // no collection ID
- q.Where("x", "!=", 1), // invalid operator
- q.Where("~", ">", 1), // invalid path
- q.WherePath([]string{"*", ""}, ">", 1), // invalid path
- q.StartAt(1), // no OrderBy
- q.StartAt(2).OrderBy("x", Asc).OrderBy("y", Desc), // wrong # OrderBy
- q.Select("*"), // invalid path
- q.SelectPaths([]string{"/", "", "~"}), // invalid path
- q.OrderBy("[", Asc), // invalid path
- q.OrderByPath([]string{""}, Desc), // invalid path
- q.Where("x", "==", st), // ServerTimestamp in filter
- q.OrderBy("a", Asc).StartAt(st), // ServerTimestamp in Start
- q.OrderBy("a", Asc).EndAt(st), // ServerTimestamp in End
- q.Where("x", "==", del), // Delete in filter
- q.OrderBy("a", Asc).StartAt(del), // Delete in Start
- q.OrderBy("a", Asc).EndAt(del), // Delete in End
- q.OrderBy(DocumentID, Asc).StartAt(7), // wrong type for __name__
- q.OrderBy(DocumentID, Asc).EndAt(7), // wrong type for __name__
- q.OrderBy("b", Asc).StartAt(docsnap), // doc snapshot does not have order-by field
- q.StartAt(docsnap).EndAt("x"), // mixed doc snapshot and fields
- q.StartAfter("x").EndBefore(docsnap), // mixed doc snapshot and fields
- } {
- _, err := query.toProto()
- if err == nil {
- t.Errorf("%+v: got nil, want error", query)
- }
- }
- }
-
- func TestQueryMethodsDoNotModifyReceiver(t *testing.T) {
- var empty Query
-
- q := Query{}
- _ = q.Select("a", "b")
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- q1 := q.Where("a", ">", 3)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
- // Extra check because Where appends to a slice.
- q1before := q.Where("a", ">", 3) // same as q1
- _ = q1.Where("b", "<", "foo")
- if !testEqual(q1, q1before) {
- t.Errorf("got %+v, want %+v", q1, q1before)
- }
-
- q = Query{}
- q1 = q.OrderBy("a", Asc)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
- // Extra check because Where appends to a slice.
- q1before = q.OrderBy("a", Asc) // same as q1
- _ = q1.OrderBy("b", Desc)
- if !testEqual(q1, q1before) {
- t.Errorf("got %+v, want %+v", q1, q1before)
- }
-
- q = Query{}
- _ = q.Offset(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- _ = q.Limit(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- _ = q.StartAt(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- _ = q.StartAfter(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- _ = q.EndAt(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
-
- q = Query{}
- _ = q.EndBefore(5)
- if !testEqual(q, empty) {
- t.Errorf("got %+v, want empty", q)
- }
- }
-
- func TestQueryFromCollectionRef(t *testing.T) {
- c := &Client{}
- coll := c.Collection("C")
- got := coll.Select("x").Offset(8)
- want := Query{
- c: c,
- parentPath: c.path(),
- collectionID: "C",
- selection: []FieldPath{{"x"}},
- offset: 8,
- }
- if !testEqual(got, want) {
- t.Fatalf("got %+v, want %+v", got, want)
- }
- }
-
- func TestQueryGetAll(t *testing.T) {
- // This implicitly tests DocumentIterator as well.
- const dbPath = "projects/projectID/databases/(default)"
- ctx := context.Background()
- c, srv := newMock(t)
- docNames := []string{"C/a", "C/b"}
- wantPBDocs := []*pb.Document{
- {
- Name: dbPath + "/documents/" + docNames[0],
- CreateTime: aTimestamp,
- UpdateTime: aTimestamp,
- Fields: map[string]*pb.Value{"f": intval(2)},
- },
- {
- Name: dbPath + "/documents/" + docNames[1],
- CreateTime: aTimestamp2,
- UpdateTime: aTimestamp3,
- Fields: map[string]*pb.Value{"f": intval(1)},
- },
- }
- wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2}
- srv.addRPC(nil, []interface{}{
- &pb.RunQueryResponse{Document: wantPBDocs[0], ReadTime: aTimestamp},
- &pb.RunQueryResponse{Document: wantPBDocs[1], ReadTime: aTimestamp2},
- })
- gotDocs, err := c.Collection("C").Documents(ctx).GetAll()
- if err != nil {
- t.Fatal(err)
- }
- if got, want := len(gotDocs), len(wantPBDocs); got != want {
- t.Errorf("got %d docs, wanted %d", got, want)
- }
- for i, got := range gotDocs {
- want, err := newDocumentSnapshot(c.Doc(docNames[i]), wantPBDocs[i], c, wantReadTimes[i])
- if err != nil {
- t.Fatal(err)
- }
- if !testEqual(got, want) {
- // avoid writing a cycle
- got.c = nil
- want.c = nil
- t.Errorf("#%d: got %+v, want %+v", i, pretty.Value(got), pretty.Value(want))
- }
- }
- }
-
- func TestQueryCompareFunc(t *testing.T) {
- mv := func(fields ...interface{}) map[string]*pb.Value {
- m := map[string]*pb.Value{}
- for i := 0; i < len(fields); i += 2 {
- m[fields[i].(string)] = fields[i+1].(*pb.Value)
- }
- return m
- }
- snap := func(ref *DocumentRef, fields map[string]*pb.Value) *DocumentSnapshot {
- return &DocumentSnapshot{Ref: ref, proto: &pb.Document{Fields: fields}}
- }
-
- c := &Client{}
- coll := c.Collection("C")
- doc1 := coll.Doc("doc1")
- doc2 := coll.Doc("doc2")
- doc3 := coll.Doc("doc3")
- doc4 := coll.Doc("doc4")
- for _, test := range []struct {
- q Query
- in []*DocumentSnapshot
- want []*DocumentSnapshot
- }{
- {
- q: coll.OrderBy("foo", Asc),
- in: []*DocumentSnapshot{
- snap(doc3, mv("foo", intval(2))),
- snap(doc4, mv("foo", intval(1))),
- snap(doc2, mv("foo", intval(2))),
- },
- want: []*DocumentSnapshot{
- snap(doc4, mv("foo", intval(1))),
- snap(doc2, mv("foo", intval(2))),
- snap(doc3, mv("foo", intval(2))),
- },
- },
- {
- q: coll.OrderBy("foo", Desc),
- in: []*DocumentSnapshot{
- snap(doc3, mv("foo", intval(2))),
- snap(doc4, mv("foo", intval(1))),
- snap(doc2, mv("foo", intval(2))),
- },
- want: []*DocumentSnapshot{
- snap(doc3, mv("foo", intval(2))),
- snap(doc2, mv("foo", intval(2))),
- snap(doc4, mv("foo", intval(1))),
- },
- },
- {
- q: coll.OrderBy("foo.bar", Asc),
- in: []*DocumentSnapshot{
- snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
- snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
- snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
- },
- want: []*DocumentSnapshot{
- snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
- snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
- snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
- },
- },
- {
- q: coll.OrderBy("foo.bar", Desc),
- in: []*DocumentSnapshot{
- snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
- snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
- snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
- },
- want: []*DocumentSnapshot{
- snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
- snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
- snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
- },
- },
- } {
- got := append([]*DocumentSnapshot(nil), test.in...)
- sort.Sort(byQuery{test.q.compareFunc(), got})
- if diff := testDiff(got, test.want); diff != "" {
- t.Errorf("%+v: %s", test.q, diff)
- }
- }
-
- // Want error on missing field.
- q := coll.OrderBy("bar", Asc)
- if q.err != nil {
- t.Fatalf("bad query: %v", q.err)
- }
- cf := q.compareFunc()
- s := snap(doc1, mv("foo", intval(1)))
- if _, err := cf(s, s); err == nil {
- t.Error("got nil, want error")
- }
- }
-
- type byQuery struct {
- compare func(d1, d2 *DocumentSnapshot) (int, error)
- docs []*DocumentSnapshot
- }
-
- func (b byQuery) Len() int { return len(b.docs) }
- func (b byQuery) Swap(i, j int) { b.docs[i], b.docs[j] = b.docs[j], b.docs[i] }
- func (b byQuery) Less(i, j int) bool {
- c, err := b.compare(b.docs[i], b.docs[j])
- if err != nil {
- panic(err)
- }
- return c < 0
- }
|