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 }