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  }