func numComponents(fq *ds.FinalizedQuery) int { numComponents := len(fq.Orders()) if p, _, _ := fq.IneqFilterLow(); p != "" { numComponents++ } if p, _, _ := fq.IneqFilterHigh(); p != "" { numComponents++ } for _, v := range fq.EqFilters() { numComponents += v.Len() } return numComponents }
func (d rdsImpl) fixQuery(fq *ds.FinalizedQuery) (*datastore.Query, error) { ret := datastore.NewQuery(fq.Kind()) start, end := fq.Bounds() if start != nil { ret = ret.Start(start.(datastore.Cursor)) } if end != nil { ret = ret.End(end.(datastore.Cursor)) } for prop, vals := range fq.EqFilters() { if prop == "__ancestor__" { p, err := dsF2RProp(d.aeCtx, vals[0]) if err != nil { return nil, err } ret = ret.Ancestor(p.Value.(*datastore.Key)) } else { filt := prop + "=" for _, v := range vals { p, err := dsF2RProp(d.aeCtx, v) if err != nil { return nil, err } ret = ret.Filter(filt, p.Value) } } } if lnam, lop, lprop := fq.IneqFilterLow(); lnam != "" { p, err := dsF2RProp(d.aeCtx, lprop) if err != nil { return nil, err } ret = ret.Filter(lnam+" "+lop, p.Value) } if hnam, hop, hprop := fq.IneqFilterHigh(); hnam != "" { p, err := dsF2RProp(d.aeCtx, hprop) if err != nil { return nil, err } ret = ret.Filter(hnam+" "+hop, p.Value) } if fq.EventuallyConsistent() { ret = ret.EventualConsistency() } if fq.KeysOnly() { ret = ret.KeysOnly() } if lim, ok := fq.Limit(); ok { ret = ret.Limit(int(lim)) } if off, ok := fq.Offset(); ok { ret = ret.Offset(int(off)) } for _, o := range fq.Orders() { ret = ret.Order(o.String()) } ret = ret.Project(fq.Project()...) if fq.Distinct() { ret = ret.Distinct() } return ret, nil }
func reduce(fq *ds.FinalizedQuery, aid, ns string, isTxn bool) (*reducedQuery, error) { if err := fq.Valid(aid, ns); err != nil { return nil, err } if isTxn && fq.Ancestor() == nil { return nil, fmt.Errorf("queries within a transaction must include an Ancestor filter") } if num := numComponents(fq); num > MaxQueryComponents { return nil, fmt.Errorf( "gae/memory: query is too large. may not have more than "+ "%d filters + sort orders + ancestor total: had %d", MaxQueryComponents, num) } ret := &reducedQuery{ aid: aid, ns: ns, kind: fq.Kind(), suffixFormat: fq.Orders(), } eqFilts := fq.EqFilters() ret.eqFilters = make(map[string]stringset.Set, len(eqFilts)) for prop, vals := range eqFilts { sVals := stringset.New(len(vals)) for _, v := range vals { sVals.Add(string(serialize.ToBytes(v))) } ret.eqFilters[prop] = sVals } startD, endD := GetBinaryBounds(fq) // Now we check the start and end cursors. // // Cursors are composed of a list of IndexColumns at the beginning, followed // by the raw bytes to use for the suffix. The cursor is only valid if all of // its IndexColumns match our proposed suffixFormat, as calculated above. // // Cursors are mutually exclusive with the start/end we picked up from the // inequality. In a well formed query, they indicate a subset of results // bounded by the inequality. Technically if the start cursor is not >= the // low bound, or the end cursor is < the high bound, it's an error, but for // simplicity we just cap to the narrowest intersection of the inequality and // cursors. ret.start = startD ret.end = endD if start, end := fq.Bounds(); start != nil || end != nil { if start != nil { if c, ok := start.(queryCursor); ok { startCols, startD, err := c.decode() if err != nil { return nil, err } if !sortOrdersEqual(startCols, ret.suffixFormat) { return nil, errors.New("gae/memory: start cursor is invalid for this query") } if ret.start == nil || bytes.Compare(ret.start, startD) < 0 { ret.start = startD } } else { return nil, errors.New("gae/memory: bad cursor type") } } if end != nil { if c, ok := end.(queryCursor); ok { endCols, endD, err := c.decode() if err != nil { return nil, err } if !sortOrdersEqual(endCols, ret.suffixFormat) { return nil, errors.New("gae/memory: end cursor is invalid for this query") } if ret.end == nil || bytes.Compare(endD, ret.end) < 0 { ret.end = endD } } else { return nil, errors.New("gae/memory: bad cursor type") } } } // Finally, verify that we could even /potentially/ do work. If we have // overlapping range ends, then we don't have anything to do. if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 { return nil, ds.ErrNullQuery } ret.numCols = len(ret.suffixFormat) for prop, vals := range ret.eqFilters { if len(ret.suffixFormat) == 1 && prop == "__ancestor__" { continue } ret.numCols += vals.Len() } return ret, nil }