go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/service/datastore/query.go (about) 1 // Copyright 2015 The LUCI Authors. 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 15 package datastore 16 17 import ( 18 "bytes" 19 "fmt" 20 "sort" 21 "strings" 22 "sync" 23 24 "go.chromium.org/luci/common/data/stringset" 25 "go.chromium.org/luci/common/errors" 26 ) 27 28 var ( 29 // ErrMultipleInequalityFilter is returned from Query.Finalize if you build a 30 // query which has inequality filters on multiple fields. 31 ErrMultipleInequalityFilter = errors.New( 32 "inequality filters on multiple properties in the same Query is not allowed") 33 34 // ErrNullQuery is returned from Query.Finalize if you build a query for which 35 // there cannot possibly be any results. 36 ErrNullQuery = errors.New( 37 "the query is overconstrained and can never have results") 38 ) 39 40 // Query is a builder-object for building a datastore query. It may represent 41 // an invalid query, but the error will only be observable when you call 42 // Finalize. 43 // 44 // Fields like "$id" are technically usable at the datastore level, but using 45 // them through the non-raw interface is likely a mistake. 46 // 47 // For example, instead of using: 48 // > datastore.NewQuery(...).Lte("$id", ...) 49 // One should use: 50 // > datastore.NewQuery(...).Lte("__key__", ...) 51 type Query struct { 52 queryFields 53 54 // These are set by Finalize as a way to cache the 1-1 correspondence of 55 // a Query to its FinalizedQuery form. err may also be set by intermediate 56 // Query functions if there's a problem before finalization. 57 // 58 // Query implements lazy finalization, meaning that it will happen at most 59 // once. This means that the finalization state and cached finalization must 60 // be locked around. 61 finalizeOnce sync.Once 62 finalized *FinalizedQuery 63 finalizeErr error 64 } 65 66 // queryFields are the Query's read-only fields. 67 // 68 // All Property and PropertySlice inside must have comparable types. 69 type queryFields struct { 70 kind string 71 72 // Indicate if the query is executed to a firestore (with datastore API) 73 firestoreMode bool 74 eventualConsistency bool 75 keysOnly bool 76 distinct bool 77 78 limit *int32 79 offset *int32 80 81 order []IndexColumn 82 project stringset.Set 83 84 eqFilts map[string]PropertySlice 85 inFilts map[string][]PropertySlice 86 87 ineqFiltProp string 88 ineqFiltLow Property 89 ineqFiltLowIncl bool 90 ineqFiltLowSet bool 91 ineqFiltHigh Property 92 ineqFiltHighIncl bool 93 ineqFiltHighSet bool 94 95 start Cursor 96 end Cursor 97 98 err error 99 } 100 101 // NewQuery returns a new Query for the given kind. If kind may be empty to 102 // begin a kindless query. 103 func NewQuery(kind string) *Query { 104 return &Query{ 105 queryFields: queryFields{ 106 kind: kind, 107 }, 108 } 109 } 110 111 func (q *Query) mod(cb func(*Query)) *Query { 112 if q.err != nil { 113 return q 114 } 115 116 ret := Query{ 117 queryFields: q.queryFields, 118 } 119 if len(q.order) > 0 { 120 ret.order = make([]IndexColumn, len(q.order)) 121 copy(ret.order, q.order) 122 } 123 if q.project != nil { 124 ret.project = q.project.Dup() 125 } 126 if len(q.eqFilts) > 0 { 127 ret.eqFilts = make(map[string]PropertySlice, len(q.eqFilts)) 128 for k, v := range q.eqFilts { 129 newV := make(PropertySlice, len(v)) 130 copy(newV, v) 131 ret.eqFilts[k] = newV 132 } 133 } 134 if len(q.inFilts) > 0 { 135 ret.inFilts = make(map[string][]PropertySlice, len(q.inFilts)) 136 for k, v := range q.inFilts { 137 // Note that we never mutate individual `v` elements (which are property 138 // slices), so there's no need to deep-clone them. We only need to clone 139 // the top container slice (i.e. `v` itself), since we do mutate it in 140 // In(...) by appending more elements to it. 141 newV := make([]PropertySlice, len(v)) 142 copy(newV, v) 143 ret.inFilts[k] = newV 144 } 145 } 146 cb(&ret) 147 return &ret 148 } 149 150 // Kind alters the kind of this query. 151 func (q *Query) Kind(kind string) *Query { 152 return q.mod(func(q *Query) { 153 q.kind = kind 154 }) 155 } 156 157 // Ancestor sets the ancestor filter for this query. 158 // 159 // If ancestor is nil, then this removes the Ancestor restriction from the 160 // query. 161 func (q *Query) Ancestor(ancestor *Key) *Query { 162 return q.mod(func(q *Query) { 163 if q.eqFilts == nil { 164 q.eqFilts = map[string]PropertySlice{} 165 } 166 if ancestor == nil { 167 delete(q.eqFilts, "__ancestor__") 168 if len(q.eqFilts) == 0 { 169 q.eqFilts = nil 170 } 171 } else { 172 q.eqFilts["__ancestor__"] = PropertySlice{MkProperty(ancestor)} 173 } 174 }) 175 } 176 177 // EventualConsistency changes the EventualConsistency setting for this query. 178 func (q *Query) EventualConsistency(on bool) *Query { 179 return q.mod(func(q *Query) { 180 q.eventualConsistency = on 181 }) 182 } 183 184 // Limit sets the limit (max items to return) for this query. If limit < 0, this 185 // removes the limit from the query entirely. 186 func (q *Query) Limit(limit int32) *Query { 187 return q.mod(func(q *Query) { 188 if limit < 0 { 189 q.limit = nil 190 } else { 191 q.limit = &limit 192 } 193 }) 194 } 195 196 // Offset sets the offset (number of items to skip) for this query. If 197 // offset < 0, this removes the offset from the query entirely. 198 func (q *Query) Offset(offset int32) *Query { 199 return q.mod(func(q *Query) { 200 if offset < 0 { 201 q.offset = nil 202 } else { 203 q.offset = &offset 204 } 205 }) 206 } 207 208 // KeysOnly makes this into a query which only returns keys (but doesn't fetch 209 // values). It's incompatible with projection queries. 210 func (q *Query) KeysOnly(on bool) *Query { 211 return q.mod(func(q *Query) { 212 q.keysOnly = on 213 }) 214 } 215 216 // Order sets one or more orders for this query. 217 func (q *Query) Order(fieldNames ...string) *Query { 218 if len(fieldNames) == 0 { 219 return q 220 } 221 return q.mod(func(q *Query) { 222 for _, fn := range fieldNames { 223 ic, err := ParseIndexColumn(fn) 224 if err != nil { 225 q.err = err 226 return 227 } 228 if q.reserved(ic.Property) { 229 return 230 } 231 q.order = append(q.order, ic) 232 } 233 }) 234 } 235 236 // ClearOrder removes all orders from this Query. 237 func (q *Query) ClearOrder() *Query { 238 return q.mod(func(q *Query) { 239 q.order = nil 240 }) 241 } 242 243 // Project lists one or more field names to project. 244 func (q *Query) Project(fieldNames ...string) *Query { 245 if len(fieldNames) == 0 { 246 return q 247 } 248 return q.mod(func(q *Query) { 249 for _, f := range fieldNames { 250 if q.reserved(f) { 251 return 252 } 253 if f == "__key__" { 254 q.err = fmt.Errorf("cannot project on %q", f) 255 return 256 } 257 if q.project == nil { 258 q.project = stringset.New(1) 259 } 260 q.project.Add(f) 261 } 262 }) 263 } 264 265 // Distinct makes a projection query only return distinct values. This has 266 // no effect on non-projection queries. 267 func (q *Query) Distinct(on bool) *Query { 268 return q.mod(func(q *Query) { 269 q.distinct = on 270 }) 271 } 272 273 // ClearProject removes all projected fields from this Query. 274 func (q *Query) ClearProject() *Query { 275 return q.mod(func(q *Query) { 276 q.project = nil 277 }) 278 } 279 280 // Start sets a starting cursor. The cursor is implementation-defined by the 281 // particular 'impl' you have installed. 282 func (q *Query) Start(c Cursor) *Query { 283 return q.mod(func(q *Query) { 284 q.start = c 285 }) 286 } 287 288 // End sets the ending cursor. The cursor is implementation-defined by the 289 // particular 'impl' you have installed. 290 func (q *Query) End(c Cursor) *Query { 291 return q.mod(func(q *Query) { 292 q.end = c 293 }) 294 } 295 296 // Eq adds one or more equality restrictions to the query. 297 // 298 // Equality filters interact with multiply-defined properties by ensuring that 299 // the given field has /at least one/ value which is equal to the specified 300 // constraint. 301 // 302 // So a query with `.Eq("thing", 1, 2)` will only return entities where the 303 // field "thing" is multiply defined and contains both a value of 1 and a value 304 // of 2. If the field is singular, such check will never pass. To query for 305 // entities with a field matching any one of values use `.In("thing", 1, 2)` 306 // filter instead. 307 // 308 // `Eq("thing", 1).Eq("thing", 2)` and `.Eq("thing", 1, 2)` have identical 309 // meaning. 310 func (q *Query) Eq(field string, values ...any) *Query { 311 if len(values) == 0 { 312 return q 313 } 314 return q.mod(func(q *Query) { 315 if !q.reserved(field) { 316 if q.eqFilts == nil { 317 q.eqFilts = make(map[string]PropertySlice, 1) 318 } 319 s := q.eqFilts[field] 320 for _, value := range values { 321 p := Property{} 322 if q.err = p.SetValue(value, ShouldIndex); q.err != nil { 323 return 324 } 325 if q.err = checkComparable(field, p.Type()); q.err != nil { 326 return 327 } 328 idx := sort.Search(len(s), func(i int) bool { 329 // s[i] >= p is the same as: 330 return s[i].Equal(&p) || p.Less(&s[i]) 331 }) 332 if idx == len(s) || !s[idx].Equal(&p) { 333 s = append(s, Property{}) 334 copy(s[idx+1:], s[idx:]) 335 s[idx] = p 336 } 337 } 338 q.eqFilts[field] = s 339 } 340 }) 341 } 342 343 // reserved checks whether a field is reserved. 344 // 345 // Set the q.err as a side-effect if field is invalid. 346 func (q *Query) reserved(field string) bool { 347 if field == "__key__" || field == "__scatter__" { 348 return false 349 } 350 if strings.HasPrefix(field, "$") { 351 q.err = fmt.Errorf( 352 `LUCI fields such as "$id" and "$kind" are not real fields: rejecting field %q`, field) 353 return true 354 } 355 if field == "" { 356 q.err = fmt.Errorf( 357 "cannot filter/project on: %q", field) 358 return true 359 } 360 if strings.HasPrefix(field, "__") && strings.HasSuffix(field, "__") { 361 q.err = fmt.Errorf( 362 "cannot filter/project on reserved property: %q", field) 363 return true 364 } 365 return false 366 } 367 368 func (q *Query) ineqOK(field string, value Property) bool { 369 if q.reserved(field) { 370 return false 371 } 372 if field == "__key__" && value.Type() != PTKey { 373 q.err = fmt.Errorf( 374 "filters on %q must have type *Key (got %s)", field, value.Type()) 375 return false 376 } 377 if q.ineqFiltProp != "" && q.ineqFiltProp != field { 378 q.err = ErrMultipleInequalityFilter 379 return false 380 } 381 return true 382 } 383 384 // Lt imposes a 'less-than' inequality restriction on the Query. 385 // 386 // Inequality filters interact with multiply-defined properties by ensuring that 387 // the given field has /exactly one/ value which matches /all/ of the inequality 388 // constraints. 389 // 390 // So a query with `.Gt("thing", 5).Lt("thing", 10)` will only return entities 391 // where the field "thing" has a single value where `5 < val < 10`. 392 func (q *Query) Lt(field string, value any) *Query { 393 p := Property{} 394 err := p.SetValue(value, ShouldIndex) 395 if err == nil { 396 err = checkComparable(field, p.Type()) 397 } 398 399 if err == nil && q.ineqFiltHighSet { 400 if q.ineqFiltHigh.Less(&p) { 401 return q 402 } else if q.ineqFiltHigh.Equal(&p) && !q.ineqFiltHighIncl { 403 return q 404 } 405 } 406 407 return q.mod(func(q *Query) { 408 if q.err = err; err != nil { 409 return 410 } 411 if q.ineqOK(field, p) { 412 q.ineqFiltProp = field 413 q.ineqFiltHighSet = true 414 q.ineqFiltHigh = p 415 q.ineqFiltHighIncl = false 416 } 417 }) 418 } 419 420 // Lte imposes a 'less-than-or-equal' inequality restriction on the Query. 421 // 422 // Inequality filters interact with multiply-defined properties by ensuring that 423 // the given field has /exactly one/ value which matches /all/ of the inequality 424 // constraints. 425 // 426 // So a query with `.Gt("thing", 5).Lt("thing", 10)` will only return entities 427 // where the field "thing" has a single value where `5 < val < 10`. 428 func (q *Query) Lte(field string, value any) *Query { 429 p := Property{} 430 err := p.SetValue(value, ShouldIndex) 431 if err == nil { 432 err = checkComparable(field, p.Type()) 433 } 434 435 if err == nil && q.ineqFiltHighSet { 436 if q.ineqFiltHigh.Less(&p) { 437 return q 438 } else if q.ineqFiltHigh.Equal(&p) { 439 return q 440 } 441 } 442 443 return q.mod(func(q *Query) { 444 if q.err = err; err != nil { 445 return 446 } 447 if q.ineqOK(field, p) { 448 q.ineqFiltProp = field 449 q.ineqFiltHighSet = true 450 q.ineqFiltHigh = p 451 q.ineqFiltHighIncl = true 452 } 453 }) 454 } 455 456 // Gt imposes a 'greater-than' inequality restriction on the Query. 457 // 458 // Inequality filters interact with multiply-defined properties by ensuring that 459 // the given field has /exactly one/ value which matches /all/ of the inequality 460 // constraints. 461 // 462 // So a query with `.Gt("thing", 5).Lt("thing", 10)` will only return entities 463 // where the field "thing" has a single value where `5 < val < 10`. 464 func (q *Query) Gt(field string, value any) *Query { 465 p := Property{} 466 err := p.SetValue(value, ShouldIndex) 467 if err == nil { 468 err = checkComparable(field, p.Type()) 469 } 470 471 if err == nil && q.ineqFiltLowSet { 472 if p.Less(&q.ineqFiltLow) { 473 return q 474 } else if p.Equal(&q.ineqFiltLow) && !q.ineqFiltLowIncl { 475 return q 476 } 477 } 478 479 return q.mod(func(q *Query) { 480 if q.err = err; err != nil { 481 return 482 } 483 if q.ineqOK(field, p) { 484 q.ineqFiltProp = field 485 q.ineqFiltLowSet = true 486 q.ineqFiltLow = p 487 q.ineqFiltLowIncl = false 488 } 489 }) 490 } 491 492 // Gte imposes a 'greater-than-or-equal' inequality restriction on the Query. 493 // 494 // Inequality filters interact with multiply-defined properties by ensuring that 495 // the given field has /exactly one/ value which matches /all/ of the inequality 496 // constraints. 497 // 498 // So a query with `.Gt("thing", 5).Lt("thing", 10)` will only return entities 499 // where the field "thing" has a single value where `5 < val < 10`. 500 func (q *Query) Gte(field string, value any) *Query { 501 p := Property{} 502 err := p.SetValue(value, ShouldIndex) 503 if err == nil { 504 err = checkComparable(field, p.Type()) 505 } 506 507 if err == nil && q.ineqFiltLowSet { 508 if p.Less(&q.ineqFiltLow) { 509 return q 510 } else if p.Equal(&q.ineqFiltLow) { 511 return q 512 } 513 } 514 515 return q.mod(func(q *Query) { 516 if q.err = err; err != nil { 517 return 518 } 519 if q.ineqOK(field, p) { 520 q.ineqFiltProp = field 521 q.ineqFiltLowSet = true 522 q.ineqFiltLow = p 523 q.ineqFiltLowIncl = true 524 } 525 }) 526 } 527 528 // In imposes a 'is-in-a-set' equality restriction on the Query. 529 // 530 // Equality filters interact with multiply-defined properties by ensuring that 531 // the given field has /at least one/ value which is equal to the specified 532 // constraint. So a query with `.In("thing", 1, 2)` will return entities 533 // where at least one value of the field "thing" is either 1 or 2. 534 // 535 // Multiple `In` filters on the same property are AND-ed together, e.g. 536 // `.In("thing", 1, 2).In("thing", 3, 4)` will return entities whose repeated 537 // "thing" field has a value equal to 1 or 2 AND another value equal to 3 or 4. 538 func (q *Query) In(field string, values ...any) *Query { 539 return q.mod(func(q *Query) { 540 if q.reserved(field) { 541 return 542 } 543 props := make(PropertySlice, len(values)) 544 for idx, value := range values { 545 if q.err = props[idx].SetValue(value, ShouldIndex); q.err != nil { 546 return 547 } 548 if q.err = checkComparable(field, props[idx].Type()); q.err != nil { 549 return 550 } 551 } 552 if q.inFilts == nil { 553 q.inFilts = make(map[string][]PropertySlice, 1) 554 } 555 q.inFilts[field] = append(q.inFilts[field], props) 556 }) 557 } 558 559 // ClearFilters clears all equality and inequality filters from the Query. It 560 // does not clear the Ancestor filter if one is defined. 561 func (q *Query) ClearFilters() *Query { 562 return q.mod(func(q *Query) { 563 anc := q.eqFilts["__ancestor__"] 564 if anc != nil { 565 q.eqFilts = map[string]PropertySlice{"__ancestor__": anc} 566 } else { 567 q.eqFilts = nil 568 } 569 q.inFilts = nil 570 q.ineqFiltLowSet = false 571 q.ineqFiltHighSet = false 572 }) 573 } 574 575 // Finalize converts this Query to a FinalizedQuery. If the Query has any 576 // inconsistencies or violates any of the query rules, that will be returned 577 // here. 578 func (q *Query) Finalize() (*FinalizedQuery, error) { 579 if q.err != nil { 580 return nil, q.err 581 } 582 583 q.finalizeOnce.Do(func() { 584 q.finalized, q.finalizeErr = q.finalizeImpl() 585 }) 586 return q.finalized, q.finalizeErr 587 } 588 589 func (q *Query) finalizeImpl() (*FinalizedQuery, error) { 590 ancestor := (*Key)(nil) 591 if slice, ok := q.eqFilts["__ancestor__"]; ok { 592 ancestor = slice[0].Value().(*Key) 593 } 594 595 if q.kind == "" { // kindless query checks 596 if q.ineqFiltProp != "" && q.ineqFiltProp != "__key__" { 597 return nil, fmt.Errorf( 598 "kindless queries can only filter on __key__, got %q", q.ineqFiltProp) 599 } 600 allowedEqs := 0 601 if ancestor != nil { 602 allowedEqs = 1 603 } 604 if len(q.eqFilts) > allowedEqs || len(q.inFilts) > 0 { 605 return nil, fmt.Errorf("kindless queries may not have any equality filters") 606 } 607 for _, o := range q.order { 608 if o.Property != "__key__" || o.Descending { 609 return nil, fmt.Errorf("invalid order for kindless query: %#v", o) 610 } 611 } 612 } 613 614 if q.keysOnly && q.project != nil && q.project.Len() > 0 { 615 return nil, errors.New("cannot project a keysOnly query") 616 } 617 618 if q.ineqFiltProp != "" { 619 if len(q.order) > 0 && q.order[0].Property != q.ineqFiltProp { 620 return nil, fmt.Errorf( 621 "first sort order must match inequality filter: %q v %q", 622 q.order[0].Property, q.ineqFiltProp) 623 } 624 if q.ineqFiltLowSet && q.ineqFiltHighSet { 625 if q.ineqFiltHigh.Less(&q.ineqFiltLow) || 626 (q.ineqFiltHigh.Equal(&q.ineqFiltLow) && 627 (!q.ineqFiltLowIncl || !q.ineqFiltHighIncl)) { 628 return nil, ErrNullQuery 629 } 630 } 631 if q.ineqFiltProp == "__key__" { 632 if q.ineqFiltLowSet { 633 if ancestor != nil && !q.ineqFiltLow.Value().(*Key).HasAncestor(ancestor) { 634 return nil, fmt.Errorf( 635 "inequality filters on __key__ must be descendants of the __ancestor__") 636 } 637 } 638 if q.ineqFiltHighSet { 639 if ancestor != nil && !q.ineqFiltHigh.Value().(*Key).HasAncestor(ancestor) { 640 return nil, fmt.Errorf( 641 "inequality filters on __key__ must be descendants of the __ancestor__") 642 } 643 } 644 } 645 } 646 647 if q.project != nil { 648 var err error 649 q.project.Iter(func(p string) bool { 650 _, iseq := q.eqFilts[p] 651 _, isin := q.inFilts[p] 652 if iseq || isin { 653 err = fmt.Errorf("cannot project on equality filter field: %s", p) 654 return false 655 } 656 return true 657 }) 658 if err != nil { 659 return nil, err 660 } 661 } 662 663 for _, slices := range q.inFilts { 664 for _, set := range slices { 665 if len(set) == 0 { 666 return nil, ErrNullQuery 667 } 668 } 669 } 670 671 ret := &FinalizedQuery{ 672 original: q, 673 kind: q.kind, 674 675 keysOnly: q.keysOnly, 676 eventuallyConsistent: q.getEventualConsistency(ancestor), 677 limit: q.limit, 678 offset: q.offset, 679 start: q.start, 680 end: q.end, 681 682 eqFilts: q.eqFilts, 683 inFilts: q.inFilts, 684 685 ineqFiltProp: q.ineqFiltProp, 686 ineqFiltLow: q.ineqFiltLow, 687 ineqFiltLowIncl: q.ineqFiltLowIncl, 688 ineqFiltLowSet: q.ineqFiltLowSet, 689 ineqFiltHigh: q.ineqFiltHigh, 690 ineqFiltHighIncl: q.ineqFiltHighIncl, 691 ineqFiltHighSet: q.ineqFiltHighSet, 692 } 693 // If a starting cursor is provided, ignore the offset, as it would have been 694 // accounted for in the query that produced the cursor. 695 if ret.start != nil { 696 ret.offset = nil 697 } 698 699 if q.project != nil { 700 ret.project = q.project.ToSlice() 701 ret.distinct = q.distinct && q.project.Len() > 0 702 703 // If we're DISTINCT && have an inequality filter, we must project that 704 // inequality property as well. 705 if ret.distinct && ret.ineqFiltProp != "" && !q.project.Has(ret.ineqFiltProp) { 706 ret.project = append([]string{ret.ineqFiltProp}, ret.project...) 707 } 708 } 709 710 seenOrders := stringset.New(len(q.order)) 711 712 // if len(q.order) > 0, we already enforce that the first order 713 // is the same as the inequality above. Otherwise we need to add it. 714 if len(q.order) == 0 && q.ineqFiltProp != "" { 715 ret.orders = []IndexColumn{{Property: q.ineqFiltProp}} 716 seenOrders.Add(q.ineqFiltProp) 717 } 718 719 // drop orders where there's an equality filter 720 // https://cloud.google.com/appengine/docs/go/datastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters 721 // Deduplicate orders 722 for _, o := range q.order { 723 if _, iseq := q.eqFilts[o.Property]; !iseq { 724 if seenOrders.Add(o.Property) { 725 ret.orders = append(ret.orders, o) 726 } 727 } 728 } 729 730 // Add any projection columns not mentioned in the user-defined order as 731 // ASCENDING orders. Technically we could be smart and automatically use 732 // a DESCENDING ordered index, if it fit, but the logic gets insane, since all 733 // suffixes of all used indexes need to be PRECISELY equal (and so you'd have 734 // to hunt/invalidate/something to find the combination of indexes that are 735 // compatible with each other as well as the query). If you want to use 736 // a DESCENDING column, just add it to the user sort order, and this loop will 737 // not synthesize a new suffix entry for it. 738 // 739 // NOTE: if you want to use an index that sorts by -__key__, you MUST 740 // include all of the projected fields for that index in the order explicitly. 741 // Otherwise the generated orders will be wacky. So: 742 // Query("Foo").Project("A", "B").Order("A").Order("-__key__") 743 // 744 // will turn into a orders of: 745 // A, ASCENDING 746 // __key__, DESCENDING 747 // B, ASCENDING 748 // __key__, ASCENDING 749 // 750 // To prevent this, your query should have another Order("B") clause before 751 // the -__key__ clause. 752 if len(ret.project) > 0 { 753 sort.Strings(ret.project) 754 for _, p := range ret.project { 755 if !seenOrders.Has(p) { 756 ret.orders = append(ret.orders, IndexColumn{Property: p}) 757 } 758 } 759 } 760 761 // If the suffix format ends with __key__ already (e.g. .Order("__key__")), 762 // then we're good to go. Otherwise we need to add it as the last bit of the 763 // suffix, since all indexes implicitly have it as the last column. 764 if len(ret.orders) == 0 || ret.orders[len(ret.orders)-1].Property != "__key__" { 765 ret.orders = append(ret.orders, IndexColumn{Property: "__key__"}) 766 } 767 768 return ret, nil 769 } 770 771 func (q *Query) String() string { 772 ret := &bytes.Buffer{} 773 needComma := false 774 p := func(fmtStr string, stuff ...any) { 775 if needComma { 776 if _, err := ret.WriteString(", "); err != nil { 777 panic(err) 778 } 779 } 780 needComma = true 781 fmt.Fprintf(ret, fmtStr, stuff...) 782 } 783 if _, err := ret.WriteString("Query("); err != nil { 784 panic(err) 785 } 786 if q.err != nil { 787 p("ERROR=%q", q.err.Error()) 788 } 789 790 // Filters 791 if q.kind != "" { 792 p("Kind=%q", q.kind) 793 } 794 if q.eqFilts["__ancestor__"] != nil { 795 p("Ancestor=%s", q.eqFilts["__ancestor__"][0].Value().(*Key).String()) 796 } 797 for prop, vals := range q.eqFilts { 798 if prop == "__ancestor__" { 799 continue 800 } 801 for _, v := range vals { 802 p("Filter(%q == %s)", prop, v.GQL()) 803 } 804 } 805 for prop, slices := range q.inFilts { 806 for _, vals := range slices { 807 gql := make([]string, len(vals)) 808 for i, v := range vals { 809 gql[i] = v.GQL() 810 } 811 p("Filter(%q in [%s])", prop, strings.Join(gql, ", ")) 812 } 813 } 814 if q.ineqFiltProp != "" { 815 if q.ineqFiltLowSet { 816 op := ">" 817 if q.ineqFiltLowIncl { 818 op = ">=" 819 } 820 p("Filter(%q %s %s)", q.ineqFiltProp, op, q.ineqFiltLow.GQL()) 821 } 822 if q.ineqFiltHighSet { 823 op := "<" 824 if q.ineqFiltHighIncl { 825 op = "<=" 826 } 827 p("Filter(%q %s %s)", q.ineqFiltProp, op, q.ineqFiltHigh.GQL()) 828 } 829 } 830 831 // Order 832 if len(q.order) > 0 { 833 orders := make([]string, len(q.order)) 834 for i, o := range q.order { 835 orders[i] = o.String() 836 } 837 p("Order(%s)", strings.Join(orders, ", ")) 838 } 839 840 // Projection 841 if q.project != nil && q.project.Len() > 0 { 842 f := "Project(%s)" 843 if q.distinct { 844 f = "Project[DISTINCT](%s)" 845 } 846 p(f, strings.Join(q.project.ToSlice(), ", ")) 847 } 848 849 // Cursors 850 if q.start != nil { 851 p("Start(%q)", q.start.String()) 852 } 853 if q.end != nil { 854 p("End(%q)", q.end.String()) 855 } 856 857 // Modifiers 858 if q.limit != nil { 859 p("Limit=%d", *q.limit) 860 } 861 if q.offset != nil { 862 p("Offset=%d", *q.offset) 863 } 864 if q.eventualConsistency { 865 p("EventualConsistency") 866 } 867 if q.keysOnly { 868 p("KeysOnly") 869 } 870 871 if _, err := ret.WriteRune(')'); err != nil { 872 panic(err) 873 } 874 875 return ret.String() 876 } 877 878 // FirestoreMode set the firestore mode. It removes internal checks for 879 // this Query which don't apply when using Firestore-in-Datastore mode. 880 // 881 // In firestore mode all Datastore queries become strongly consistent by 882 // default, but still can be made eventually consistent via a call to 883 // EventualConsistency(true). In particular this is useful for aggregation 884 // queries like Count(). 885 // 886 // Note that firestore mode allows non-ancestor queries within a transaction. 887 func (q *Query) FirestoreMode(on bool) *Query { 888 return q.mod(func(q *Query) { 889 q.firestoreMode = on 890 }) 891 } 892 893 // GetFirestoreMode returns the firestore mode. 894 func (q *Query) GetFirestoreMode() bool { 895 return q.firestoreMode 896 } 897 898 func (q *Query) getEventualConsistency(ancestor *Key) bool { 899 return q.eventualConsistency || (!q.firestoreMode && ancestor == nil) 900 } 901 902 // checkComparable returns an error if this property type is not comparable. 903 func checkComparable(field string, pt PropertyType) error { 904 if !pt.Comparable() { 905 return fmt.Errorf("a non-comparable value in a filter on field %q", field) 906 } 907 return nil 908 } 909 910 // All cmp* functions below return -1 if a < b, 0 if a == b, 1 if a > b. 911 912 func cmpInteger(a, b int) int { 913 switch { 914 case a == b: 915 return 0 916 case a < b: 917 return -1 918 default: 919 return 1 920 } 921 } 922 923 func cmpStringSet(a, b stringset.Set) int { 924 // Quick check for the most common case to skip heavy calls below. 925 if a.Len() == 0 && b.Len() == 0 { 926 return 0 927 } 928 929 // Compare as math sets: discard common elements and then compare remainders 930 // lexicographically. 931 common := a.Intersect(b) 932 auniq := a.Difference(common).ToSortedSlice() 933 buniq := b.Difference(common).ToSortedSlice() 934 935 for i := 0; i < len(auniq) && i < len(buniq); i++ { 936 switch { 937 case auniq[i] < buniq[i]: 938 return -1 939 case auniq[i] > buniq[i]: 940 return 1 941 } 942 } 943 944 return cmpInteger(len(auniq), len(buniq)) 945 } 946 947 func cmpStr(a, b string) int { 948 switch { 949 case a == b: 950 return 0 951 case a < b: 952 return -1 953 default: 954 return 1 955 } 956 } 957 958 func cmpBoolean(a, b bool) int { 959 switch { 960 case a == b: 961 return 0 962 case !a: 963 return -1 964 default: 965 return 1 966 } 967 } 968 969 func cmpEqFilters(a, b map[string]PropertySlice) int { 970 // Quick checks for common cases. 971 switch { 972 case len(a) == 0 && len(b) == 0: 973 return 0 974 case len(a) == 0 && len(b) != 0: 975 return -1 976 case len(a) != 0 && len(b) == 0: 977 return 1 978 } 979 980 // Compare maps in the sorted order of keys. 981 cap := len(a) 982 if len(b) > cap { 983 cap = len(b) 984 } 985 keys := stringset.New(cap) 986 for k := range a { 987 keys.Add(k) 988 } 989 for k := range b { 990 keys.Add(k) 991 } 992 for _, key := range keys.ToSortedSlice() { 993 if cmp := cmpPropertySliceSet(a[key], b[key]); cmp != 0 { 994 return cmp 995 } 996 } 997 return cmpInteger(len(a), len(b)) 998 } 999 1000 func cmpInFilters(a, b map[string][]PropertySlice) int { 1001 // Quick checks for common cases. 1002 switch { 1003 case len(a) == 0 && len(b) == 0: 1004 return 0 1005 case len(a) == 0 && len(b) != 0: 1006 return -1 1007 case len(a) != 0 && len(b) == 0: 1008 return 1 1009 } 1010 1011 // Compare maps in the sorted order of keys. Compare values lexicographically. 1012 cap := len(a) 1013 if len(b) > cap { 1014 cap = len(b) 1015 } 1016 keys := stringset.New(cap) 1017 for k := range a { 1018 keys.Add(k) 1019 } 1020 for k := range b { 1021 keys.Add(k) 1022 } 1023 for _, key := range keys.ToSortedSlice() { 1024 alist := a[key] 1025 blist := b[key] 1026 for i := 0; i < len(alist) && i < len(blist); i++ { 1027 if cmp := cmpPropertySliceSet(alist[i], blist[i]); cmp != 0 { 1028 return cmp 1029 } 1030 } 1031 if cmp := cmpInteger(len(alist), len(blist)); cmp != 0 { 1032 return cmp 1033 } 1034 } 1035 return cmpInteger(len(a), len(b)) 1036 } 1037 1038 func cmpPropertySliceSet(a, b PropertySlice) int { 1039 // Quick checks for common cases. 1040 switch { 1041 case len(a) == 0 && len(b) == 0: 1042 return 0 1043 case len(a) == 0 && len(b) != 0: 1044 return -1 1045 case len(a) != 0 && len(b) == 0: 1046 return 1 1047 } 1048 1049 asorted := a.Slice() 1050 sort.Sort(asorted) 1051 1052 bsorted := b.Slice() 1053 sort.Sort(bsorted) 1054 1055 for i := 0; i < len(asorted) && i < len(bsorted); i++ { 1056 if cmp := asorted[i].Compare(&bsorted[i]); cmp != 0 { 1057 return cmp 1058 } 1059 } 1060 1061 return cmpInteger(len(asorted), len(bsorted)) 1062 } 1063 1064 func cmpIneqOp(a *Property, aincl, aset bool, b *Property, bincl, bset bool) int { 1065 switch { 1066 case !aset && !bset: 1067 // If both are unset, don't bother comparing properties. They are null. 1068 return 0 1069 case !aset && bset: 1070 // If only 'b' set, then a < b. 1071 return -1 1072 case aset && !bset: 1073 // If only 'a' set, then a > b. 1074 return 1 1075 default: 1076 // If both are set, compare the rest of the fields. 1077 if cmp := a.Compare(b); cmp != 0 { 1078 return cmp 1079 } 1080 return cmpBoolean(aincl, bincl) 1081 } 1082 } 1083 1084 func cmpIndexColumnList(a, b []IndexColumn) int { 1085 for i := 0; i < len(a) && i < len(b); i++ { 1086 if cmp := cmpStr(a[i].Property, b[i].Property); cmp != 0 { 1087 return cmp 1088 } 1089 if cmp := cmpBoolean(a[i].Descending, b[i].Descending); cmp != 0 { 1090 return cmp 1091 } 1092 } 1093 return cmpInteger(len(a), len(b)) 1094 } 1095 1096 func cmpOptionalInt32(a, b *int32) int { 1097 switch { 1098 case a == nil && b == nil: 1099 return 0 1100 case a == nil && b != nil: 1101 return -1 1102 case a != nil && b == nil: 1103 return 1 1104 default: 1105 switch { 1106 case *a == *b: 1107 return 0 1108 case *a < *b: 1109 return -1 1110 default: 1111 return 1 1112 } 1113 } 1114 } 1115 1116 func cmpCursor(a, b Cursor) int { 1117 var astr string 1118 if a != nil { 1119 astr = a.String() 1120 } 1121 var bstr string 1122 if b != nil { 1123 bstr = b.String() 1124 } 1125 return cmpStr(astr, bstr) 1126 } 1127 1128 // Less returns true if a < b. It is used for local sorting of lists of queries, 1129 // there is nothing datastore specific about this. 1130 func (a *Query) Less(b *Query) bool { 1131 // For concreteness compare query components in the same order they would 1132 // appear in a GQL query string. Things that do not show up in GQL are 1133 // compared last. Note this should cover every field in queryFields struct. 1134 // 1135 // See https://cloud.google.com/datastore/docs/reference/gql_reference 1136 if cmp := cmpStringSet(a.project, b.project); cmp != 0 { 1137 return cmp < 0 1138 } 1139 if cmp := cmpStr(a.kind, b.kind); cmp != 0 { 1140 return cmp < 0 1141 } 1142 if cmp := cmpBoolean(a.distinct, b.distinct); cmp != 0 { 1143 return cmp < 0 1144 } 1145 if cmp := cmpEqFilters(a.eqFilts, b.eqFilts); cmp != 0 { 1146 return cmp < 0 1147 } 1148 if cmp := cmpInFilters(a.inFilts, b.inFilts); cmp != 0 { 1149 return cmp < 0 1150 } 1151 if cmp := cmpStr(a.ineqFiltProp, b.ineqFiltProp); cmp != 0 { 1152 return cmp < 0 1153 } 1154 if cmp := cmpIneqOp( 1155 &a.ineqFiltLow, a.ineqFiltLowIncl, a.ineqFiltLowSet, 1156 &b.ineqFiltLow, b.ineqFiltLowIncl, b.ineqFiltLowSet, 1157 ); cmp != 0 { 1158 return cmp < 0 1159 } 1160 if cmp := cmpIneqOp( 1161 &a.ineqFiltHigh, a.ineqFiltHighIncl, a.ineqFiltHighSet, 1162 &b.ineqFiltHigh, b.ineqFiltHighIncl, b.ineqFiltHighSet, 1163 ); cmp != 0 { 1164 return cmp < 0 1165 } 1166 if cmp := cmpIndexColumnList(a.order, b.order); cmp != 0 { 1167 return cmp < 0 1168 } 1169 if cmp := cmpOptionalInt32(a.limit, b.limit); cmp != 0 { 1170 return cmp < 0 1171 } 1172 if cmp := cmpOptionalInt32(a.offset, b.offset); cmp != 0 { 1173 return cmp < 0 1174 } 1175 if cmp := cmpCursor(a.start, b.start); cmp != 0 { 1176 return cmp < 0 1177 } 1178 if cmp := cmpCursor(a.end, b.end); cmp != 0 { 1179 return cmp < 0 1180 } 1181 if cmp := cmpBoolean(a.keysOnly, b.keysOnly); cmp != 0 { 1182 return cmp < 0 1183 } 1184 if cmp := cmpBoolean(a.firestoreMode, b.firestoreMode); cmp != 0 { 1185 return cmp < 0 1186 } 1187 if cmp := cmpBoolean(a.eventualConsistency, b.eventualConsistency); cmp != 0 { 1188 return cmp < 0 1189 } 1190 // They are equal, which means `a < b` is false. 1191 return false 1192 }