Files
zeekay f5aaeb712e Hanzo Graph — the engine, on zapdb, ZAP-native
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.
2026-08-20 21:26:27 -07:00

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)
}