// TODO(djd): This function is ugly in its current context. Refactor. func callNext(ctx context.Context, client *Client, req *pb.RunQueryRequest, resp *pb.RunQueryResponse, offset, limit int32) (*pb.RunQueryResponse, error) { if resp.GetBatch().EndCursor == nil { return nil, errors.New("datastore: internal error: server did not return a cursor") } q := req.GetQuery() q.StartCursor = resp.Batch.EndCursor q.Offset = offset if limit >= 0 { q.Limit = &wrapperspb.Int32Value{limit} } return client.client.RunQuery(ctx, req) }
// toProto converts the query to a protocol buffer. func (q *Query) toProto(req *pb.RunQueryRequest) error { if len(q.projection) != 0 && q.keysOnly { return errors.New("datastore: query cannot both project and be keys-only") } dst := &pb.Query{} if q.kind != "" { dst.Kind = []*pb.KindExpression{{Name: q.kind}} } if q.projection != nil { for _, propertyName := range q.projection { dst.Projection = append(dst.Projection, &pb.Projection{Property: &pb.PropertyReference{Name: propertyName}}) } if q.distinct { for _, propertyName := range q.projection { dst.DistinctOn = append(dst.DistinctOn, &pb.PropertyReference{Name: propertyName}) } } } if q.keysOnly { dst.Projection = []*pb.Projection{{Property: &pb.PropertyReference{Name: keyFieldName}}} } var filters []*pb.Filter for _, qf := range q.filter { if qf.FieldName == "" { return errors.New("datastore: empty query filter field name") } v, err := interfaceToProto(reflect.ValueOf(qf.Value).Interface(), false) if err != nil { return fmt.Errorf("datastore: bad query filter value type: %v", err) } op, ok := operatorToProto[qf.Op] if !ok { return errors.New("datastore: unknown query filter operator") } xf := &pb.PropertyFilter{ Op: op, Property: &pb.PropertyReference{Name: qf.FieldName}, Value: v, } filters = append(filters, &pb.Filter{ FilterType: &pb.Filter_PropertyFilter{xf}, }) } if q.ancestor != nil { filters = append(filters, &pb.Filter{ FilterType: &pb.Filter_PropertyFilter{&pb.PropertyFilter{ Property: &pb.PropertyReference{Name: "__key__"}, Op: pb.PropertyFilter_HAS_ANCESTOR, Value: &pb.Value{ValueType: &pb.Value_KeyValue{keyToProto(q.ancestor)}}, }}}) } if len(filters) == 1 { dst.Filter = filters[0] } else if len(filters) > 1 { dst.Filter = &pb.Filter{FilterType: &pb.Filter_CompositeFilter{&pb.CompositeFilter{ Op: pb.CompositeFilter_AND, Filters: filters, }}} } for _, qo := range q.order { if qo.FieldName == "" { return errors.New("datastore: empty query order field name") } xo := &pb.PropertyOrder{ Property: &pb.PropertyReference{Name: qo.FieldName}, Direction: sortDirectionToProto[qo.Direction], } dst.Order = append(dst.Order, xo) } if q.limit >= 0 { dst.Limit = &wrapperspb.Int32Value{q.limit} } dst.Offset = q.offset dst.StartCursor = q.start dst.EndCursor = q.end if t := q.trans; t != nil { if t.id == nil { return errExpiredTransaction } if q.eventual { return errors.New("datastore: cannot use EventualConsistency query in a transaction") } req.ReadOptions = &pb.ReadOptions{ ConsistencyType: &pb.ReadOptions_Transaction{t.id}, } } if q.eventual { req.ReadOptions = &pb.ReadOptions{&pb.ReadOptions_ReadConsistency_{pb.ReadOptions_EVENTUAL}} } req.QueryType = &pb.RunQueryRequest_Query{dst} return nil }