Skip to content
Closed
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
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ go.work.sum
.env

cpu_profile.prof

# IDE config
.idea/
.vscode/

# Tool config directories
.osgrep
.opencode
.claude

# Benchmark result files
load_test/*.json

# Built binaries
/query
/modusgraphgen
/modusgraph-gen
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,69 @@ if err != nil {

These operations are useful for testing or when you need to reset your database state.

## Code Generation

modusGraph includes a code generation tool that reads your Go structs and produces a fully typed
client library with CRUD operations, query builders, auto-paging iterators, functional options, and
an optional CLI.

### Installation

```sh
go install github.com/matthewmcneely/modusgraph/cmd/modusgraphgen@latest
```

### Usage

Add a `go:generate` directive to your package:

```go
//go:generate go run github.com/matthewmcneely/modusgraph/cmd/modusgraphgen
```

Then run:

```sh
go generate ./...
```

### What Gets Generated

| Template | Output | Scope |
|----------|--------|-------|
| client | `client_gen.go` | Once -- typed `Client` with sub-clients per entity |
| page_options | `page_options_gen.go` | Once -- `First(n)` and `Offset(n)` pagination |
| iter | `iter_gen.go` | Once -- auto-paging `SearchIter` and `ListIter` |
| entity | `<entity>_gen.go` | Per entity -- `Get`, `Add`, `Update`, `Delete`, `Search`, `List` |
| options | `<entity>_options_gen.go` | Per entity -- functional options for each scalar field |
| query | `<entity>_query_gen.go` | Per entity -- fluent query builder |
| cli | `cmd/<pkg>/main.go` | Once -- Kong CLI with subcommands per entity |

### Flags

| Flag | Default | Description |
|------|---------|-------------|
| `-pkg` | `.` | Path to the target Go package directory |
| `-output` | same as `-pkg` | Output directory for generated files |
| `-cli-dir` | `{output}/cmd/{package}` | Output directory for CLI main.go |
| `-cli-name` | package name | Name for CLI binary |
| `-with-validator` | `false` | Enable struct validation in generated CLI |

### Entity Detection

A struct is recognized as an entity when it has both of these fields:

```go
UID string `json:"uid,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
```

All other exported fields with `json` and optional `dgraph` struct tags are parsed as entity fields.
Edge relationships are detected when a field type is `[]OtherEntity` where `OtherEntity` is another
struct in the same package. See the
[Defining Your Graph with Structs](#defining-your-graph-with-structs) section above for the full
struct tag reference.

## Limitations

modusGraph has a few limitations to be aware of:
Expand Down
17 changes: 17 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import (
"github.com/go-playground/validator/v10"
)

// DgraphMapper is implemented by generated entities with private fields.
// It provides a map-based serialization path for Dgraph mutations, allowing
// private fields to be persisted without requiring unsafe reflection or
// modifications to the dgman library.
type DgraphMapper interface {
DgraphMap() map[string]interface{}
}

// Client provides an interface for ModusGraph operations
type Client interface {
// Insert adds a new object or slice of objects to the database.
Expand Down Expand Up @@ -363,6 +371,9 @@ func (c client) Insert(ctx context.Context, obj any) error {
}

return c.process(ctx, obj, "Insert", func(tx *dg.TxnContext, obj any) ([]string, error) {
if mapped, ok := toDgraphMap(obj); ok {
return mutateWithMap(tx, obj, mapped)
}
return tx.MutateBasic(obj)
})
}
Expand All @@ -380,6 +391,9 @@ func (c client) InsertRaw(ctx context.Context, obj any) error {
}

return c.process(ctx, obj, "Insert", func(tx *dg.TxnContext, obj any) ([]string, error) {
if mapped, ok := toDgraphMap(obj); ok {
return mutateWithMap(tx, obj, mapped)
}
return tx.MutateBasic(obj)
})
}
Expand Down Expand Up @@ -408,6 +422,9 @@ func (c client) Update(ctx context.Context, obj any) error {
}

return c.process(ctx, obj, "Update", func(tx *dg.TxnContext, obj any) ([]string, error) {
if mapped, ok := toDgraphMap(obj); ok {
return mutateWithMap(tx, obj, mapped)
}
return tx.MutateBasic(obj)
})
}
Expand Down
Loading
Loading