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  }