mirror of
https://github.com/hanzoai/graph.git
synced 2026-10-30 18:35:37 +00:00
Derived from Dgraph (github.com/dgraph-io/dgraph) at commit 64804bd2, Apache-2.0. See NOTICE. WHAT THIS IS. The graph engine as a Go library: posting lists, the DQL parser, the type and tokenizer system, the index and schema layers, and the wire types. 39,170 lines of the upstream's 166,337. THE STORE is github.com/luxfi/zapdb, a Badger fork that kept the package name, the subpackages (options, pb, y) and the API — Open, OpenManaged, DB, Options — so the 74 import sites are aliased rather than rewritten. THE CODEC is ZAP. zapdb serializes with ZAP, not protobuf: its pb package builds the ZAP types by default and the protobuf ones only under `-tags grpc`, a path that does not compile (table/builder.go calls pb.Marshal, which that tag removes). One call site in posting needed converting; every other type here is Hanzo's own protobuf and is untouched. NO TRANSPORT. An embedded engine dials nothing, so nothing here imports google.golang.org/grpc. Removed: 20 transport functions in x, the generated service stubs for Raft, Zero and Worker (1,657 lines), and the JWT helper — identity belongs to IAM. The core called none of them. NOT KEPT: worker, conn, raftwal, the cluster commands, the GraphQL layer and the test harnesses. `query`, the DQL execution engine, went with them because it depends on worker; parsing DQL is available here, executing it is not. The history of the upstream project lives at github.com/dgraph-io/dgraph. This repository begins at the derivation because three quarters of that history describes code this fork does not carry.
218 lines
6.0 KiB
Go
218 lines
6.0 KiB
Go
/*
|
|
* SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package dql
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/dgraph-io/dgo/v250/protos/api"
|
|
"github.com/hanzoai/graph/protos/pb"
|
|
"github.com/hanzoai/graph/types"
|
|
"github.com/hanzoai/graph/x"
|
|
)
|
|
|
|
var (
|
|
errInvalidUID = errors.New("UID must to be greater than 0")
|
|
)
|
|
|
|
// Mutation stores the strings corresponding to set and delete operations.
|
|
type Mutation struct {
|
|
Cond string
|
|
Set []*api.NQuad
|
|
Del []*api.NQuad
|
|
AllowedPreds []string
|
|
|
|
Metadata *pb.Metadata
|
|
}
|
|
|
|
// ParseUid parses the given string into an UID. This method returns with an error
|
|
// if the string cannot be parsed or the parsed UID is zero.
|
|
func ParseUid(xid string) (uint64, error) {
|
|
// If string represents a UID, convert to uint64 and return.
|
|
uid, err := strconv.ParseUint(xid, 0, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if uid == 0 {
|
|
return 0, errInvalidUID
|
|
}
|
|
return uid, nil
|
|
}
|
|
|
|
// NQuad is an alias for the NQuad type in the API protobuf library.
|
|
type NQuad struct {
|
|
*api.NQuad
|
|
}
|
|
|
|
func TypeValFrom(val *api.Value) types.Val {
|
|
switch val.Val.(type) {
|
|
case *api.Value_BytesVal:
|
|
return types.Val{Tid: types.BinaryID, Value: val.GetBytesVal()}
|
|
case *api.Value_IntVal:
|
|
return types.Val{Tid: types.IntID, Value: val.GetIntVal()}
|
|
case *api.Value_StrVal:
|
|
return types.Val{Tid: types.StringID, Value: val.GetStrVal()}
|
|
case *api.Value_BoolVal:
|
|
return types.Val{Tid: types.BoolID, Value: val.GetBoolVal()}
|
|
case *api.Value_DoubleVal:
|
|
return types.Val{Tid: types.FloatID, Value: val.GetDoubleVal()}
|
|
case *api.Value_GeoVal:
|
|
return types.Val{Tid: types.GeoID, Value: val.GetGeoVal()}
|
|
case *api.Value_BigfloatVal:
|
|
return types.Val{Tid: types.BigFloatID, Value: val.GetBigfloatVal()}
|
|
case *api.Value_DatetimeVal:
|
|
return types.Val{Tid: types.DateTimeID, Value: val.GetDatetimeVal()}
|
|
case *api.Value_PasswordVal:
|
|
return types.Val{Tid: types.PasswordID, Value: val.GetPasswordVal()}
|
|
case *api.Value_Vfloat32Val:
|
|
return types.Val{
|
|
Tid: types.VFloatID,
|
|
Value: types.BytesAsFloatArray(val.GetVfloat32Val()),
|
|
}
|
|
case *api.Value_DefaultVal:
|
|
return types.Val{Tid: types.DefaultID, Value: val.GetDefaultVal()}
|
|
}
|
|
|
|
return types.Val{Tid: types.StringID, Value: ""}
|
|
}
|
|
|
|
func byteVal(nq NQuad) ([]byte, types.TypeID, error) {
|
|
// We infer object type from type of value. We set appropriate type in parse
|
|
// function or the Go client has already set.
|
|
p := TypeValFrom(nq.ObjectValue)
|
|
// These three would have already been marshalled to bytes by the client or
|
|
// in parse function.
|
|
if p.Tid == types.GeoID || p.Tid == types.DateTimeID || p.Tid == types.BigFloatID {
|
|
return p.Value.([]byte), p.Tid, nil
|
|
}
|
|
|
|
p1 := types.ValueForType(types.BinaryID)
|
|
if err := types.Marshal(p, &p1); err != nil {
|
|
return []byte{}, p.Tid, err
|
|
}
|
|
return p1.Value.([]byte), p.Tid, nil
|
|
}
|
|
|
|
func toUid(subject string, newToUid map[string]uint64) (uid uint64, err error) {
|
|
if id, err := ParseUid(subject); err == nil || err == errInvalidUID {
|
|
return id, err
|
|
}
|
|
// It's an xid
|
|
if id, present := newToUid[subject]; present {
|
|
return id, err
|
|
}
|
|
return 0, errors.Errorf("UID not found/generated for xid %s\n", subject)
|
|
}
|
|
|
|
var emptyEdge pb.DirectedEdge
|
|
|
|
func (nq NQuad) createEdgePrototype(subjectUid uint64) *pb.DirectedEdge {
|
|
return &pb.DirectedEdge{
|
|
Entity: subjectUid,
|
|
Attr: nq.Predicate,
|
|
Namespace: nq.Namespace,
|
|
Lang: nq.Lang,
|
|
Facets: nq.Facets,
|
|
}
|
|
}
|
|
|
|
// CreateUidEdge returns a Directed edge connecting the given subject and object UIDs.
|
|
func (nq NQuad) CreateUidEdge(subjectUid uint64, objectUid uint64) *pb.DirectedEdge {
|
|
out := nq.createEdgePrototype(subjectUid)
|
|
out.ValueId = objectUid
|
|
out.ValueType = pb.Posting_UID
|
|
return out
|
|
}
|
|
|
|
// CreateValueEdge returns a DirectedEdge with the given subject. The predicate,
|
|
// language, and facet values are derived from the NQuad.
|
|
func (nq NQuad) CreateValueEdge(subjectUid uint64) (*pb.DirectedEdge, error) {
|
|
var err error
|
|
|
|
out := nq.createEdgePrototype(subjectUid)
|
|
if err = copyValue(out, nq); err != nil {
|
|
return &emptyEdge, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ToDeletePredEdge takes an NQuad of the form '* p *' and returns the equivalent
|
|
// directed edge. Returns an error if the NQuad does not have the expected form.
|
|
func (nq NQuad) ToDeletePredEdge() (*pb.DirectedEdge, error) {
|
|
if nq.Subject != x.Star && nq.ObjectValue.String() != x.Star {
|
|
return &emptyEdge, errors.Errorf("Subject and object both should be *. Got: %+v", nq)
|
|
}
|
|
|
|
out := &pb.DirectedEdge{
|
|
// This along with edge.ObjectValue == x.Star would indicate
|
|
// that we want to delete the predicate.
|
|
Entity: 0,
|
|
Attr: nq.Predicate,
|
|
Namespace: nq.Namespace,
|
|
Lang: nq.Lang,
|
|
Facets: nq.Facets,
|
|
Op: pb.DirectedEdge_DEL,
|
|
}
|
|
|
|
if err := copyValue(out, nq); err != nil {
|
|
return &emptyEdge, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ToEdgeUsing determines the UIDs for the provided XIDs and populates the
|
|
// xidToUid map.
|
|
func (nq NQuad) ToEdgeUsing(newToUid map[string]uint64) (*pb.DirectedEdge, error) {
|
|
var edge *pb.DirectedEdge
|
|
sUid, err := toUid(nq.Subject, newToUid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sUid == 0 {
|
|
return nil, errors.Errorf("Subject should be > 0 for nquad: %+v", nq)
|
|
}
|
|
|
|
switch nq.valueType() {
|
|
case x.ValueUid:
|
|
oUid, err := toUid(nq.ObjectId, newToUid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if oUid == 0 {
|
|
return nil, errors.Errorf("ObjectId should be > 0 for nquad: %+v", nq)
|
|
}
|
|
edge = nq.CreateUidEdge(sUid, oUid)
|
|
case x.ValuePlain, x.ValueMulti:
|
|
edge, err = nq.CreateValueEdge(sUid)
|
|
default:
|
|
return &emptyEdge, errors.Errorf("Unknown value type for nquad: %+v", nq)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return edge, nil
|
|
}
|
|
|
|
func copyValue(out *pb.DirectedEdge, nq NQuad) error {
|
|
var err error
|
|
var t types.TypeID
|
|
if out.Value, t, err = byteVal(nq); err != nil {
|
|
return err
|
|
}
|
|
out.ValueType = t.Enum()
|
|
return nil
|
|
}
|
|
|
|
func (nq NQuad) valueType() x.ValueTypeInfo {
|
|
hasValue := nq.ObjectValue != nil
|
|
hasLang := len(nq.Lang) > 0
|
|
hasSpecialId := len(nq.ObjectId) == 0
|
|
return x.ValueType(hasValue, hasLang, hasSpecialId)
|
|
}
|