github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/types/facets/utils.go (about)

     1  /*
     2   * Copyright 2017-2018 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package facets
    18  
    19  import (
    20  	"math"
    21  	"sort"
    22  	"strconv"
    23  	"unicode"
    24  
    25  	"github.com/dgraph-io/dgo/protos/api"
    26  	"github.com/dgraph-io/dgraph/protos/pb"
    27  	"github.com/dgraph-io/dgraph/tok"
    28  	"github.com/dgraph-io/dgraph/types"
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  // SortAndValidate sorts And validates the facets.
    33  func SortAndValidate(fs []*api.Facet) error {
    34  	if len(fs) == 0 {
    35  		return nil
    36  	}
    37  	sort.Slice(fs, func(i, j int) bool {
    38  		return fs[i].Key < fs[j].Key
    39  	})
    40  	for i := 1; i < len(fs); i++ {
    41  		if fs[i-1].Key == fs[i].Key {
    42  			return errors.Errorf("Repeated keys are not allowed in facets. But got %s",
    43  				fs[i].Key)
    44  		}
    45  	}
    46  	return nil
    47  }
    48  
    49  // CopyFacets makes a copy of facets of the posting which are requested in param.Keys.
    50  func CopyFacets(fcs []*api.Facet, param *pb.FacetParams) (fs []*api.Facet) {
    51  	if param == nil || fcs == nil {
    52  		return nil
    53  	}
    54  	// facets and param.keys are both sorted,
    55  	// We also need all keys if param.AllKeys is true.
    56  	numKeys := len(param.Param)
    57  	numFacets := len(fcs)
    58  	for kidx, fidx := 0, 0; (param.AllKeys || kidx < numKeys) && fidx < numFacets; {
    59  		f := fcs[fidx]
    60  		if param.AllKeys || param.Param[kidx].Key == f.Key {
    61  			fcopy := &api.Facet{
    62  				Key:     f.Key,
    63  				Value:   nil,
    64  				ValType: f.ValType,
    65  			}
    66  			if !param.AllKeys {
    67  				fcopy.Alias = param.Param[kidx].Alias
    68  			}
    69  			fcopy.Value = make([]byte, len(f.Value))
    70  			copy(fcopy.Value, f.Value)
    71  			fs = append(fs, fcopy)
    72  			kidx++
    73  			fidx++
    74  		} else if f.Key > param.Param[kidx].Key {
    75  			kidx++
    76  		} else {
    77  			fidx++
    78  		}
    79  	}
    80  	return fs
    81  }
    82  
    83  // valAndValType returns interface val and valtype for facet.
    84  func valAndValType(val string) (interface{}, api.Facet_ValType, error) {
    85  	if len(val) == 0 { // empty string case
    86  		return "", api.Facet_STRING, nil
    87  	}
    88  	// strings should be in quotes.
    89  	if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
    90  		uq, err := strconv.Unquote(val)
    91  		return uq, api.Facet_STRING, errors.Wrapf(err, "could not unquote %q:", val)
    92  	}
    93  	if intVal, err := strconv.ParseInt(val, 0, 64); err == nil {
    94  		return int64(intVal), api.Facet_INT, nil
    95  	} else if numErr := err.(*strconv.NumError); numErr.Err == strconv.ErrRange {
    96  		// if we have only digits in val, then val is a big integer : return error
    97  		// otherwise try to parse as float.
    98  		allNumChars := true
    99  		for _, v := range val {
   100  			if !unicode.IsDigit(v) {
   101  				allNumChars = false
   102  				break
   103  			}
   104  		}
   105  		if allNumChars {
   106  			return nil, api.Facet_INT, err
   107  		}
   108  	}
   109  	if floatVal, err := strconv.ParseFloat(val, 64); err == nil {
   110  		// We can't store NaN as it is because it serializes into invalid JSON.
   111  		if math.IsNaN(floatVal) {
   112  			return nil, api.Facet_FLOAT, errors.Errorf("Got invalid value: NaN")
   113  		}
   114  
   115  		return floatVal, api.Facet_FLOAT, nil
   116  	} else if numErr := err.(*strconv.NumError); numErr.Err == strconv.ErrRange {
   117  		return nil, api.Facet_FLOAT, err
   118  	}
   119  	if val == "true" || val == "false" {
   120  		return val == "true", api.Facet_BOOL, nil
   121  	}
   122  	if t, err := types.ParseTime(val); err == nil {
   123  		return t, api.Facet_DATETIME, nil
   124  	}
   125  	return nil, api.Facet_STRING, errors.Errorf("Could not parse the facet value : [%s]", val)
   126  }
   127  
   128  // FacetFor returns Facet for given key and val.
   129  func FacetFor(key, val string) (*api.Facet, error) {
   130  	v, vt, err := valAndValType(val)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	facet, err := ToBinary(key, v, vt)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	if vt == api.Facet_STRING {
   141  		// tokenize val.
   142  		facet.Tokens, err = tok.GetTermTokens([]string{v.(string)})
   143  		if err == nil {
   144  			sort.Strings(facet.Tokens)
   145  		}
   146  	}
   147  	return facet, err
   148  }
   149  
   150  // ToBinary converts the given value into a binary value.
   151  func ToBinary(key string, value interface{}, sourceType api.Facet_ValType) (
   152  	*api.Facet, error) {
   153  	// convert facet val interface{} to binary
   154  	sourceTid, err := TypeIDFor(&api.Facet{ValType: sourceType})
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	targetVal := &types.Val{Tid: types.BinaryID}
   160  	if err = types.Marshal(types.Val{Tid: sourceTid, Value: value}, targetVal); err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	return &api.Facet{Key: key, Value: targetVal.Value.([]byte), ValType: sourceType}, nil
   165  }
   166  
   167  // TypeIDFor gives TypeID for facet.
   168  func TypeIDFor(f *api.Facet) (types.TypeID, error) {
   169  	switch f.ValType {
   170  	case api.Facet_INT:
   171  		return types.IntID, nil
   172  	case api.Facet_FLOAT:
   173  		return types.FloatID, nil
   174  	case api.Facet_BOOL:
   175  		return types.BoolID, nil
   176  	case api.Facet_DATETIME:
   177  		return types.DateTimeID, nil
   178  	case api.Facet_STRING:
   179  		return types.StringID, nil
   180  	default:
   181  		return types.DefaultID, errors.Errorf("Unrecognized facet type: %v", f.ValType)
   182  	}
   183  }
   184  
   185  // ValFor converts Facet into types.Val.
   186  func ValFor(f *api.Facet) (types.Val, error) {
   187  	val := types.Val{Tid: types.BinaryID, Value: f.Value}
   188  	facetTid, err := TypeIDFor(f)
   189  	if err != nil {
   190  		return types.Val{}, err
   191  	}
   192  
   193  	return types.Convert(val, facetTid)
   194  }