github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/gql/mutation.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 gql 18 19 import ( 20 "strconv" 21 22 "github.com/dgraph-io/dgo/protos/api" 23 "github.com/dgraph-io/dgraph/protos/pb" 24 "github.com/dgraph-io/dgraph/types" 25 "github.com/dgraph-io/dgraph/x" 26 "github.com/pkg/errors" 27 ) 28 29 var ( 30 errInvalidUID = errors.New("UID has to be greater than one") 31 ) 32 33 // Mutation stores the strings corresponding to set and delete operations. 34 type Mutation struct { 35 Set []*api.NQuad 36 Del []*api.NQuad 37 } 38 39 // ParseUid parses the given string into an UID. This method returns with an error 40 // if the string cannot be parsed or the parsed UID is zero. 41 func ParseUid(xid string) (uint64, error) { 42 // If string represents a UID, convert to uint64 and return. 43 uid, err := strconv.ParseUint(xid, 0, 64) 44 if err != nil { 45 return 0, err 46 } 47 if uid == 0 { 48 return 0, errInvalidUID 49 } 50 return uid, nil 51 } 52 53 // NQuad is an alias for the NQuad type in the API protobuf library. 54 type NQuad struct { 55 *api.NQuad 56 } 57 58 func typeValFrom(val *api.Value) types.Val { 59 switch val.Val.(type) { 60 case *api.Value_BytesVal: 61 return types.Val{Tid: types.BinaryID, Value: val.GetBytesVal()} 62 case *api.Value_IntVal: 63 return types.Val{Tid: types.IntID, Value: val.GetIntVal()} 64 case *api.Value_StrVal: 65 return types.Val{Tid: types.StringID, Value: val.GetStrVal()} 66 case *api.Value_BoolVal: 67 return types.Val{Tid: types.BoolID, Value: val.GetBoolVal()} 68 case *api.Value_DoubleVal: 69 return types.Val{Tid: types.FloatID, Value: val.GetDoubleVal()} 70 case *api.Value_GeoVal: 71 return types.Val{Tid: types.GeoID, Value: val.GetGeoVal()} 72 case *api.Value_DatetimeVal: 73 return types.Val{Tid: types.DateTimeID, Value: val.GetDatetimeVal()} 74 case *api.Value_PasswordVal: 75 return types.Val{Tid: types.PasswordID, Value: val.GetPasswordVal()} 76 case *api.Value_DefaultVal: 77 return types.Val{Tid: types.DefaultID, Value: val.GetDefaultVal()} 78 } 79 80 return types.Val{Tid: types.StringID, Value: ""} 81 } 82 83 func byteVal(nq NQuad) ([]byte, types.TypeID, error) { 84 // We infer object type from type of value. We set appropriate type in parse 85 // function or the Go client has already set. 86 p := typeValFrom(nq.ObjectValue) 87 // These three would have already been marshalled to bytes by the client or 88 // in parse function. 89 if p.Tid == types.GeoID || p.Tid == types.DateTimeID { 90 return p.Value.([]byte), p.Tid, nil 91 } 92 93 p1 := types.ValueForType(types.BinaryID) 94 if err := types.Marshal(p, &p1); err != nil { 95 return []byte{}, p.Tid, err 96 } 97 return []byte(p1.Value.([]byte)), p.Tid, nil 98 } 99 100 func toUid(subject string, newToUid map[string]uint64) (uid uint64, err error) { 101 if id, err := ParseUid(subject); err == nil || err == errInvalidUID { 102 return id, err 103 } 104 // It's an xid 105 if id, present := newToUid[subject]; present { 106 return id, err 107 } 108 return 0, errors.Errorf("UID not found/generated for xid %s\n", subject) 109 } 110 111 var emptyEdge pb.DirectedEdge 112 113 func (nq NQuad) createEdgePrototype(subjectUid uint64) *pb.DirectedEdge { 114 return &pb.DirectedEdge{ 115 Entity: subjectUid, 116 Attr: nq.Predicate, 117 Label: nq.Label, 118 Lang: nq.Lang, 119 Facets: nq.Facets, 120 } 121 } 122 123 // CreateUidEdge returns a Directed edge connecting the given subject and object UIDs. 124 func (nq NQuad) CreateUidEdge(subjectUid uint64, objectUid uint64) *pb.DirectedEdge { 125 out := nq.createEdgePrototype(subjectUid) 126 out.ValueId = objectUid 127 out.ValueType = pb.Posting_UID 128 return out 129 } 130 131 // CreateValueEdge returns a DirectedEdge with the given subject. The predicate, label, 132 // language, and facet values are derived from the NQuad. 133 func (nq NQuad) CreateValueEdge(subjectUid uint64) (*pb.DirectedEdge, error) { 134 var err error 135 136 out := nq.createEdgePrototype(subjectUid) 137 if err = copyValue(out, nq); err != nil { 138 return &emptyEdge, err 139 } 140 return out, nil 141 } 142 143 // ToDeletePredEdge takes an NQuad of the form '* p *' and returns the equivalent 144 // directed edge. Returns an error if the NQuad does not have the expected form. 145 func (nq NQuad) ToDeletePredEdge() (*pb.DirectedEdge, error) { 146 if nq.Subject != x.Star && nq.ObjectValue.String() != x.Star { 147 return &emptyEdge, errors.Errorf("Subject and object both should be *. Got: %+v", nq) 148 } 149 150 out := &pb.DirectedEdge{ 151 // This along with edge.ObjectValue == x.Star would indicate 152 // that we want to delete the predicate. 153 Entity: 0, 154 Attr: nq.Predicate, 155 Label: nq.Label, 156 Lang: nq.Lang, 157 Facets: nq.Facets, 158 Op: pb.DirectedEdge_DEL, 159 } 160 161 if err := copyValue(out, nq); err != nil { 162 return &emptyEdge, err 163 } 164 return out, nil 165 } 166 167 // ToEdgeUsing determines the UIDs for the provided XIDs and populates the 168 // xidToUid map. 169 func (nq NQuad) ToEdgeUsing(newToUid map[string]uint64) (*pb.DirectedEdge, error) { 170 var edge *pb.DirectedEdge 171 sUid, err := toUid(nq.Subject, newToUid) 172 if err != nil { 173 return nil, err 174 } 175 176 if sUid == 0 { 177 return nil, errors.Errorf("Subject should be > 0 for nquad: %+v", nq) 178 } 179 180 switch nq.valueType() { 181 case x.ValueUid: 182 oUid, err := toUid(nq.ObjectId, newToUid) 183 if err != nil { 184 return nil, err 185 } 186 if oUid == 0 { 187 return nil, errors.Errorf("ObjectId should be > 0 for nquad: %+v", nq) 188 } 189 edge = nq.CreateUidEdge(sUid, oUid) 190 case x.ValuePlain, x.ValueMulti: 191 edge, err = nq.CreateValueEdge(sUid) 192 default: 193 return &emptyEdge, errors.Errorf("Unknown value type for nquad: %+v", nq) 194 } 195 if err != nil { 196 return nil, err 197 } 198 return edge, nil 199 } 200 201 func copyValue(out *pb.DirectedEdge, nq NQuad) error { 202 var err error 203 var t types.TypeID 204 if out.Value, t, err = byteVal(nq); err != nil { 205 return err 206 } 207 out.ValueType = t.Enum() 208 return nil 209 } 210 211 func (nq NQuad) valueType() x.ValueTypeInfo { 212 hasValue := nq.ObjectValue != nil 213 hasLang := len(nq.Lang) > 0 214 hasSpecialId := len(nq.ObjectId) == 0 215 return x.ValueType(hasValue, hasLang, hasSpecialId) 216 }