github.com/m3db/m3@v1.5.0/src/dbnode/network/server/tchannelthrift/convert/convert.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package convert
    22  
    23  import (
    24  	stdctx "context"
    25  	"errors"
    26  	"fmt"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
    30  	tterrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors"
    31  	"github.com/m3db/m3/src/dbnode/storage/index"
    32  	"github.com/m3db/m3/src/dbnode/storage/limits"
    33  	"github.com/m3db/m3/src/dbnode/x/xio"
    34  	"github.com/m3db/m3/src/dbnode/x/xpool"
    35  	"github.com/m3db/m3/src/m3ninx/generated/proto/querypb"
    36  	"github.com/m3db/m3/src/m3ninx/idx"
    37  	"github.com/m3db/m3/src/x/checked"
    38  	"github.com/m3db/m3/src/x/context"
    39  	xerrors "github.com/m3db/m3/src/x/errors"
    40  	"github.com/m3db/m3/src/x/ident"
    41  	xtime "github.com/m3db/m3/src/x/time"
    42  )
    43  
    44  var (
    45  	errUnknownTimeType  = errors.New("unknown time type")
    46  	errUnknownUnit      = errors.New("unknown unit")
    47  	errNilTaggedRequest = errors.New("nil write tagged request")
    48  
    49  	timeZero time.Time
    50  )
    51  
    52  const (
    53  	fetchTaggedTimeType = rpc.TimeType_UNIX_NANOSECONDS
    54  )
    55  
    56  // ToTime converts a value to a time.
    57  func ToTime(value int64, timeType rpc.TimeType) (xtime.UnixNano, error) {
    58  	unit, err := ToDuration(timeType)
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  	// NB(r): Doesn't matter what unit is if we have zero of them.
    63  	if value == 0 {
    64  		return 0, nil
    65  	}
    66  	return xtime.FromNormalizedTime(value, unit), nil
    67  }
    68  
    69  // ToValue converts a time to a value.
    70  func ToValue(t xtime.UnixNano, timeType rpc.TimeType) (int64, error) {
    71  	unit, err := ToDuration(timeType)
    72  	if err != nil {
    73  		return 0, err
    74  	}
    75  
    76  	return t.ToNormalizedTime(unit), nil
    77  }
    78  
    79  // ToDuration converts a time type to a duration.
    80  func ToDuration(timeType rpc.TimeType) (time.Duration, error) {
    81  	unit, err := ToUnit(timeType)
    82  	if err != nil {
    83  		return 0, err
    84  	}
    85  	return unit.Value()
    86  }
    87  
    88  // ToUnit converts a time type to a unit.
    89  func ToUnit(timeType rpc.TimeType) (xtime.Unit, error) {
    90  	switch timeType {
    91  	case rpc.TimeType_UNIX_SECONDS:
    92  		return xtime.Second, nil
    93  	case rpc.TimeType_UNIX_MILLISECONDS:
    94  		return xtime.Millisecond, nil
    95  	case rpc.TimeType_UNIX_MICROSECONDS:
    96  		return xtime.Microsecond, nil
    97  	case rpc.TimeType_UNIX_NANOSECONDS:
    98  		return xtime.Nanosecond, nil
    99  	}
   100  	return 0, errUnknownTimeType
   101  }
   102  
   103  // ToTimeType converts a unit to a time type
   104  func ToTimeType(unit xtime.Unit) (rpc.TimeType, error) {
   105  	switch unit {
   106  	case xtime.Second:
   107  		return rpc.TimeType_UNIX_SECONDS, nil
   108  	case xtime.Millisecond:
   109  		return rpc.TimeType_UNIX_MILLISECONDS, nil
   110  	case xtime.Microsecond:
   111  		return rpc.TimeType_UNIX_MICROSECONDS, nil
   112  	case xtime.Nanosecond:
   113  		return rpc.TimeType_UNIX_NANOSECONDS, nil
   114  	}
   115  	return 0, errUnknownUnit
   116  }
   117  
   118  // ToSegmentsResult is the result of a convert to segments call,
   119  // if the segments were merged then checksum is ptr to the checksum
   120  // otherwise it is nil.
   121  type ToSegmentsResult struct {
   122  	Segments *rpc.Segments
   123  	Checksum *int64
   124  }
   125  
   126  // ToSegments converts a list of blocks to segments.
   127  func ToSegments(ctx context.Context, blocks []xio.BlockReader) (ToSegmentsResult, error) { //nolint: gocyclo
   128  	if len(blocks) == 0 {
   129  		return ToSegmentsResult{}, nil
   130  	}
   131  
   132  	s := &rpc.Segments{}
   133  
   134  	if len(blocks) == 1 {
   135  		// check the deadline before potentially blocking for the results from the disk read. in the worst case we'll
   136  		// wait for one extra block past the rpc deadline.
   137  		select {
   138  		case <-ctx.GoContext().Done():
   139  			return ToSegmentsResult{}, ctx.GoContext().Err()
   140  		default:
   141  		}
   142  		seg, err := blocks[0].Segment()
   143  		if err != nil {
   144  			return ToSegmentsResult{}, err
   145  		}
   146  		if seg.Len() == 0 {
   147  			return ToSegmentsResult{}, nil
   148  		}
   149  		startTime := int64(blocks[0].Start)
   150  		blockSize := xtime.ToNormalizedDuration(blocks[0].BlockSize, time.Nanosecond)
   151  		checksum := int64(seg.CalculateChecksum())
   152  		s.Merged = &rpc.Segment{
   153  			Head:      bytesRef(seg.Head),
   154  			Tail:      bytesRef(seg.Tail),
   155  			StartTime: &startTime,
   156  			BlockSize: &blockSize,
   157  			Checksum:  &checksum,
   158  		}
   159  		return ToSegmentsResult{
   160  			Segments: s,
   161  			Checksum: &checksum,
   162  		}, nil
   163  	}
   164  
   165  	for _, block := range blocks {
   166  		select {
   167  		case <-ctx.GoContext().Done():
   168  			return ToSegmentsResult{}, ctx.GoContext().Err()
   169  		default:
   170  		}
   171  		seg, err := block.Segment()
   172  		if err != nil {
   173  			return ToSegmentsResult{}, err
   174  		}
   175  		if seg.Len() == 0 {
   176  			continue
   177  		}
   178  		startTime := int64(block.Start)
   179  		blockSize := xtime.ToNormalizedDuration(block.BlockSize, time.Nanosecond)
   180  		checksum := int64(seg.CalculateChecksum())
   181  		s.Unmerged = append(s.Unmerged, &rpc.Segment{
   182  			Head:      bytesRef(seg.Head),
   183  			Tail:      bytesRef(seg.Tail),
   184  			StartTime: &startTime,
   185  			BlockSize: &blockSize,
   186  			Checksum:  &checksum,
   187  		})
   188  	}
   189  	if len(s.Unmerged) == 0 {
   190  		return ToSegmentsResult{}, nil
   191  	}
   192  
   193  	return ToSegmentsResult{Segments: s}, nil
   194  }
   195  
   196  func bytesRef(data checked.Bytes) []byte {
   197  	if data != nil {
   198  		return data.Bytes()
   199  	}
   200  	return nil
   201  }
   202  
   203  // ToRPCError converts a server error to a RPC error.
   204  func ToRPCError(err error) *rpc.Error {
   205  	if err == nil {
   206  		return nil
   207  	}
   208  
   209  	// If already an RPC error then just return it.
   210  	var rpcErr *rpc.Error
   211  	if errors.As(err, &rpcErr) {
   212  		return rpcErr
   213  	}
   214  
   215  	if limits.IsQueryLimitExceededError(err) {
   216  		return tterrors.NewResourceExhaustedError(err)
   217  	}
   218  	if xerrors.IsInvalidParams(err) {
   219  		return tterrors.NewBadRequestError(err)
   220  	}
   221  	if xerrors.Is(err, stdctx.Canceled) || xerrors.Is(err, stdctx.DeadlineExceeded) {
   222  		return tterrors.NewTimeoutError(err)
   223  	}
   224  
   225  	return tterrors.NewInternalError(err)
   226  }
   227  
   228  // FetchTaggedConversionPools allows users to pass a pool for conversions.
   229  type FetchTaggedConversionPools interface {
   230  	// ID returns an ident.Pool
   231  	ID() ident.Pool
   232  
   233  	// CheckedBytesWrapperPool returns a CheckedBytesWrapperPool.
   234  	CheckedBytesWrapper() xpool.CheckedBytesWrapperPool
   235  }
   236  
   237  // FromRPCFetchTaggedRequest converts the rpc request type for FetchTaggedRequest into corresponding Go API types.
   238  func FromRPCFetchTaggedRequest(
   239  	req *rpc.FetchTaggedRequest, pools FetchTaggedConversionPools,
   240  ) (ident.ID, index.Query, index.QueryOptions, bool, error) {
   241  	start, rangeStartErr := ToTime(req.RangeStart, fetchTaggedTimeType)
   242  	if rangeStartErr != nil {
   243  		return nil, index.Query{}, index.QueryOptions{}, false, rangeStartErr
   244  	}
   245  
   246  	end, rangeEndErr := ToTime(req.RangeEnd, fetchTaggedTimeType)
   247  	if rangeEndErr != nil {
   248  		return nil, index.Query{}, index.QueryOptions{}, false, rangeEndErr
   249  	}
   250  
   251  	opts := index.QueryOptions{
   252  		StartInclusive:    start,
   253  		EndExclusive:      end,
   254  		RequireExhaustive: req.RequireExhaustive,
   255  		RequireNoWait:     req.RequireNoWait,
   256  	}
   257  	if l := req.SeriesLimit; l != nil {
   258  		opts.SeriesLimit = int(*l)
   259  	}
   260  	if l := req.DocsLimit; l != nil {
   261  		opts.DocsLimit = int(*l)
   262  	}
   263  	if len(req.Source) > 0 {
   264  		opts.Source = req.Source
   265  	}
   266  
   267  	q, err := idx.Unmarshal(req.Query)
   268  	if err != nil {
   269  		return nil, index.Query{}, index.QueryOptions{}, false, err
   270  	}
   271  
   272  	var ns ident.ID
   273  	if pools != nil {
   274  		nsBytes := pools.CheckedBytesWrapper().Get(req.NameSpace)
   275  		ns = pools.ID().BinaryID(nsBytes)
   276  	} else {
   277  		ns = ident.StringID(string(req.NameSpace))
   278  	}
   279  	return ns, index.Query{Query: q}, opts, req.FetchData, nil
   280  }
   281  
   282  // ToRPCFetchTaggedRequest converts the Go `client/` types into rpc request type
   283  // for FetchTaggedRequest.
   284  func ToRPCFetchTaggedRequest(
   285  	ns ident.ID,
   286  	q index.Query,
   287  	opts index.QueryOptions,
   288  	fetchData bool,
   289  ) (rpc.FetchTaggedRequest, error) {
   290  	rangeStart, tsErr := ToValue(opts.StartInclusive, fetchTaggedTimeType)
   291  	if tsErr != nil {
   292  		return rpc.FetchTaggedRequest{}, tsErr
   293  	}
   294  
   295  	rangeEnd, tsErr := ToValue(opts.EndExclusive, fetchTaggedTimeType)
   296  	if tsErr != nil {
   297  		return rpc.FetchTaggedRequest{}, tsErr
   298  	}
   299  
   300  	query, queryErr := idx.Marshal(q.Query)
   301  	if queryErr != nil {
   302  		return rpc.FetchTaggedRequest{}, queryErr
   303  	}
   304  
   305  	request := rpc.FetchTaggedRequest{
   306  		NameSpace:         ns.Bytes(),
   307  		RangeStart:        rangeStart,
   308  		RangeEnd:          rangeEnd,
   309  		FetchData:         fetchData,
   310  		Query:             query,
   311  		RequireExhaustive: opts.RequireExhaustive,
   312  		RequireNoWait:     opts.RequireNoWait,
   313  	}
   314  
   315  	if opts.SeriesLimit > 0 {
   316  		l := int64(opts.SeriesLimit)
   317  		request.SeriesLimit = &l
   318  	}
   319  
   320  	if opts.DocsLimit > 0 {
   321  		l := int64(opts.DocsLimit)
   322  		request.DocsLimit = &l
   323  	}
   324  
   325  	if len(opts.Source) > 0 {
   326  		request.Source = opts.Source
   327  	}
   328  
   329  	return request, nil
   330  }
   331  
   332  // FromRPCAggregateQueryRequest converts the rpc request type for AggregateRawQueryRequest into corresponding Go API types.
   333  func FromRPCAggregateQueryRequest(
   334  	req *rpc.AggregateQueryRequest,
   335  ) (ident.ID, index.Query, index.AggregationOptions, error) {
   336  	start, rangeStartErr := ToTime(req.RangeStart, fetchTaggedTimeType)
   337  	if rangeStartErr != nil {
   338  		return nil, index.Query{}, index.AggregationOptions{}, rangeStartErr
   339  	}
   340  
   341  	end, rangeEndErr := ToTime(req.RangeEnd, fetchTaggedTimeType)
   342  	if rangeEndErr != nil {
   343  		return nil, index.Query{}, index.AggregationOptions{}, rangeEndErr
   344  	}
   345  
   346  	opts := index.AggregationOptions{
   347  		QueryOptions: index.QueryOptions{
   348  			StartInclusive: start,
   349  			EndExclusive:   end,
   350  		},
   351  	}
   352  	if l := req.SeriesLimit; l != nil {
   353  		opts.SeriesLimit = int(*l)
   354  	}
   355  	if l := req.DocsLimit; l != nil {
   356  		opts.DocsLimit = int(*l)
   357  	}
   358  	if r := req.RequireExhaustive; r != nil {
   359  		opts.RequireExhaustive = *r
   360  	}
   361  	if r := req.RequireNoWait; r != nil {
   362  		opts.RequireNoWait = *r
   363  	}
   364  
   365  	if len(req.Source) > 0 {
   366  		opts.Source = req.Source
   367  	}
   368  
   369  	query, err := FromRPCQuery(req.Query)
   370  	if err != nil {
   371  		return nil, index.Query{}, index.AggregationOptions{}, err
   372  	}
   373  
   374  	opts.FieldFilter = make(index.AggregateFieldFilter, 0, len(req.TagNameFilter))
   375  	for _, f := range req.TagNameFilter {
   376  		opts.FieldFilter = append(opts.FieldFilter, []byte(f))
   377  	}
   378  
   379  	if req.AggregateQueryType == rpc.AggregateQueryType_AGGREGATE_BY_TAG_NAME_VALUE {
   380  		opts.Type = index.AggregateTagNamesAndValues
   381  	} else {
   382  		opts.Type = index.AggregateTagNames
   383  	}
   384  
   385  	ns := ident.StringID(req.NameSpace)
   386  	return ns, index.Query{Query: query}, opts, nil
   387  }
   388  
   389  // FromRPCAggregateQueryRawRequest converts the rpc request type for AggregateRawQueryRequest into corresponding Go API types.
   390  func FromRPCAggregateQueryRawRequest(
   391  	req *rpc.AggregateQueryRawRequest,
   392  	pools FetchTaggedConversionPools,
   393  ) (ident.ID, index.Query, index.AggregationOptions, error) {
   394  	start, rangeStartErr := ToTime(req.RangeStart, fetchTaggedTimeType)
   395  	if rangeStartErr != nil {
   396  		return nil, index.Query{}, index.AggregationOptions{}, rangeStartErr
   397  	}
   398  
   399  	end, rangeEndErr := ToTime(req.RangeEnd, fetchTaggedTimeType)
   400  	if rangeEndErr != nil {
   401  		return nil, index.Query{}, index.AggregationOptions{}, rangeEndErr
   402  	}
   403  
   404  	opts := index.AggregationOptions{
   405  		QueryOptions: index.QueryOptions{
   406  			StartInclusive: start,
   407  			EndExclusive:   end,
   408  		},
   409  	}
   410  	if l := req.SeriesLimit; l != nil {
   411  		opts.SeriesLimit = int(*l)
   412  	}
   413  	if l := req.DocsLimit; l != nil {
   414  		opts.DocsLimit = int(*l)
   415  	}
   416  	if r := req.RequireExhaustive; r != nil {
   417  		opts.RequireExhaustive = *r
   418  	}
   419  	if r := req.RequireNoWait; r != nil {
   420  		opts.RequireNoWait = *r
   421  	}
   422  
   423  	if len(req.Source) > 0 {
   424  		opts.Source = req.Source
   425  	}
   426  
   427  	query, err := idx.Unmarshal(req.Query)
   428  	if err != nil {
   429  		return nil, index.Query{}, index.AggregationOptions{}, err
   430  	}
   431  
   432  	opts.FieldFilter = index.AggregateFieldFilter(req.TagNameFilter)
   433  	if req.AggregateQueryType == rpc.AggregateQueryType_AGGREGATE_BY_TAG_NAME_VALUE {
   434  		opts.Type = index.AggregateTagNamesAndValues
   435  	} else {
   436  		opts.Type = index.AggregateTagNames
   437  	}
   438  
   439  	var ns ident.ID
   440  	if pools != nil {
   441  		nsBytes := pools.CheckedBytesWrapper().Get(req.NameSpace)
   442  		ns = pools.ID().BinaryID(nsBytes)
   443  	} else {
   444  		ns = ident.StringID(string(req.NameSpace))
   445  	}
   446  	return ns, index.Query{Query: query}, opts, nil
   447  }
   448  
   449  // ToRPCAggregateQueryRawRequest converts the Go `client/` types into rpc
   450  // request type for AggregateQueryRawRequest.
   451  func ToRPCAggregateQueryRawRequest(
   452  	ns ident.ID,
   453  	q index.Query,
   454  	opts index.AggregationOptions,
   455  ) (rpc.AggregateQueryRawRequest, error) {
   456  	rangeStart, tsErr := ToValue(opts.StartInclusive, fetchTaggedTimeType)
   457  	if tsErr != nil {
   458  		return rpc.AggregateQueryRawRequest{}, tsErr
   459  	}
   460  
   461  	rangeEnd, tsErr := ToValue(opts.EndExclusive, fetchTaggedTimeType)
   462  	if tsErr != nil {
   463  		return rpc.AggregateQueryRawRequest{}, tsErr
   464  	}
   465  
   466  	request := rpc.AggregateQueryRawRequest{
   467  		NameSpace:  ns.Bytes(),
   468  		RangeStart: rangeStart,
   469  		RangeEnd:   rangeEnd,
   470  	}
   471  
   472  	if opts.SeriesLimit > 0 {
   473  		l := int64(opts.SeriesLimit)
   474  		request.SeriesLimit = &l
   475  	}
   476  	if opts.DocsLimit > 0 {
   477  		l := int64(opts.DocsLimit)
   478  		request.DocsLimit = &l
   479  	}
   480  	if opts.RequireExhaustive {
   481  		r := opts.RequireExhaustive
   482  		request.RequireExhaustive = &r
   483  	}
   484  	if opts.RequireNoWait {
   485  		r := opts.RequireNoWait
   486  		request.RequireNoWait = &r
   487  	}
   488  
   489  	if len(opts.Source) > 0 {
   490  		request.Source = opts.Source
   491  	}
   492  
   493  	query, queryErr := idx.Marshal(q.Query)
   494  	if queryErr != nil {
   495  		return rpc.AggregateQueryRawRequest{}, queryErr
   496  	}
   497  	request.Query = query
   498  
   499  	if opts.Type == index.AggregateTagNamesAndValues {
   500  		request.AggregateQueryType = rpc.AggregateQueryType_AGGREGATE_BY_TAG_NAME_VALUE
   501  	} else {
   502  		request.AggregateQueryType = rpc.AggregateQueryType_AGGREGATE_BY_TAG_NAME
   503  	}
   504  
   505  	// TODO(prateek): pool the []byte underlying opts.FieldFilter
   506  	filters := make([][]byte, 0, len(opts.FieldFilter))
   507  	for _, f := range opts.FieldFilter {
   508  		copied := append([]byte(nil), f...)
   509  		filters = append(filters, copied)
   510  	}
   511  	request.TagNameFilter = filters
   512  
   513  	return request, nil
   514  }
   515  
   516  // ToTagsIter returns a tag iterator over the given request.
   517  func ToTagsIter(r *rpc.WriteTaggedRequest) (ident.TagIterator, error) {
   518  	if r == nil {
   519  		return nil, errNilTaggedRequest
   520  	}
   521  
   522  	return &writeTaggedIter{
   523  		rawRequest: r,
   524  		currentIdx: -1,
   525  	}, nil
   526  }
   527  
   528  // NB(prateek): writeTaggedIter is in-efficient in how it handles internal
   529  // allocations. Only use it for non-performance critical RPC endpoints.
   530  type writeTaggedIter struct {
   531  	rawRequest *rpc.WriteTaggedRequest
   532  	currentIdx int
   533  	currentTag ident.Tag
   534  }
   535  
   536  func (w *writeTaggedIter) Next() bool {
   537  	w.release()
   538  	w.currentIdx++
   539  	if w.currentIdx < len(w.rawRequest.Tags) {
   540  		w.currentTag.Name = ident.StringID(w.rawRequest.Tags[w.currentIdx].Name)
   541  		w.currentTag.Value = ident.StringID(w.rawRequest.Tags[w.currentIdx].Value)
   542  		return true
   543  	}
   544  	return false
   545  }
   546  
   547  func (w *writeTaggedIter) release() {
   548  	if i := w.currentTag.Name; i != nil {
   549  		w.currentTag.Name.Finalize()
   550  		w.currentTag.Name = nil
   551  	}
   552  	if i := w.currentTag.Value; i != nil {
   553  		w.currentTag.Value.Finalize()
   554  		w.currentTag.Value = nil
   555  	}
   556  }
   557  
   558  func (w *writeTaggedIter) Current() ident.Tag {
   559  	return w.currentTag
   560  }
   561  
   562  func (w *writeTaggedIter) CurrentIndex() int {
   563  	if w.currentIdx >= 0 {
   564  		return w.currentIdx
   565  	}
   566  	return 0
   567  }
   568  
   569  func (w *writeTaggedIter) Err() error {
   570  	return nil
   571  }
   572  
   573  func (w *writeTaggedIter) Close() {
   574  	w.release()
   575  	w.currentIdx = -1
   576  }
   577  
   578  func (w *writeTaggedIter) Len() int {
   579  	return len(w.rawRequest.Tags)
   580  }
   581  
   582  func (w *writeTaggedIter) Remaining() int {
   583  	if r := len(w.rawRequest.Tags) - 1 - w.currentIdx; r >= 0 {
   584  		return r
   585  	}
   586  	return 0
   587  }
   588  
   589  func (w *writeTaggedIter) Duplicate() ident.TagIterator {
   590  	return &writeTaggedIter{
   591  		rawRequest: w.rawRequest,
   592  		currentIdx: -1,
   593  	}
   594  }
   595  
   596  func (w *writeTaggedIter) Rewind() {
   597  	w.release()
   598  	w.currentIdx = -1
   599  }
   600  
   601  // FromRPCQuery will create a m3ninx index query from an RPC query.
   602  // NB: a nil query is considered equivalent to an `All` query.
   603  func FromRPCQuery(query *rpc.Query) (idx.Query, error) {
   604  	if query == nil {
   605  		return idx.NewAllQuery(), nil
   606  	}
   607  
   608  	queryProto, err := parseQuery(query)
   609  	if err != nil {
   610  		return idx.Query{}, err
   611  	}
   612  
   613  	marshalled, err := queryProto.Marshal()
   614  	if err != nil {
   615  		return idx.Query{}, err
   616  	}
   617  
   618  	return idx.Unmarshal(marshalled)
   619  }
   620  
   621  func parseQuery(query *rpc.Query) (*querypb.Query, error) {
   622  	result := new(querypb.Query)
   623  	if query == nil {
   624  		return nil, xerrors.NewInvalidParamsError(fmt.Errorf("no query specified"))
   625  	}
   626  	if query.All != nil {
   627  		result.Query = &querypb.Query_All{
   628  			All: &querypb.AllQuery{},
   629  		}
   630  	}
   631  	if query.Field != nil {
   632  		result.Query = &querypb.Query_Field{
   633  			Field: &querypb.FieldQuery{
   634  				Field: []byte(query.Field.Field),
   635  			},
   636  		}
   637  	}
   638  	if query.Term != nil {
   639  		result.Query = &querypb.Query_Term{
   640  			Term: &querypb.TermQuery{
   641  				Field: []byte(query.Term.Field),
   642  				Term:  []byte(query.Term.Term),
   643  			},
   644  		}
   645  	}
   646  	if query.Regexp != nil {
   647  		if result.Query != nil {
   648  			return nil, xerrors.NewInvalidParamsError(fmt.Errorf("multiple query types specified"))
   649  		}
   650  		result.Query = &querypb.Query_Regexp{
   651  			Regexp: &querypb.RegexpQuery{
   652  				Field:  []byte(query.Regexp.Field),
   653  				Regexp: []byte(query.Regexp.Regexp),
   654  			},
   655  		}
   656  	}
   657  	if query.Negation != nil {
   658  		if result.Query != nil {
   659  			return nil, xerrors.NewInvalidParamsError(fmt.Errorf("multiple query types specified"))
   660  		}
   661  		inner, err := parseQuery(query.Negation.Query)
   662  		if err != nil {
   663  			return nil, err
   664  		}
   665  		result.Query = &querypb.Query_Negation{
   666  			Negation: &querypb.NegationQuery{
   667  				Query: inner,
   668  			},
   669  		}
   670  	}
   671  	if query.Conjunction != nil {
   672  		if result.Query != nil {
   673  			return nil, xerrors.NewInvalidParamsError(fmt.Errorf("multiple query types specified"))
   674  		}
   675  		var queries []*querypb.Query
   676  		for _, query := range query.Conjunction.Queries {
   677  			inner, err := parseQuery(query)
   678  			if err != nil {
   679  				return nil, err
   680  			}
   681  			queries = append(queries, inner)
   682  		}
   683  		result.Query = &querypb.Query_Conjunction{
   684  			Conjunction: &querypb.ConjunctionQuery{
   685  				Queries: queries,
   686  			},
   687  		}
   688  	}
   689  	if query.Disjunction != nil {
   690  		if result.Query != nil {
   691  			return nil, xerrors.NewInvalidParamsError(fmt.Errorf("multiple query types specified"))
   692  		}
   693  		var queries []*querypb.Query
   694  		for _, query := range query.Disjunction.Queries {
   695  			inner, err := parseQuery(query)
   696  			if err != nil {
   697  				return nil, err
   698  			}
   699  			queries = append(queries, inner)
   700  		}
   701  		result.Query = &querypb.Query_Disjunction{
   702  			Disjunction: &querypb.DisjunctionQuery{
   703  				Queries: queries,
   704  			},
   705  		}
   706  	}
   707  	if result.Query == nil {
   708  		return nil, xerrors.NewInvalidParamsError(fmt.Errorf("no query types specified"))
   709  	}
   710  	return result, nil
   711  }