You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

857 lines
24 KiB

  1. // Copyright 2017 Google LLC
  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 firestore
  15. import (
  16. "context"
  17. "math"
  18. "sort"
  19. "testing"
  20. "cloud.google.com/go/internal/pretty"
  21. tspb "github.com/golang/protobuf/ptypes/timestamp"
  22. "github.com/golang/protobuf/ptypes/wrappers"
  23. pb "google.golang.org/genproto/googleapis/firestore/v1"
  24. )
  25. func TestFilterToProto(t *testing.T) {
  26. for _, test := range []struct {
  27. in filter
  28. want *pb.StructuredQuery_Filter
  29. }{
  30. {
  31. filter{[]string{"a"}, ">", 1},
  32. &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_FieldFilter{
  33. FieldFilter: &pb.StructuredQuery_FieldFilter{
  34. Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
  35. Op: pb.StructuredQuery_FieldFilter_GREATER_THAN,
  36. Value: intval(1),
  37. },
  38. }},
  39. },
  40. {
  41. filter{[]string{"a"}, "==", nil},
  42. &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{
  43. UnaryFilter: &pb.StructuredQuery_UnaryFilter{
  44. OperandType: &pb.StructuredQuery_UnaryFilter_Field{
  45. Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
  46. },
  47. Op: pb.StructuredQuery_UnaryFilter_IS_NULL,
  48. },
  49. }},
  50. },
  51. {
  52. filter{[]string{"a"}, "==", math.NaN()},
  53. &pb.StructuredQuery_Filter{FilterType: &pb.StructuredQuery_Filter_UnaryFilter{
  54. UnaryFilter: &pb.StructuredQuery_UnaryFilter{
  55. OperandType: &pb.StructuredQuery_UnaryFilter_Field{
  56. Field: &pb.StructuredQuery_FieldReference{FieldPath: "a"},
  57. },
  58. Op: pb.StructuredQuery_UnaryFilter_IS_NAN,
  59. },
  60. }},
  61. },
  62. } {
  63. got, err := test.in.toProto()
  64. if err != nil {
  65. t.Fatal(err)
  66. }
  67. if !testEqual(got, test.want) {
  68. t.Errorf("%+v:\ngot\n%v\nwant\n%v", test.in, pretty.Value(got), pretty.Value(test.want))
  69. }
  70. }
  71. }
  72. func TestQueryToProto(t *testing.T) {
  73. filtr := func(path []string, op string, val interface{}) *pb.StructuredQuery_Filter {
  74. f, err := filter{path, op, val}.toProto()
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. return f
  79. }
  80. c := &Client{projectID: "P", databaseID: "DB"}
  81. coll := c.Collection("C")
  82. q := coll.Query
  83. docsnap := &DocumentSnapshot{
  84. Ref: coll.Doc("D"),
  85. proto: &pb.Document{
  86. Fields: map[string]*pb.Value{"a": intval(7), "b": intval(8), "c": arrayval(intval(1), intval(2))},
  87. },
  88. }
  89. for _, test := range []struct {
  90. desc string
  91. in Query
  92. want *pb.StructuredQuery
  93. }{
  94. {
  95. desc: "q.Select()",
  96. in: q.Select(),
  97. want: &pb.StructuredQuery{
  98. Select: &pb.StructuredQuery_Projection{
  99. Fields: []*pb.StructuredQuery_FieldReference{fref1("__name__")},
  100. },
  101. },
  102. },
  103. {
  104. desc: `q.Select("a", "b")`,
  105. in: q.Select("a", "b"),
  106. want: &pb.StructuredQuery{
  107. Select: &pb.StructuredQuery_Projection{
  108. Fields: []*pb.StructuredQuery_FieldReference{fref1("a"), fref1("b")},
  109. },
  110. },
  111. },
  112. {
  113. desc: `q.Select("a", "b").Select("c")`,
  114. in: q.Select("a", "b").Select("c"), // last wins
  115. want: &pb.StructuredQuery{
  116. Select: &pb.StructuredQuery_Projection{
  117. Fields: []*pb.StructuredQuery_FieldReference{fref1("c")},
  118. },
  119. },
  120. },
  121. {
  122. desc: `q.SelectPaths([]string{"*"}, []string{"/"})`,
  123. in: q.SelectPaths([]string{"*"}, []string{"/"}),
  124. want: &pb.StructuredQuery{
  125. Select: &pb.StructuredQuery_Projection{
  126. Fields: []*pb.StructuredQuery_FieldReference{fref1("*"), fref1("/")},
  127. },
  128. },
  129. },
  130. {
  131. desc: `q.Where("a", ">", 5)`,
  132. in: q.Where("a", ">", 5),
  133. want: &pb.StructuredQuery{Where: filtr([]string{"a"}, ">", 5)},
  134. },
  135. {
  136. desc: `q.Where("a", "==", NaN)`,
  137. in: q.Where("a", "==", float32(math.NaN())),
  138. want: &pb.StructuredQuery{Where: filtr([]string{"a"}, "==", math.NaN())},
  139. },
  140. {
  141. desc: `q.Where("c", "array-contains", 1)`,
  142. in: q.Where("c", "array-contains", 1),
  143. want: &pb.StructuredQuery{Where: filtr([]string{"c"}, "array-contains", 1)},
  144. },
  145. {
  146. desc: `q.Where("a", ">", 5).Where("b", "<", "foo")`,
  147. in: q.Where("a", ">", 5).Where("b", "<", "foo"),
  148. want: &pb.StructuredQuery{
  149. Where: &pb.StructuredQuery_Filter{
  150. FilterType: &pb.StructuredQuery_Filter_CompositeFilter{
  151. &pb.StructuredQuery_CompositeFilter{
  152. Op: pb.StructuredQuery_CompositeFilter_AND,
  153. Filters: []*pb.StructuredQuery_Filter{
  154. filtr([]string{"a"}, ">", 5), filtr([]string{"b"}, "<", "foo"),
  155. },
  156. },
  157. },
  158. },
  159. },
  160. },
  161. {
  162. desc: ` q.WherePath([]string{"/", "*"}, ">", 5)`,
  163. in: q.WherePath([]string{"/", "*"}, ">", 5),
  164. want: &pb.StructuredQuery{Where: filtr([]string{"/", "*"}, ">", 5)},
  165. },
  166. {
  167. desc: `q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc)`,
  168. in: q.OrderBy("b", Asc).OrderBy("a", Desc).OrderByPath([]string{"~"}, Asc),
  169. want: &pb.StructuredQuery{
  170. OrderBy: []*pb.StructuredQuery_Order{
  171. {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING},
  172. {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
  173. {Field: fref1("~"), Direction: pb.StructuredQuery_ASCENDING},
  174. },
  175. },
  176. },
  177. {
  178. desc: `q.Offset(2).Limit(3)`,
  179. in: q.Offset(2).Limit(3),
  180. want: &pb.StructuredQuery{
  181. Offset: 2,
  182. Limit: &wrappers.Int32Value{Value: 3},
  183. },
  184. },
  185. {
  186. desc: `q.Offset(2).Limit(3).Limit(4).Offset(5)`,
  187. in: q.Offset(2).Limit(3).Limit(4).Offset(5), // last wins
  188. want: &pb.StructuredQuery{
  189. Offset: 5,
  190. Limit: &wrappers.Int32Value{Value: 4},
  191. },
  192. },
  193. {
  194. desc: `q.OrderBy("a", Asc).StartAt(7).EndBefore(9)`,
  195. in: q.OrderBy("a", Asc).StartAt(7).EndBefore(9),
  196. want: &pb.StructuredQuery{
  197. OrderBy: []*pb.StructuredQuery_Order{
  198. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  199. },
  200. StartAt: &pb.Cursor{
  201. Values: []*pb.Value{intval(7)},
  202. Before: true,
  203. },
  204. EndAt: &pb.Cursor{
  205. Values: []*pb.Value{intval(9)},
  206. Before: true,
  207. },
  208. },
  209. },
  210. {
  211. desc: `q.OrderBy("a", Asc).StartAt(7).EndAt(9)`,
  212. in: q.OrderBy("a", Asc).StartAt(7).EndAt(9),
  213. want: &pb.StructuredQuery{
  214. OrderBy: []*pb.StructuredQuery_Order{
  215. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  216. },
  217. StartAt: &pb.Cursor{
  218. Values: []*pb.Value{intval(7)},
  219. Before: true,
  220. },
  221. EndAt: &pb.Cursor{
  222. Values: []*pb.Value{intval(9)},
  223. Before: false,
  224. },
  225. },
  226. },
  227. {
  228. desc: `q.OrderBy("a", Asc).StartAfter(7).EndAt(9)`,
  229. in: q.OrderBy("a", Asc).StartAfter(7).EndAt(9),
  230. want: &pb.StructuredQuery{
  231. OrderBy: []*pb.StructuredQuery_Order{
  232. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  233. },
  234. StartAt: &pb.Cursor{
  235. Values: []*pb.Value{intval(7)},
  236. Before: false,
  237. },
  238. EndAt: &pb.Cursor{
  239. Values: []*pb.Value{intval(9)},
  240. Before: false,
  241. },
  242. },
  243. },
  244. {
  245. desc: `q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar")`,
  246. in: q.OrderBy(DocumentID, Asc).StartAfter("foo").EndBefore("bar"),
  247. want: &pb.StructuredQuery{
  248. OrderBy: []*pb.StructuredQuery_Order{
  249. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  250. },
  251. StartAt: &pb.Cursor{
  252. Values: []*pb.Value{refval(coll.parentPath + "/C/foo")},
  253. Before: false,
  254. },
  255. EndAt: &pb.Cursor{
  256. Values: []*pb.Value{refval(coll.parentPath + "/C/bar")},
  257. Before: true,
  258. },
  259. },
  260. },
  261. {
  262. desc: `q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10)`,
  263. in: q.OrderBy("a", Asc).OrderBy("b", Desc).StartAfter(7, 8).EndAt(9, 10),
  264. want: &pb.StructuredQuery{
  265. OrderBy: []*pb.StructuredQuery_Order{
  266. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  267. {Field: fref1("b"), Direction: pb.StructuredQuery_DESCENDING},
  268. },
  269. StartAt: &pb.Cursor{
  270. Values: []*pb.Value{intval(7), intval(8)},
  271. Before: false,
  272. },
  273. EndAt: &pb.Cursor{
  274. Values: []*pb.Value{intval(9), intval(10)},
  275. Before: false,
  276. },
  277. },
  278. },
  279. {
  280. // last of StartAt/After wins, same for End
  281. desc: `q.OrderBy("a", Asc).StartAfter(1).StartAt(2).EndAt(3).EndBefore(4)`,
  282. in: q.OrderBy("a", Asc).
  283. StartAfter(1).StartAt(2).
  284. EndAt(3).EndBefore(4),
  285. want: &pb.StructuredQuery{
  286. OrderBy: []*pb.StructuredQuery_Order{
  287. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  288. },
  289. StartAt: &pb.Cursor{
  290. Values: []*pb.Value{intval(2)},
  291. Before: true,
  292. },
  293. EndAt: &pb.Cursor{
  294. Values: []*pb.Value{intval(4)},
  295. Before: true,
  296. },
  297. },
  298. },
  299. // Start/End with DocumentSnapshot
  300. // These tests are from the "Document Snapshot Cursors" doc.
  301. {
  302. desc: `q.StartAt(docsnap)`,
  303. in: q.StartAt(docsnap),
  304. want: &pb.StructuredQuery{
  305. OrderBy: []*pb.StructuredQuery_Order{
  306. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  307. },
  308. StartAt: &pb.Cursor{
  309. Values: []*pb.Value{refval(coll.parentPath + "/C/D")},
  310. Before: true,
  311. },
  312. },
  313. },
  314. {
  315. desc: `q.OrderBy("a", Asc).StartAt(docsnap)`,
  316. in: q.OrderBy("a", Asc).StartAt(docsnap),
  317. want: &pb.StructuredQuery{
  318. OrderBy: []*pb.StructuredQuery_Order{
  319. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  320. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  321. },
  322. StartAt: &pb.Cursor{
  323. Values: []*pb.Value{intval(7), refval(coll.parentPath + "/C/D")},
  324. Before: true,
  325. },
  326. },
  327. },
  328. {
  329. desc: `q.OrderBy("a", Desc).StartAt(docsnap)`,
  330. in: q.OrderBy("a", Desc).StartAt(docsnap),
  331. want: &pb.StructuredQuery{
  332. OrderBy: []*pb.StructuredQuery_Order{
  333. {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
  334. {Field: fref1("__name__"), Direction: pb.StructuredQuery_DESCENDING},
  335. },
  336. StartAt: &pb.Cursor{
  337. Values: []*pb.Value{intval(7), refval(coll.parentPath + "/C/D")},
  338. Before: true,
  339. },
  340. },
  341. },
  342. {
  343. desc: `q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap)`,
  344. in: q.OrderBy("a", Desc).OrderBy("b", Asc).StartAt(docsnap),
  345. want: &pb.StructuredQuery{
  346. OrderBy: []*pb.StructuredQuery_Order{
  347. {Field: fref1("a"), Direction: pb.StructuredQuery_DESCENDING},
  348. {Field: fref1("b"), Direction: pb.StructuredQuery_ASCENDING},
  349. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  350. },
  351. StartAt: &pb.Cursor{
  352. Values: []*pb.Value{intval(7), intval(8), refval(coll.parentPath + "/C/D")},
  353. Before: true,
  354. },
  355. },
  356. },
  357. {
  358. desc: `q.Where("a", "==", 3).StartAt(docsnap)`,
  359. in: q.Where("a", "==", 3).StartAt(docsnap),
  360. want: &pb.StructuredQuery{
  361. Where: filtr([]string{"a"}, "==", 3),
  362. OrderBy: []*pb.StructuredQuery_Order{
  363. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  364. },
  365. StartAt: &pb.Cursor{
  366. Values: []*pb.Value{refval(coll.parentPath + "/C/D")},
  367. Before: true,
  368. },
  369. },
  370. },
  371. {
  372. desc: `q.Where("a", "<", 3).StartAt(docsnap)`,
  373. in: q.Where("a", "<", 3).StartAt(docsnap),
  374. want: &pb.StructuredQuery{
  375. Where: filtr([]string{"a"}, "<", 3),
  376. OrderBy: []*pb.StructuredQuery_Order{
  377. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  378. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  379. },
  380. StartAt: &pb.Cursor{
  381. Values: []*pb.Value{intval(7), refval(coll.parentPath + "/C/D")},
  382. Before: true,
  383. },
  384. },
  385. },
  386. {
  387. desc: `q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap)`,
  388. in: q.Where("b", "==", 1).Where("a", "<", 3).StartAt(docsnap),
  389. want: &pb.StructuredQuery{
  390. Where: &pb.StructuredQuery_Filter{
  391. FilterType: &pb.StructuredQuery_Filter_CompositeFilter{
  392. &pb.StructuredQuery_CompositeFilter{
  393. Op: pb.StructuredQuery_CompositeFilter_AND,
  394. Filters: []*pb.StructuredQuery_Filter{
  395. filtr([]string{"b"}, "==", 1),
  396. filtr([]string{"a"}, "<", 3),
  397. },
  398. },
  399. },
  400. },
  401. OrderBy: []*pb.StructuredQuery_Order{
  402. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  403. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  404. },
  405. StartAt: &pb.Cursor{
  406. Values: []*pb.Value{intval(7), refval(coll.parentPath + "/C/D")},
  407. Before: true,
  408. },
  409. },
  410. },
  411. } {
  412. got, err := test.in.toProto()
  413. if err != nil {
  414. t.Fatalf("%s: %v", test.desc, err)
  415. continue
  416. }
  417. test.want.From = []*pb.StructuredQuery_CollectionSelector{{CollectionId: "C"}}
  418. if !testEqual(got, test.want) {
  419. t.Fatalf("%s:\ngot\n%v\nwant\n%v", test.desc, pretty.Value(got), pretty.Value(test.want))
  420. }
  421. }
  422. }
  423. func fref1(s string) *pb.StructuredQuery_FieldReference {
  424. return fref([]string{s})
  425. }
  426. func TestQueryToProtoErrors(t *testing.T) {
  427. st := map[string]interface{}{"a": ServerTimestamp}
  428. del := map[string]interface{}{"a": Delete}
  429. c := &Client{projectID: "P", databaseID: "DB"}
  430. coll := c.Collection("C")
  431. docsnap := &DocumentSnapshot{
  432. Ref: coll.Doc("D"),
  433. proto: &pb.Document{
  434. Fields: map[string]*pb.Value{"a": intval(7)},
  435. },
  436. }
  437. q := coll.Query
  438. for i, query := range []Query{
  439. {}, // no collection ID
  440. q.Where("x", "!=", 1), // invalid operator
  441. q.Where("~", ">", 1), // invalid path
  442. q.WherePath([]string{"*", ""}, ">", 1), // invalid path
  443. q.StartAt(1), // no OrderBy
  444. q.StartAt(2).OrderBy("x", Asc).OrderBy("y", Desc), // wrong # OrderBy
  445. q.Select("*"), // invalid path
  446. q.SelectPaths([]string{"/", "", "~"}), // invalid path
  447. q.OrderBy("[", Asc), // invalid path
  448. q.OrderByPath([]string{""}, Desc), // invalid path
  449. q.Where("x", "==", st), // ServerTimestamp in filter
  450. q.OrderBy("a", Asc).StartAt(st), // ServerTimestamp in Start
  451. q.OrderBy("a", Asc).EndAt(st), // ServerTimestamp in End
  452. q.Where("x", "==", del), // Delete in filter
  453. q.OrderBy("a", Asc).StartAt(del), // Delete in Start
  454. q.OrderBy("a", Asc).EndAt(del), // Delete in End
  455. q.OrderBy(DocumentID, Asc).StartAt(7), // wrong type for __name__
  456. q.OrderBy(DocumentID, Asc).EndAt(7), // wrong type for __name__
  457. q.OrderBy("b", Asc).StartAt(docsnap), // doc snapshot does not have order-by field
  458. q.StartAt(docsnap).EndAt("x"), // mixed doc snapshot and fields
  459. q.StartAfter("x").EndBefore(docsnap), // mixed doc snapshot and fields
  460. } {
  461. _, err := query.toProto()
  462. if err == nil {
  463. t.Errorf("query %d \"%+v\": got nil, want error", i, query)
  464. }
  465. }
  466. }
  467. func TestQueryMethodsDoNotModifyReceiver(t *testing.T) {
  468. var empty Query
  469. q := Query{}
  470. _ = q.Select("a", "b")
  471. if !testEqual(q, empty) {
  472. t.Errorf("got %+v, want empty", q)
  473. }
  474. q = Query{}
  475. q1 := q.Where("a", ">", 3)
  476. if !testEqual(q, empty) {
  477. t.Errorf("got %+v, want empty", q)
  478. }
  479. // Extra check because Where appends to a slice.
  480. q1before := q.Where("a", ">", 3) // same as q1
  481. _ = q1.Where("b", "<", "foo")
  482. if !testEqual(q1, q1before) {
  483. t.Errorf("got %+v, want %+v", q1, q1before)
  484. }
  485. q = Query{}
  486. q1 = q.OrderBy("a", Asc)
  487. if !testEqual(q, empty) {
  488. t.Errorf("got %+v, want empty", q)
  489. }
  490. // Extra check because Where appends to a slice.
  491. q1before = q.OrderBy("a", Asc) // same as q1
  492. _ = q1.OrderBy("b", Desc)
  493. if !testEqual(q1, q1before) {
  494. t.Errorf("got %+v, want %+v", q1, q1before)
  495. }
  496. q = Query{}
  497. _ = q.Offset(5)
  498. if !testEqual(q, empty) {
  499. t.Errorf("got %+v, want empty", q)
  500. }
  501. q = Query{}
  502. _ = q.Limit(5)
  503. if !testEqual(q, empty) {
  504. t.Errorf("got %+v, want empty", q)
  505. }
  506. q = Query{}
  507. _ = q.StartAt(5)
  508. if !testEqual(q, empty) {
  509. t.Errorf("got %+v, want empty", q)
  510. }
  511. q = Query{}
  512. _ = q.StartAfter(5)
  513. if !testEqual(q, empty) {
  514. t.Errorf("got %+v, want empty", q)
  515. }
  516. q = Query{}
  517. _ = q.EndAt(5)
  518. if !testEqual(q, empty) {
  519. t.Errorf("got %+v, want empty", q)
  520. }
  521. q = Query{}
  522. _ = q.EndBefore(5)
  523. if !testEqual(q, empty) {
  524. t.Errorf("got %+v, want empty", q)
  525. }
  526. }
  527. func TestQueryFromCollectionRef(t *testing.T) {
  528. c := &Client{projectID: "P", databaseID: "D"}
  529. coll := c.Collection("C")
  530. got := coll.Select("x").Offset(8)
  531. want := Query{
  532. c: c,
  533. parentPath: c.path() + "/documents",
  534. path: "projects/P/databases/D/documents/C",
  535. collectionID: "C",
  536. selection: []FieldPath{{"x"}},
  537. offset: 8,
  538. }
  539. if !testEqual(got, want) {
  540. t.Fatalf("\ngot %+v, \nwant %+v", got, want)
  541. }
  542. }
  543. func TestQueryGetAll(t *testing.T) {
  544. // This implicitly tests DocumentIterator as well.
  545. const dbPath = "projects/projectID/databases/(default)"
  546. ctx := context.Background()
  547. c, srv := newMock(t)
  548. docNames := []string{"C/a", "C/b"}
  549. wantPBDocs := []*pb.Document{
  550. {
  551. Name: dbPath + "/documents/" + docNames[0],
  552. CreateTime: aTimestamp,
  553. UpdateTime: aTimestamp,
  554. Fields: map[string]*pb.Value{"f": intval(2)},
  555. },
  556. {
  557. Name: dbPath + "/documents/" + docNames[1],
  558. CreateTime: aTimestamp2,
  559. UpdateTime: aTimestamp3,
  560. Fields: map[string]*pb.Value{"f": intval(1)},
  561. },
  562. }
  563. wantReadTimes := []*tspb.Timestamp{aTimestamp, aTimestamp2}
  564. srv.addRPC(nil, []interface{}{
  565. &pb.RunQueryResponse{Document: wantPBDocs[0], ReadTime: aTimestamp},
  566. &pb.RunQueryResponse{Document: wantPBDocs[1], ReadTime: aTimestamp2},
  567. })
  568. gotDocs, err := c.Collection("C").Documents(ctx).GetAll()
  569. if err != nil {
  570. t.Fatal(err)
  571. }
  572. if got, want := len(gotDocs), len(wantPBDocs); got != want {
  573. t.Errorf("got %d docs, wanted %d", got, want)
  574. }
  575. for i, got := range gotDocs {
  576. want, err := newDocumentSnapshot(c.Doc(docNames[i]), wantPBDocs[i], c, wantReadTimes[i])
  577. if err != nil {
  578. t.Fatal(err)
  579. }
  580. if !testEqual(got, want) {
  581. // avoid writing a cycle
  582. got.c = nil
  583. want.c = nil
  584. t.Errorf("#%d: got %+v, want %+v", i, pretty.Value(got), pretty.Value(want))
  585. }
  586. }
  587. }
  588. func TestQueryCompareFunc(t *testing.T) {
  589. mv := func(fields ...interface{}) map[string]*pb.Value {
  590. m := map[string]*pb.Value{}
  591. for i := 0; i < len(fields); i += 2 {
  592. m[fields[i].(string)] = fields[i+1].(*pb.Value)
  593. }
  594. return m
  595. }
  596. snap := func(ref *DocumentRef, fields map[string]*pb.Value) *DocumentSnapshot {
  597. return &DocumentSnapshot{Ref: ref, proto: &pb.Document{Fields: fields}}
  598. }
  599. c := &Client{}
  600. coll := c.Collection("C")
  601. doc1 := coll.Doc("doc1")
  602. doc2 := coll.Doc("doc2")
  603. doc3 := coll.Doc("doc3")
  604. doc4 := coll.Doc("doc4")
  605. for _, test := range []struct {
  606. q Query
  607. in []*DocumentSnapshot
  608. want []*DocumentSnapshot
  609. }{
  610. {
  611. q: coll.OrderBy("foo", Asc),
  612. in: []*DocumentSnapshot{
  613. snap(doc3, mv("foo", intval(2))),
  614. snap(doc4, mv("foo", intval(1))),
  615. snap(doc2, mv("foo", intval(2))),
  616. },
  617. want: []*DocumentSnapshot{
  618. snap(doc4, mv("foo", intval(1))),
  619. snap(doc2, mv("foo", intval(2))),
  620. snap(doc3, mv("foo", intval(2))),
  621. },
  622. },
  623. {
  624. q: coll.OrderBy("foo", Desc),
  625. in: []*DocumentSnapshot{
  626. snap(doc3, mv("foo", intval(2))),
  627. snap(doc4, mv("foo", intval(1))),
  628. snap(doc2, mv("foo", intval(2))),
  629. },
  630. want: []*DocumentSnapshot{
  631. snap(doc3, mv("foo", intval(2))),
  632. snap(doc2, mv("foo", intval(2))),
  633. snap(doc4, mv("foo", intval(1))),
  634. },
  635. },
  636. {
  637. q: coll.OrderBy("foo.bar", Asc),
  638. in: []*DocumentSnapshot{
  639. snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
  640. snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
  641. snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
  642. },
  643. want: []*DocumentSnapshot{
  644. snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
  645. snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
  646. snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
  647. },
  648. },
  649. {
  650. q: coll.OrderBy("foo.bar", Desc),
  651. in: []*DocumentSnapshot{
  652. snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
  653. snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
  654. snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
  655. },
  656. want: []*DocumentSnapshot{
  657. snap(doc3, mv("foo", mapval(mv("bar", intval(2))))),
  658. snap(doc2, mv("foo", mapval(mv("bar", intval(2))))),
  659. snap(doc1, mv("foo", mapval(mv("bar", intval(1))))),
  660. },
  661. },
  662. } {
  663. got := append([]*DocumentSnapshot(nil), test.in...)
  664. sort.Sort(byQuery{test.q.compareFunc(), got})
  665. if diff := testDiff(got, test.want); diff != "" {
  666. t.Errorf("%+v: %s", test.q, diff)
  667. }
  668. }
  669. // Want error on missing field.
  670. q := coll.OrderBy("bar", Asc)
  671. if q.err != nil {
  672. t.Fatalf("bad query: %v", q.err)
  673. }
  674. cf := q.compareFunc()
  675. s := snap(doc1, mv("foo", intval(1)))
  676. if _, err := cf(s, s); err == nil {
  677. t.Error("got nil, want error")
  678. }
  679. }
  680. func TestQuerySubCollections(t *testing.T) {
  681. c := &Client{projectID: "P", databaseID: "DB"}
  682. /*
  683. parent-collection
  684. +---------+ +---------+
  685. | |
  686. | |
  687. parent-doc some-other-parent-doc
  688. |
  689. |
  690. sub-collection
  691. |
  692. |
  693. sub-doc
  694. |
  695. |
  696. sub-sub-collection
  697. |
  698. |
  699. sub-sub-doc
  700. */
  701. parentColl := c.Collection("parent-collection")
  702. parentDoc := parentColl.Doc("parent-doc")
  703. someOtherParentDoc := parentColl.Doc("some-other-parent-doc")
  704. subColl := parentDoc.Collection("sub-collection")
  705. subDoc := subColl.Doc("sub-doc")
  706. subSubColl := subDoc.Collection("sub-sub-collection")
  707. subSubDoc := subSubColl.Doc("sub-sub-doc")
  708. testCases := []struct {
  709. queryColl *CollectionRef
  710. queryFilterDoc *DocumentRef // startAt or endBefore
  711. wantColl string
  712. wantRef string
  713. wantErr bool
  714. }{
  715. // Queries are allowed at depth 0.
  716. {parentColl, parentDoc, "parent-collection", "projects/P/databases/DB/documents/parent-collection/parent-doc", false},
  717. // Queries are allowed at any depth.
  718. {subColl, subDoc, "sub-collection", "projects/P/databases/DB/documents/parent-collection/parent-doc/sub-collection/sub-doc", false},
  719. // Queries must be on immediate children (not allowed on grandchildren).
  720. {subColl, someOtherParentDoc, "", "", true},
  721. // Queries must be on immediate children (not allowed on siblings).
  722. {subColl, subSubDoc, "", "", true},
  723. }
  724. // startAt
  725. for _, testCase := range testCases {
  726. // Query a child within the document.
  727. q := testCase.queryColl.StartAt(&DocumentSnapshot{
  728. Ref: testCase.queryFilterDoc,
  729. proto: &pb.Document{
  730. Fields: map[string]*pb.Value{"a": intval(7)},
  731. },
  732. }).OrderBy("a", Asc)
  733. got, err := q.toProto()
  734. if testCase.wantErr {
  735. if err == nil {
  736. t.Fatal("expected err, got nil")
  737. }
  738. continue
  739. }
  740. if err != nil {
  741. t.Fatal(err)
  742. }
  743. want := &pb.StructuredQuery{
  744. From: []*pb.StructuredQuery_CollectionSelector{
  745. {CollectionId: testCase.wantColl},
  746. },
  747. OrderBy: []*pb.StructuredQuery_Order{
  748. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  749. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  750. },
  751. StartAt: &pb.Cursor{
  752. Values: []*pb.Value{
  753. intval(7),
  754. // This is the only part of the assertion we really care about.
  755. refval(testCase.wantRef),
  756. },
  757. Before: true,
  758. },
  759. }
  760. if !testEqual(got, want) {
  761. t.Fatalf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
  762. }
  763. }
  764. // endBefore
  765. for _, testCase := range testCases {
  766. // Query a child within the document.
  767. q := testCase.queryColl.EndBefore(&DocumentSnapshot{
  768. Ref: testCase.queryFilterDoc,
  769. proto: &pb.Document{
  770. Fields: map[string]*pb.Value{"a": intval(7)},
  771. },
  772. }).OrderBy("a", Asc)
  773. got, err := q.toProto()
  774. if testCase.wantErr {
  775. if err == nil {
  776. t.Fatal("expected err, got nil")
  777. }
  778. continue
  779. }
  780. if err != nil {
  781. t.Fatal(err)
  782. }
  783. want := &pb.StructuredQuery{
  784. From: []*pb.StructuredQuery_CollectionSelector{
  785. {CollectionId: testCase.wantColl},
  786. },
  787. OrderBy: []*pb.StructuredQuery_Order{
  788. {Field: fref1("a"), Direction: pb.StructuredQuery_ASCENDING},
  789. {Field: fref1("__name__"), Direction: pb.StructuredQuery_ASCENDING},
  790. },
  791. EndAt: &pb.Cursor{
  792. Values: []*pb.Value{
  793. intval(7),
  794. // This is the only part of the assertion we really care about.
  795. refval(testCase.wantRef),
  796. },
  797. Before: true,
  798. },
  799. }
  800. if !testEqual(got, want) {
  801. t.Fatalf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
  802. }
  803. }
  804. }
  805. // Stop should be callable on an uninitialized QuerySnapshotIterator.
  806. func TestStop_Uninitialized(t *testing.T) {
  807. i := &QuerySnapshotIterator{}
  808. i.Stop()
  809. }
  810. type byQuery struct {
  811. compare func(d1, d2 *DocumentSnapshot) (int, error)
  812. docs []*DocumentSnapshot
  813. }
  814. func (b byQuery) Len() int { return len(b.docs) }
  815. func (b byQuery) Swap(i, j int) { b.docs[i], b.docs[j] = b.docs[j], b.docs[i] }
  816. func (b byQuery) Less(i, j int) bool {
  817. c, err := b.compare(b.docs[i], b.docs[j])
  818. if err != nil {
  819. panic(err)
  820. }
  821. return c < 0
  822. }