Example #1
0
// 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)
}
Example #2
0
// 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
}