Skip to content
Snippets Groups Projects
client.go 13.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright 2016 Dgraph Labs, Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package client
    
    import (
    
    	"github.com/dgraph-io/dgraph/protos"
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    	"github.com/dgraph-io/dgraph/schema"
    
    	"github.com/dgraph-io/dgraph/types"
    	"github.com/dgraph-io/dgraph/x"
    
    	geom "github.com/twpayne/go-geom"
    	"github.com/twpayne/go-geom/encoding/geojson"
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    type opType int
    
    Pawan Rawal's avatar
    Pawan Rawal committed
    	// SET indicates a Set mutation.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    	SET opType = iota
    
    Pawan Rawal's avatar
    Pawan Rawal committed
    	// DEL indicates a Delete mutation.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // A Req represents a single request to the backend Dgraph instance.  Each request may contain multiple set, delete and
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // schema mutations, and a single GraphQL+- query.  IF the query contains GraphQL variables, then the map giving values to these
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // must be stored in the request with the query.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Request returns the protos.Request backing the Req.
    
    func (req *Req) Request() *protos.Request {
    
    func checkSchema(schema protos.SchemaUpdate) error {
    	typ := types.TypeID(schema.ValueType)
    	if typ == types.UidID && schema.Directive == protos.SchemaUpdate_INDEX {
    		// index on uid type
    		return x.Errorf("Index not allowed on predicate of type uid on predicate %s",
    			schema.Predicate)
    	} else if typ != types.UidID && schema.Directive == protos.SchemaUpdate_REVERSE {
    		// reverse on non-uid type
    		return x.Errorf("Cannot reverse for non-uid type on predicate %s", schema.Predicate)
    	}
    	return nil
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetQuery sets the query in req to the given string.
    // The query string is not checked until the request is
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // run, when it is parsed and checked server-side.
    
    Pawan Rawal's avatar
    Pawan Rawal committed
    func (req *Req) SetQuery(q string) {
    	req.gr.Query = q
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetQueryWithVariables sets query q (which contains graphQL variables mapped
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // in vars) as the query in req and sets vars as the corresponding query variables.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Neither the query string nor the variables are checked until the request is run,
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // when it is parsed and checked server-side.
    
    Pawan Rawal's avatar
    Pawan Rawal committed
    func (req *Req) SetQueryWithVariables(q string, vars map[string]string) {
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    func (req *Req) addMutation(e Edge, op opType) {
    
    	if req.gr.Mutation == nil {
    
    		req.gr.Mutation = new(protos.Mutation)
    
    		req.gr.Mutation.Set = append(req.gr.Mutation.Set, &e.nq)
    
    	} else if op == DEL {
    
    		req.gr.Mutation.Del = append(req.gr.Mutation.Del, &e.nq)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Set adds edge e to the set mutation of request req, thus scheduling the edge to be added to the graph when the request is run.
    // The edge must be syntatically valid: have a valid source (a Node), predicate and target (a Node or value), otherwise an error is returned.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // The edge is not checked agaist the schema until the request is run --- so setting a UID edge to a value, for example, doesn't result in an
    // error until the request is run.
    
    func (req *Req) Set(e Edge) {
    
    	req.addMutation(e, SET)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Delete adds edge e to the delete mutation of request req, thus scheduling the edge to be removed from the graph when the request is run.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // The edge must have a valid source (a Node), predicate and target (a Node or value), otherwise an error is returned.  The edge need not represent
    // an edge in the graph --- applying such a mutation simply has no effect.
    
    func (req *Req) Delete(e Edge) {
    
    	req.addMutation(e, DEL)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // AddSchema adds the single schema mutation s to the request.
    func (req *Req) AddSchema(s protos.SchemaUpdate) error {
    
    	if req.gr.Mutation == nil {
    
    		req.gr.Mutation = new(protos.Mutation)
    
    	}
    	req.gr.Mutation.Schema = append(req.gr.Mutation.Schema, &s)
    	return nil
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // AddSchemaFromString parses s for schema mutations and adds each update in s
    // to the request using AddSchema.  The given string should be of the form:
    // edgename: uid @reverse .
    // edge2: string @index(exact) .
    // etc.
    // to use the form "mutuation { schema { ... }}" issue the mutation through 
    // SetQuery.
    func (req *Req) AddSchemaFromString(s string) error {
    	schemaUpdate, err := schema.Parse(s)
    	if err != nil {
    		return err
    	}
    
    	if len(schemaUpdate) == 0 {
    		return nil
    	}
    
    	for _, smut := range schemaUpdate {
    		if err = req.AddSchema(*smut); err != nil {
    			return err
    		}
    	}
    
    	return nil
    }
    
    
    func (req *Req) size() int {
    
    	if req.gr.Mutation == nil {
    		return 0
    	}
    
    	return len(req.gr.Mutation.Set) + len(req.gr.Mutation.Del) + len(req.gr.Mutation.Schema)
    
    }
    
    func (req *Req) reset() {
    	req.gr.Query = ""
    	req.gr.Mutation.Set = req.gr.Mutation.Set[:0]
    	req.gr.Mutation.Del = req.gr.Mutation.Del[:0]
    
    	req.gr.Mutation.Schema = req.gr.Mutation.Schema[:0]
    
    	e  Edge
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    	op opType
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Node representes a single node in the graph.
    
    type Node struct {
    	uid uint64
    	// We can do variables in mutations.
    	varName string
    }
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // String returns Node n as a string
    
    func (n Node) String() string {
    
    	if n.uid != 0 {
    		return fmt.Sprintf("%#x", uint64(n.uid))
    	}
    	return n.varName
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // ConnectTo creates an edge labelled pred from Node n to Node n1
    
    func (n *Node) ConnectTo(pred string, n1 Node) Edge {
    	e := Edge{}
    
    	if len(n.varName) != 0 {
    		e.nq.SubjectVar = n.String()
    	} else {
    		e.nq.Subject = n.String()
    	}
    
    	e.nq.Predicate = pred
    	e.ConnectTo(n1)
    	return e
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Edge create an edge with source Node n and predicate pred, but without a target.
    // The edge needs to be completed by calling Edge.ConnectTo() if the edge is a
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // UID edge, or one of the Edge.SetValue...() functions if the edge is of a scalar type.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // The edge can't be commited to the store --- calling Req.Set() to add the edge to
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // a request will result in an error --- until it is completed.
    
    func (n *Node) Edge(pred string) Edge {
    	e := Edge{}
    
    	if len(n.varName) != 0 {
    		e.nq.SubjectVar = n.String()
    	} else {
    		e.nq.Subject = n.String()
    	}
    
    	e.nq.Predicate = pred
    	return e
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // An Edge represents an edge between a source node and a target (either a node or a value.)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // Facets are stored in the edge.  See Node.Edge(), Node.ConnectTo(), Edge.ConnecTo(),
    // Edge.AddFacet and the Edge.SetValue...() functions to
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // make a valid edge for a set or delete mutation.
    
    type Edge struct {
    	nq protos.NQuad
    }
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // NewEdge creates and Edge from an NQuad.
    
    func NewEdge(nq protos.NQuad) Edge {
    	return Edge{nq}
    }
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // ConnectTo adds Node n as the target of the edge.  If the edge already has a known scalar type,
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // for example if Edge.SetValue...() had been called on the edge, then an error is returned.
    
    func (e *Edge) ConnectTo(n Node) error {
    	if e.nq.ObjectType > 0 {
    		return ErrValue
    
    	if len(n.varName) != 0 {
    		e.nq.ObjectVar = n.String()
    	} else {
    		e.nq.ObjectId = n.String()
    	}
    
    	return nil
    
    func validateStr(val string) error {
    	for idx, c := range val {
    		if c == '"' && (idx == 0 || val[idx-1] != '\\') {
    			return fmt.Errorf(`" must be preceded by a \ in object value`)
    
    	return nil
    }
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueString sets the value of Edge e as string val and sets the type of the edge to types.StringID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // The string must escape " with \, otherwise the edge and type are left unchanged and an error returned.
    
    func (e *Edge) SetValueString(val string) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    
    	if err := validateStr(val); err != nil {
    		return err
    	}
    
    	v, err := types.ObjectValue(types.StringID, val)
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.StringID)
    	return nil
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueInt sets the value of Edge e as int64 val and sets the type of the edge to types.IntID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    func (e *Edge) SetValueInt(val int64) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	v, err := types.ObjectValue(types.IntID, val)
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.IntID)
    	return nil
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueFloat sets the value of Edge e as float64 val and sets the type of the edge to types.FloatID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    func (e *Edge) SetValueFloat(val float64) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	v, err := types.ObjectValue(types.FloatID, val)
    	if err != nil {
    
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.FloatID)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueBool sets the value of Edge e as bool val and sets the type of the edge to types.BoolID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    func (e *Edge) SetValueBool(val bool) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	v, err := types.ObjectValue(types.BoolID, val)
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.BoolID)
    	return nil
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValuePassword sets the value of Edge e as password string val and sets the type of the edge to types.PasswordID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    func (e *Edge) SetValuePassword(val string) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	v, err := types.ObjectValue(types.PasswordID, val)
    	if err != nil {
    
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.PasswordID)
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueDatetime sets the value of Edge e as time.Time dateTime and sets the type of the edge to types.DateTimeID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    func (e *Edge) SetValueDatetime(dateTime time.Time) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	d, err := types.ObjectValue(types.DateTimeID, dateTime)
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = d
    	e.nq.ObjectType = int32(types.DateTimeID)
    	return nil
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueGeoJson sets the value of Edge e as the GeoJSON object parsed from json string and sets the type of the edge to types.GeoID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    // If the string fails to parse with geojson.Unmarshal() the edge is left unchanged and an error returned.
    
    func (e *Edge) SetValueGeoJson(json string) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    
    	var g geom.T
    	// Parse the json
    	err := geojson.Unmarshal([]byte(json), &g)
    	if err != nil {
    		return err
    	}
    
    	geo, err := types.ObjectValue(types.GeoID, g)
    	if err != nil {
    		return err
    	}
    
    	e.nq.ObjectValue = geo
    	e.nq.ObjectType = int32(types.GeoID)
    	return nil
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueDefault sets the value of Edge e as string val and sets the type of the edge to types.DefaultID.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // The string must escape " with \, otherwise the edge and type are left unchanged and an error returned.
    
    func (e *Edge) SetValueDefault(val string) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	if err := validateStr(val); err != nil {
    		return err
    	}
    
    	v, err := types.ObjectValue(types.DefaultID, val)
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.StringID)
    	return nil
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // SetValueBytes allows setting the value of an edge to raw bytes and sets the type of the edge to types.BinaryID.
    // If the edge had previous been assigned another value (even of another type), the value and type are overwritten.
    // If the edge has previously been connected to a node, the edge and type are left unchanged and ErrConnected is returned.
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // the bytes are encoded as base64.
    
    func (e *Edge) SetValueBytes(val []byte) error {
    	if len(e.nq.ObjectId) > 0 {
    		return ErrConnected
    	}
    	dst := make([]byte, base64.StdEncoding.EncodedLen(len(val)))
    	base64.StdEncoding.Encode(dst, val)
    	v, err := types.ObjectValue(types.BinaryID, []byte(dst))
    	if err != nil {
    		return err
    	}
    	e.nq.ObjectValue = v
    	e.nq.ObjectType = int32(types.BinaryID)
    	return nil
    }
    
    
    MichaelJCompton's avatar
    MichaelJCompton committed
    // AddFacet adds the key, value pair as facets on Edge e.  No checking is done.
    
    func (e *Edge) AddFacet(key, val string) {
    	e.nq.Facets = append(e.nq.Facets, &protos.Facet{
    		Key: key,
    		Val: val,
    	})