| Index: impl/memory/datastore_query.go
 | 
| diff --git a/impl/memory/datastore_query.go b/impl/memory/datastore_query.go
 | 
| index a5d15d21359d19f70e7960d12859b59f2661f429..65b6b311d44a01bc1f0c5f29e9c6969118b28592 100644
 | 
| --- a/impl/memory/datastore_query.go
 | 
| +++ b/impl/memory/datastore_query.go
 | 
| @@ -15,6 +15,7 @@ import (
 | 
|  	ds "github.com/luci/gae/service/datastore"
 | 
|  	"github.com/luci/gae/service/datastore/serialize"
 | 
|  	"github.com/luci/luci-go/common/cmpbin"
 | 
| +	"github.com/luci/luci-go/common/stringset"
 | 
|  )
 | 
|  
 | 
|  // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver.
 | 
| @@ -165,7 +166,7 @@ type queryImpl struct {
 | 
|  
 | 
|  	// prop -> encoded values (which are ds.Property objects)
 | 
|  	// "__ancestor__" is the key for Ancestor queries.
 | 
| -	eqFilters          map[string]stringSet
 | 
| +	eqFilters          map[string]stringset.Set
 | 
|  	ineqFilter         queryIneqFilter
 | 
|  	order              []ds.IndexColumn
 | 
|  	startCursor        []byte
 | 
| @@ -226,12 +227,8 @@ func (q *queryImpl) reduce(ns string, isTxn bool) (*reducedQuery, error) {
 | 
|  			"gae/memory: Distinct() only makes sense on projection queries.")
 | 
|  	}
 | 
|  	if q.eqFilters["__ancestor__"] != nil && q.ineqFilter.prop == "__key__" {
 | 
| -		anc := []byte(nil)
 | 
| -		for k := range q.eqFilters["__ancestor__"] {
 | 
| -			anc = []byte(k)
 | 
| -			break
 | 
| -		}
 | 
| -		anc = anc[:len(anc)-1]
 | 
| +		ancS, _ := q.eqFilters["__ancestor__"].Peek()
 | 
| +		anc := []byte(ancS[:len(ancS)-1])
 | 
|  		if q.ineqFilter.start != nil && !bytes.HasPrefix(q.ineqFilter.start, anc) {
 | 
|  			return nil, errors.New(
 | 
|  				"gae/memory: __key__ inequality filter has a value outside of Ancestor()")
 | 
| @@ -356,7 +353,7 @@ func (q *queryImpl) reduce(ns string, isTxn bool) (*reducedQuery, error) {
 | 
|  		if len(ret.suffixFormat) == 1 && prop == "__ancestor__" {
 | 
|  			continue
 | 
|  		}
 | 
| -		ret.numCols += len(vals)
 | 
| +		ret.numCols += vals.Len()
 | 
|  	}
 | 
|  
 | 
|  	return ret, nil
 | 
| @@ -373,7 +370,7 @@ func (q *queryImpl) numComponents() int {
 | 
|  		}
 | 
|  	}
 | 
|  	for _, v := range q.eqFilters {
 | 
| -		numComponents += len(v)
 | 
| +		numComponents += v.Len()
 | 
|  	}
 | 
|  	return numComponents
 | 
|  }
 | 
| @@ -387,9 +384,9 @@ func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
 | 
|  		return q
 | 
|  	}
 | 
|  	nq := *q
 | 
| -	nq.eqFilters = make(map[string]stringSet, len(q.eqFilters))
 | 
| +	nq.eqFilters = make(map[string]stringset.Set, len(q.eqFilters))
 | 
|  	for prop, vals := range q.eqFilters {
 | 
| -		nq.eqFilters[prop] = vals.dup()
 | 
| +		nq.eqFilters[prop] = vals.Dup()
 | 
|  	}
 | 
|  	nq.order = make([]ds.IndexColumn, len(q.order))
 | 
|  	copy(nq.order, q.order)
 | 
| @@ -441,9 +438,11 @@ func (q *queryImpl) Distinct() ds.Query {
 | 
|  func (q *queryImpl) addEqFilt(prop string, p ds.Property) {
 | 
|  	binVal := string(serialize.ToBytes(p))
 | 
|  	if cur, ok := q.eqFilters[prop]; !ok {
 | 
| -		q.eqFilters[prop] = stringSet{binVal: {}}
 | 
| +		s := stringset.New(1)
 | 
| +		s.Add(binVal)
 | 
| +		q.eqFilters[prop] = s
 | 
|  	} else {
 | 
| -		cur.add(binVal)
 | 
| +		cur.Add(binVal)
 | 
|  	}
 | 
|  }
 | 
|  
 | 
| @@ -584,9 +583,9 @@ func (q *queryImpl) Project(fieldName ...string) ds.Query {
 | 
|  			if q.keysOnly {
 | 
|  				return errors.New("cannot project a keysOnly query")
 | 
|  			}
 | 
| -			dupCheck := stringSet{}
 | 
| +			dupCheck := stringset.New(len(fieldName) + len(q.project))
 | 
|  			for _, f := range fieldName {
 | 
| -				if !dupCheck.add(f) {
 | 
| +				if !dupCheck.Add(f) {
 | 
|  					return fmt.Errorf("cannot project on the same field twice: %q", f)
 | 
|  				}
 | 
|  				if f == "" {
 | 
| 
 |