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.omniUpdate
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
Full-text search
query search_tickets($term: String) {
match {
$t: Ticket
search($t.title, $term)
}
return { $t.slug, $t.title }
}Fuzzy search
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 }
}Vector search
query similar_people($vec: Vector) {
match { $p: Person }
order nearest($p.embedding, $vec)
limit 10
return { $p.name, $p.bio }
}Hybrid search
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 mainRead-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 mainComposition 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"
}