Omnigraph
Guides

Common Patterns

Copy-paste patterns for node operations, traversal, negation, search, branching, and agent coordination.

A quick reference of patterns you'll use repeatedly when building with Omnigraph. Each pattern shows the query (.gq) or command needed.

Node operations

Find by key

query find_person($name: String) {
    match { $p: Person { name: $name } }
    return { $p.name, $p.age }
}

Find by filter

query enterprise_accounts() {
    match { $a: Account { tier: "enterprise" } }
    return { $a.name, $a.tier }
}

Upsert

Load data with --upsert to create or update based on the @key field:

omnigraph load --data people.jsonl --upsert ./repo.omni

Update

mutation update_status($slug: String, $new_status: String) {
    match { $t: Ticket { slug: $slug } }
    set { $t.status: $new_status }
}

Delete

mutation remove_ticket($slug: String) {
    match { $t: Ticket { slug: $slug } }
    delete { $t }
}

Traversal patterns

One hop

query contacts_for_account($account: String) {
    match {
        $a: Account { name: $account }
        $a hasContact $c
    }
    return { $c.name, $c.email }
}

Reverse traversal

query which_account($contact_email: String) {
    match {
        $c: Contact { email: $contact_email }
        $a hasContact $c
    }
    return { $a.name }
}

Two hops

query deals_for_contact($email: String) {
    match {
        $c: Contact { email: $email }
        $a hasContact $c
        $a hasDeal $d
    }
    return { $a.name, $d.name, $d.stage }
}

Variable-depth traversal

query all_dependencies($service: String) {
    match {
        $s: Service { name: $service }
        $s dependsOn* $dep
    }
    return { $dep.name }
}

Cross-type traversal

query full_context($account: String) {
    match {
        $a: Account { name: $account }
        $a hasContact $c
        $a hasDeal $d
        $d assignedTo $rep: Rep
    }
    return { $c.name, $d.name, $d.stage, $rep.name }
}

Negation patterns

Missing relationship

query accounts_without_contacts() {
    match {
        $a: Account
        not { $a hasContact $c }
    }
    return { $a.name }
}

Missing condition

query open_tickets_without_response() {
    match {
        $t: Ticket { status: "open" }
        not {
            $t hasMessage $m
            $m.sender_type = "agent"
        }
    }
    return { $t.slug, $t.title }
}

Aggregation patterns

Count

query tickets_per_status() {
    match { $t: Ticket }
    return { $t.status, count($t) }
}

Sum and average

query pipeline_by_stage() {
    match { $d: Deal }
    return { $d.stage, count($d), sum($d.value), avg($d.value) }
}

Search patterns

query search_tickets($term: String) {
    match {
        $t: Ticket
        search($t.title, $term)
    }
    return { $t.slug, $t.title }
}
query fuzzy_find($term: String) {
    match {
        $p: Person
        fuzzy($p.name, $term, 2)
    }
    return { $p.name }
}

BM25 ranking

query ranked_search($term: String) {
    match {
        $p: Person
        search($p.bio, $term)
    }
    order bm25($p.bio, $term)
    return { $p.name, $p.bio }
}
query similar_people($vec: Vector) {
    match { $p: Person }
    order nearest($p.embedding, $vec)
    limit 10
    return { $p.name, $p.bio }
}
query hybrid_find($term: String, $vec: Vector) {
    match {
        $p: Person
        search($p.bio, $term)
    }
    order rrf(nearest($p.embedding, $vec), bm25($p.bio, $term))
    limit 10
    return { $p.name, $p.bio }
}

Scoped search (search within a traversal)

query search_within_account($account: String, $term: String) {
    match {
        $a: Account { name: $account }
        $a hasActivity $act
        search($act.summary, $term)
    }
    order bm25($act.summary, $term)
    return { $act.summary, $act.date }
}

Branching patterns

Branch per agent

omnigraph branch create --from main enrichment-agent-run-042
# Agent does work
omnigraph branch diff enrichment-agent-run-042 main
omnigraph branch merge enrichment-agent-run-042 --into main

Read-branch-merge cycle

# 1. Read current state
omnigraph read ./repo.omni --query pending_work.gq

# 2. Branch for isolated writes
omnigraph branch create --from main agent-work-session

# 3. Agent writes to branch
omnigraph load --data results.jsonl --branch agent-work-session ./repo.omni

# 4. Validate before merge
omnigraph read ./repo.omni --branch agent-work-session --query validation.gq

# 5. Merge when ready
omnigraph branch merge agent-work-session --into main

Composition patterns

Hub / 360-degree view

Pull everything connected to a central entity:

query account_360($name: String) {
    match {
        $a: Account { name: $name }
        $a hasContact $c
        $a hasDeal $d
        $a hasActivity $act
    }
    return { $c.name, $c.email, $d.name, $d.stage, $act.summary }
}

Chain

One agent's output becomes another agent's input:

query unscored_enriched_accounts() {
    match {
        $a: Account
        $a hasSignal $s: Signal { processed: true }
        not { $a hasScore $sc }
    }
    return { $a.name }
}

Diamond

Two traversal paths converge on the same node:

query shared_contacts($account_a: String, $account_b: String) {
    match {
        $a: Account { name: $account_a }
        $b: Account { name: $account_b }
        $a hasContact $c
        $b hasContact $c
    }
    return { $c.name, $c.email }
}

Query as hook trigger

Use a query to define the condition that triggers an agent:

query stale_accounts() {
    match {
        $a: Account
        not {
            $a hasActivity $act
            $act.date > "2025-01-01"
        }
    }
    return { $a.name }
}
{
  "name": "stale_account_alert",
  "query": "stale_accounts",
  "condition": "result_count > 0",
  "action": "wake_outreach_agent"
}

On this page