Skip to content
This repository was archived by the owner on Sep 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 93 additions & 42 deletions api.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,127 @@
package modusdb

import (
"context"
"fmt"
"reflect"

"github.com/dgraph-io/dgraph/v24/x"
"github.com/dgraph-io/dgraph/v24/dql"
"github.com/dgraph-io/dgraph/v24/schema"
)

type ModusDbOption func(*modusDbOptions)

type modusDbOptions struct {
namespace uint64
}

func WithNamespace(namespace uint64) ModusDbOption {
return func(o *modusDbOptions) {
o.namespace = namespace
func Create[T any](db *DB, object *T, ns ...uint64) (uint64, *T, error) {
db.mutex.Lock()
defer db.mutex.Unlock()
if len(ns) > 1 {
return 0, object, fmt.Errorf("only one namespace is allowed")
}
ctx, n, err := getDefaultNamespace(db, ns...)
if err != nil {
return 0, object, err
}
}

func getDefaultNamespace(db *DB, ns ...uint64) (context.Context, *Namespace, error) {
dbOpts := &modusDbOptions{
namespace: db.defaultNamespace.ID(),
gid, err := db.z.nextUID()
if err != nil {
return 0, object, err
}
for _, ns := range ns {
WithNamespace(ns)(dbOpts)

dms := make([]*dql.Mutation, 0)
sch := &schema.ParsedSchema{}
err = generateCreateDqlMutationsAndSchema[T](ctx, n, *object, gid, &dms, sch)
if err != nil {
return 0, object, err
}

n, err := db.getNamespaceWithLock(dbOpts.namespace)
err = n.alterSchemaWithParsed(ctx, sch)
if err != nil {
return nil, nil, err
return 0, object, err
}

ctx := context.Background()
ctx = x.AttachNamespace(ctx, n.ID())
err = applyDqlMutations(ctx, db, dms)
if err != nil {
return 0, object, err
}

return ctx, n, nil
return getByGid[T](ctx, n, gid)
}

func Create[T any](db *DB, object *T, ns ...uint64) (uint64, *T, error) {
func Upsert[T any](db *DB, object *T, ns ...uint64) (uint64, *T, bool, error) {

var wasFound bool
db.mutex.Lock()
defer db.mutex.Unlock()
if len(ns) > 1 {
return 0, object, fmt.Errorf("only one namespace is allowed")
return 0, object, false, fmt.Errorf("only one namespace is allowed")
}
if object == nil {
return 0, nil, false, fmt.Errorf("object is nil")
}

ctx, n, err := getDefaultNamespace(db, ns...)
if err != nil {
return 0, object, err
return 0, object, false, err
}

gid, err := db.z.nextUID()
gid, cf, err := getUniqueConstraint[T](*object)
if err != nil {
return 0, object, err
return 0, nil, false, err
}

dms, sch, err := generateCreateDqlMutationsAndSchema(n, object, gid)
dms := make([]*dql.Mutation, 0)
sch := &schema.ParsedSchema{}
err = generateCreateDqlMutationsAndSchema[T](ctx, n, *object, gid, &dms, sch)
if err != nil {
return 0, object, err
return 0, nil, false, err
}

ctx = x.AttachNamespace(ctx, n.ID())

err = n.alterSchemaWithParsed(ctx, sch)
if err != nil {
return 0, object, err
return 0, nil, false, err
}

err = applyDqlMutations(ctx, db, dms)
if err != nil {
return 0, object, err
if gid != 0 {
gid, _, err = getByGidWithObject[T](ctx, n, gid, *object)
if err != nil && err != ErrNoObjFound {
return 0, nil, false, err
}
wasFound = err == nil
} else if cf != nil {
gid, _, err = getByConstrainedFieldWithObject[T](ctx, n, *cf, *object)
if err != nil && err != ErrNoObjFound {
return 0, nil, false, err
}
wasFound = err == nil
}
if gid == 0 {
gid, err = db.z.nextUID()
if err != nil {
return 0, nil, false, err
}
}

v := reflect.ValueOf(object).Elem()
dms = make([]*dql.Mutation, 0)
err = generateCreateDqlMutationsAndSchema[T](ctx, n, *object, gid, &dms, sch)
if err != nil {
return 0, nil, false, err
}

gidField := v.FieldByName("Gid")
err = applyDqlMutations(ctx, db, dms)
if err != nil {
return 0, nil, false, err
}

if gidField.IsValid() && gidField.CanSet() && gidField.Kind() == reflect.Uint64 {
gidField.SetUint(gid)
gid, object, err = getByGid[T](ctx, n, gid)
if err != nil {
return 0, nil, false, err
}

return gid, object, nil
return gid, object, wasFound, nil
}

func Get[T any, R UniqueField](db *DB, uniqueField R, ns ...uint64) (uint64, *T, error) {
db.mutex.Lock()
defer db.mutex.Unlock()
if len(ns) > 1 {
return 0, nil, fmt.Errorf("only one namespace is allowed")
}
ctx, n, err := getDefaultNamespace(db, ns...)
if err != nil {
return 0, nil, err
Expand All @@ -104,6 +140,9 @@ func Get[T any, R UniqueField](db *DB, uniqueField R, ns ...uint64) (uint64, *T,
func Delete[T any, R UniqueField](db *DB, uniqueField R, ns ...uint64) (uint64, *T, error) {
db.mutex.Lock()
defer db.mutex.Unlock()
if len(ns) > 1 {
return 0, nil, fmt.Errorf("only one namespace is allowed")
}
ctx, n, err := getDefaultNamespace(db, ns...)
if err != nil {
return 0, nil, err
Expand All @@ -125,7 +164,19 @@ func Delete[T any, R UniqueField](db *DB, uniqueField R, ns ...uint64) (uint64,
}

if cf, ok := any(uniqueField).(ConstrainedField); ok {
return getByConstrainedField[T](ctx, n, cf)
uid, obj, err := getByConstrainedField[T](ctx, n, cf)
if err != nil {
return 0, nil, err
}

dms := generateDeleteDqlMutations(n, uid)

err = applyDqlMutations(ctx, db, dms)
if err != nil {
return 0, nil, err
}

return uid, obj, nil
}

return 0, nil, fmt.Errorf("invalid unique field type")
Expand Down
41 changes: 41 additions & 0 deletions api_dql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package modusdb

import "fmt"

type QueryFunc func() string

const (
objQuery = `
{
obj(%s) {
uid
expand(_all_) {
uid
expand(_all_)
dgraph.type
}
dgraph.type
%s
}
}
`

funcUid = `func: uid(%d)`
funcEq = `func: eq(%s, %s)`
)

func buildUidQuery(gid uint64) QueryFunc {
return func() string {
return fmt.Sprintf(funcUid, gid)
}
}

func buildEqQuery(key, value any) QueryFunc {
return func() string {
return fmt.Sprintf(funcEq, key, value)
}
}

func formatObjQuery(qf QueryFunc, extraFields string) string {
return fmt.Sprintf(objQuery, qf(), extraFields)
}
Loading
Loading