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
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,38 @@ modusGraph has a few limitations to be aware of:
- **Schema evolution**: While modusGraph supports schema inference through tags, evolving an
existing schema with new fields requires careful consideration to avoid data inconsistencies.

## CLI Commands and Examples

modusGraph provides several command-line tools and example applications to help you interact with
and explore the package. These are organized in the `cmd` and `examples` folders:

### Commands (`cmd` folder)

- **`cmd/query`**: A flexible CLI tool for running arbitrary DQL (Dgraph Query Language) queries
against a modusGraph database.
- Reads a query from standard input and prints JSON results.
- Supports file-based modusGraph storage.
- Flags: `--dir`, `--pretty`, `--timeout`, `-v` (verbosity).
- See [`cmd/query/README.md`](./cmd/query/README.md) for usage and examples.

### Examples (`examples` folder)

- **`examples/basic`**: Demonstrates CRUD operations for a simple `Thread` entity.

- Flags: `--dir`, `--addr`, `--cmd`, `--author`, `--name`, `--uid`, `--workspace`.
- Supports create, update, delete, get, and list commands.
- See [`examples/basic/README.md`](./examples/basic/README.md) for details.

- **`examples/load`**: Shows how to load the standard 1million RDF dataset into modusGraph for
benchmarking.

- Downloads, initializes, and loads the dataset into a specified directory.
- Flags: `--dir`, `--verbosity`.
- See [`examples/load/README.md`](./examples/load/README.md) for instructions.

You can use these tools as starting points for your own applications or as references for
integrating modusGraph into your workflow.

## Open Source

The modus framework, including modusGraph, is developed by [Hypermode](https://hypermode.com/) as an
Expand Down
9 changes: 9 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,15 @@ func (c client) DropData(ctx context.Context) error {

// QueryRaw implements raw querying (DQL syntax).
func (c client) QueryRaw(ctx context.Context, q string) ([]byte, error) {
if c.engine != nil {
ns := c.engine.GetDefaultNamespace()
resp, err := ns.Query(ctx, q)
if err != nil {
return nil, err
}
return resp.GetJson(), nil
}

client, err := c.pool.get()
if err != nil {
c.logger.Error(err, "Failed to get client from pool")
Expand Down
78 changes: 78 additions & 0 deletions cmd/query/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# modusGraph Query CLI

This command-line tool allows you to run arbitrary DQL (Dgraph Query Language) queries against a
modusGraph database, either in local file-based mode or (optionally) against a remote
Dgraph-compatible endpoint.

## Requirements

- Go 1.24 or higher
- Access to a directory containing a modusGraph database (created by modusGraph)

## Installation

```bash
# Navigate to the cmd/query directory
cd cmd/query

# Run directly
go run main.go --dir /path/to/modusgraph [options]

# Or build and then run
go build -o modusgraph-query
./modusgraph-query --dir /path/to/modusgraph [options]
```

## Usage

The tool reads a DQL query from standard input and prints the JSON response to standard output.

```sh
Usage of ./main:
--dir string Directory where the modusGraph database is stored (required)
--pretty Pretty-print the JSON output (default true)
--timeout Query timeout duration (default 30s)
-v int Verbosity level for logging (e.g., -v=1, -v=2)
```

### Example: Querying the Graph

```bash
echo '{ q(func: has(name@en), first: 10) { id: uid name@en } }' | go run main.go --dir /tmp/modusgraph
```

### Example: With Verbose Logging

```bash
echo '{ q(func: has(name@en), first: 10) { id: uid name@en } }' | go run main.go --dir /tmp/modusgraph -v 1
```

### Example: Build and Run

```bash
go build -o modusgraph-query
cat query.dql | ./modusgraph-query --dir /tmp/modusgraph
```

## Notes

- The `--dir` flag is required and must point to a directory initialized by modusGraph.
- The query must be provided via standard input.
- Use the `-v` flag to control logging verbosity (higher values show more log output).
- Use the `--pretty=false` flag to disable pretty-printing of the JSON response.
- The tool logs query timing and errors to standard error.

## Example Output

```json
{
"q": [
{ "id": "0x2", "name@en": "Ivan Sen" },
{ "id": "0x3", "name@en": "Peter Lord" }
]
}
```

---

For more advanced usage and integration, see the main [modusGraph documentation](../../README.md).
128 changes: 128 additions & 0 deletions cmd/query/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
* SPDX-License-Identifier: Apache-2.0
*/

package main

import (
"bufio"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/go-logr/stdr"
"github.com/hypermodeinc/modusgraph"
)

func main() {
// Define flags
dirFlag := flag.String("dir", "", "Directory where the modusGraph database is stored")
prettyFlag := flag.Bool("pretty", true, "Pretty-print the JSON output")
timeoutFlag := flag.Duration("timeout", 30*time.Second, "Query timeout duration")
flag.Parse()

// Initialize the stdr logger with the verbosity from -v
stdLogger := log.New(os.Stdout, "", log.LstdFlags)
logger := stdr.NewWithOptions(stdLogger, stdr.Options{LogCaller: stdr.All}).WithName("mg")
vFlag := flag.Lookup("v")
if vFlag != nil {
val, err := strconv.Atoi(vFlag.Value.String())
if err != nil {
log.Fatalf("Error: Invalid verbosity level: %s", vFlag.Value.String())
}
stdr.SetVerbosity(val)
}

// Validate required flags
if *dirFlag == "" {
log.Println("Error: --dir parameter is required")
flag.Usage()
os.Exit(1)
}

// Create clean directory path
dirPath := filepath.Clean(*dirFlag)
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
log.Fatalf("Error: Directory %s does not exist", dirPath)
}

// Initialize modusGraph client with the directory where data is stored
logger.V(1).Info("Initializing modusGraph client", "directory", dirPath)
client, err := modusgraph.NewClient(fmt.Sprintf("file://%s", dirPath),
modusgraph.WithLogger(logger))
if err != nil {
logger.Error(err, "Failed to initialize modusGraph client")
os.Exit(1)
}
defer client.Close()

// Read query from stdin
reader := bufio.NewReader(os.Stdin)
query := ""
for {
line, err := reader.ReadString('\n')
if err != nil && err != io.EOF {
logger.Error(err, "Error reading from stdin")
os.Exit(1)
}

query += line

if err == io.EOF {
break
}
}

query = strings.TrimSpace(query)
if query == "" {
logger.Error(nil, "Empty query provided")
os.Exit(1)
}

logger.V(1).Info("Executing query", "query", query)

// Set up context with timeout
ctx, cancel := context.WithTimeout(context.Background(), *timeoutFlag)
defer cancel()

start := time.Now()

// Execute the query
resp, err := client.QueryRaw(ctx, query)
if err != nil {
logger.Error(err, "Query execution failed")
os.Exit(1)
}

elapsed := time.Since(start)
elapsedMs := float64(elapsed.Nanoseconds()) / 1e6
logger.V(1).Info("Query completed", "elapsed_ms", elapsedMs)

// Format and print the response
if *prettyFlag {
var data any
if err := json.Unmarshal(resp, &data); err != nil {
logger.Error(err, "Failed to parse JSON response")
os.Exit(1)
}

prettyJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
logger.Error(err, "Failed to format JSON response")
os.Exit(1)
}

fmt.Println(string(prettyJSON))
} else {
fmt.Println(string(resp))
}
}
14 changes: 9 additions & 5 deletions examples/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -84,11 +85,14 @@ func main() {
// Initialize standard logger with stdr
stdLogger := log.New(os.Stdout, "", log.LstdFlags)
logger := stdr.NewWithOptions(stdLogger, stdr.Options{LogCaller: stdr.All}).WithName("mg")

// Set verbosity level
stdr.SetVerbosity(1)

logger.Info("Logger initialized")
vFlag := flag.Lookup("v")
if vFlag != nil {
val, err := strconv.Atoi(vFlag.Value.String())
if err != nil {
log.Fatalf("Error: Invalid verbosity level: %s", vFlag.Value.String())
}
stdr.SetVerbosity(val)
}

// Initialize modusGraph client with logger
client, err := mg.NewClient(endpoint,
Expand Down
74 changes: 74 additions & 0 deletions examples/load/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# modusGraph 1Million Dataset Loader

This command-line application demonstrates how to load the 1million dataset into modusGraph. The
1million dataset consists of approximately one million RDF triples representing relationships
between various entities and is commonly used for benchmarking graph database performance.

## Requirements

- Go 1.24 or higher
- Approximately 500MB of disk space for the downloaded dataset
- Internet connection (to download the dataset files)

## Usage

```sh
# Navigate to the examples/load directory
cd examples/load

# Run directly
go run main.go --dir /path/to/data/directory

# Or build and then run
go build -o modusgraph-loader
./modusgraph-loader --dir /path/to/data/directory
```

### Command Line Options

```sh
Usage of ./modusgraph-loader:
--dir string Directory where modusGraph will initialize and store the 1million dataset (required)
--verbosity int Verbosity level (0-2) (default 1)
```

## How It Works

1. The application creates the specified directory if it doesn't exist
2. It initializes a modusGraph engine in that directory
3. Downloads the 1million schema and RDF data files from the Dgraph benchmarks repository
4. Drops any existing data in the modusGraph instance
5. Loads the schema and RDF data into the database
6. Provides progress and timing information

## Performance Considerations

- Loading the 1million dataset may take several minutes depending on your hardware
- The application sets a 30-minute timeout for the loading process
- Memory usage will peak during the loading process

## Using the Loaded Dataset

After loading is complete, you can use the database in other applications by initializing modusGraph
with the same directory:

```go
// Initialize modusGraph client with the same directory
client, err := mg.NewClient("file:///path/to/data/directory")
if err != nil {
// handle error
}
defer client.Close()

// Now you can run queries against the 1million dataset
```

## Dataset Details

The 1million dataset represents:

- Films, directors, and actors
- Relationships between these entities
- Various properties like names, dates, and film details

This is a great dataset for learning and testing graph query capabilities.
Loading