Overview
The $edge operator is one of GenosDB’s most powerful features. It transforms standard queries into graph exploration tools , allowing you to traverse entire relationship trees in a single declarative query.
Graph traversal uses the link relationships you create between nodes to discover connected data at any depth.
How $edge Works
A query with $edge has two logical parts:
Find Starting Points
The main query conditions identify the node(s) from which traversal begins.
Traverse and Filter Descendants
The $edge sub-query is applied to every descendant node found by following links recursively.
Return Matching Descendants
The final result contains only the descendant nodes that match the $edge criteria, not the starting nodes.
Basic Syntax
const { results } = await db . map ({
query: {
// 1. Conditions to find starting node(s)
type: 'folder' ,
name: 'Documents' ,
// 2. Traverse from starting nodes
$edge: {
// 3. Filter for descendants matching this sub-query
type: 'file' ,
extension: 'pdf'
}
}
})
// results contains only the PDF files found under Documents
Simple Traversal Example
Setting Up the Graph
import { gdb } from 'genosdb'
const db = await gdb ( 'file-system' )
// Create folders
await db . put ({ type: 'folder' , name: 'Documents' }, 'folder:docs' )
await db . put ({ type: 'folder' , name: 'Work' }, 'folder:work' )
await db . put ({ type: 'folder' , name: 'Personal' }, 'folder:personal' )
// Create files
await db . put ({ type: 'file' , name: 'report.pdf' , size: 1024 }, 'file:report' )
await db . put ({ type: 'file' , name: 'notes.txt' , size: 512 }, 'file:notes' )
await db . put ({ type: 'file' , name: 'photo.jpg' , size: 2048 }, 'file:photo' )
// Create folder hierarchy
await db . link ( 'folder:docs' , 'folder:work' ) // docs -> work
await db . link ( 'folder:docs' , 'folder:personal' ) // docs -> personal
await db . link ( 'folder:work' , 'file:report' ) // work -> report.pdf
await db . link ( 'folder:work' , 'file:notes' ) // work -> notes.txt
await db . link ( 'folder:personal' , 'file:photo' ) // personal -> photo.jpg
Traversing the Graph
// Find all files under Documents (regardless of depth)
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Documents' ,
$edge: {
type: 'file' // Only return files
}
}
})
console . log ( results )
// [
// { id: 'file:report', value: { type: 'file', name: 'report.pdf', ... } },
// { id: 'file:notes', value: { type: 'file', name: 'notes.txt', ... } },
// { id: 'file:photo', value: { type: 'file', name: 'photo.jpg', ... } }
// ]
The starting node (folder:docs) is not included in the results. Only the descendants that match the $edge filter are returned.
Advanced Traversal Examples
Filtering Descendants
// Find only large PDF files under Documents
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Documents' ,
$edge: {
type: 'file' ,
extension: 'pdf' ,
size: { $gt: 1000 } // Larger than 1000 bytes
}
}
})
Multiple Starting Points
// Find files in any folder named "Archive"
const { results } = await db . map ({
query: {
type: 'folder' ,
name: { $like: '%Archive%' }, // Matches any folder with "Archive" in name
$edge: {
type: 'file'
}
}
})
Complex Descendant Filters
// Find images or videos in the Media folder
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Media' ,
$edge: {
type: 'file' ,
$or: [
{ extension: { $in: [ 'jpg' , 'png' , 'gif' ] } }, // Images
{ extension: { $in: [ 'mp4' , 'avi' , 'mov' ] } } // Videos
]
}
}
})
Social Network Example
Building a Social Graph
const db = await gdb ( 'social-network' )
// Create users
await db . put ({ type: 'user' , name: 'Alice' , age: 30 }, 'user:alice' )
await db . put ({ type: 'user' , name: 'Bob' , age: 25 }, 'user:bob' )
await db . put ({ type: 'user' , name: 'Charlie' , age: 35 }, 'user:charlie' )
await db . put ({ type: 'user' , name: 'Diana' , age: 28 }, 'user:diana' )
await db . put ({ type: 'user' , name: 'Eve' , age: 22 }, 'user:eve' )
// Create posts
await db . put ({ type: 'post' , content: 'Hello world!' , likes: 10 }, 'post:1' )
await db . put ({ type: 'post' , content: 'GenosDB is great' , likes: 25 }, 'post:2' )
await db . put ({ type: 'post' , content: 'Learning graphs' , likes: 5 }, 'post:3' )
// Create connections
await db . link ( 'user:alice' , 'user:bob' ) // Alice -> Bob (friend)
await db . link ( 'user:alice' , 'user:charlie' ) // Alice -> Charlie (friend)
await db . link ( 'user:bob' , 'user:diana' ) // Bob -> Diana (friend)
await db . link ( 'user:charlie' , 'user:eve' ) // Charlie -> Eve (friend)
// Link users to their posts
await db . link ( 'user:alice' , 'post:1' )
await db . link ( 'user:bob' , 'post:2' )
await db . link ( 'user:diana' , 'post:3' )
Finding Friends’ Posts
// Find all posts by Alice's friends (direct and indirect)
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'post' // Traverse to any connected posts
}
}
})
console . log ( results )
// Returns post:1, post:2, post:3 (posts by Alice, Bob, and Diana)
Finding Friends-of-Friends
// Find all people in Alice's extended network
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'user' // Traverse to connected users
}
}
})
console . log ( results . map ( r => r . value . name ))
// ['Bob', 'Charlie', 'Diana', 'Eve'] (all reachable users)
Filtered Network Traversal
// Find adult friends (age >= 18) in Alice's network
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'user' ,
age: { $gte: 25 } // Only users 25 or older
}
}
})
console . log ( results . map ( r => r . value . name ))
// ['Bob', 'Charlie', 'Diana'] (Eve is 22, filtered out)
Organization Hierarchy Example
// Create organization structure
await db . put ({ type: 'company' , name: 'TechCorp' }, 'company:tech' )
await db . put ({ type: 'department' , name: 'Engineering' }, 'dept:eng' )
await db . put ({ type: 'department' , name: 'Design' }, 'dept:design' )
await db . put ({ type: 'team' , name: 'Backend' , budget: 50000 }, 'team:backend' )
await db . put ({ type: 'team' , name: 'Frontend' , budget: 45000 }, 'team:frontend' )
await db . put ({ type: 'team' , name: 'UX' , budget: 30000 }, 'team:ux' )
await db . put ({ type: 'team' , name: 'UI' , budget: 28000 }, 'team:ui' )
// Build hierarchy
await db . link ( 'company:tech' , 'dept:eng' )
await db . link ( 'company:tech' , 'dept:design' )
await db . link ( 'dept:eng' , 'team:backend' )
await db . link ( 'dept:eng' , 'team:frontend' )
await db . link ( 'dept:design' , 'team:ux' )
await db . link ( 'dept:design' , 'team:ui' )
// Find all well-funded teams in the company
const { results } = await db . map ({
query: {
type: 'company' ,
name: 'TechCorp' ,
$edge: {
type: 'team' ,
budget: { $gte: 40000 } // Teams with >= 40k budget
}
}
})
console . log ( results . map ( r => r . value . name ))
// ['Backend', 'Frontend'] (UX and UI teams filtered out)
Combining $edge with Other Operators
With Logical Operators
// Find PDFs or large images in Documents
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Documents' ,
$edge: {
type: 'file' ,
$or: [
{ extension: 'pdf' },
{
$and: [
{ extension: { $in: [ 'jpg' , 'png' ] } },
{ size: { $gt: 1000000 } } // > 1MB
]
}
]
}
}
})
With Text Search
// Find documents containing "report" in Alice's folders
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'file' ,
name: { $text: 'report' }
}
}
})
With Sorting and Pagination
// Find recent posts from Alice's network, sorted by likes
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'post' ,
createdAt: { $gt: lastWeek }
}
},
field: 'likes' , // Sort by likes
order: 'desc' , // Highest first
$limit: 10 // Top 10
})
Real-Time Graph Traversal
Make graph queries reactive:
const { results , unsubscribe } = await db . map (
{
query: {
type: 'user' ,
name: 'Alice' ,
$edge: {
type: 'post'
}
}
},
({ id , value , action }) => {
if ( action === 'added' ) {
console . log ( 'New post in network:' , value )
} else if ( action === 'removed' ) {
console . log ( 'Post removed:' , id )
}
}
)
// Initial posts
console . log ( 'Initial posts:' , results )
// Later: cleanup
unsubscribe ()
Reactive graph queries update when:
New links are created
Linked nodes are updated
Linked nodes are deleted
Traversal Depth
By default, $edge traverses the entire descendant tree with no depth limit:
// Traverses all levels: children, grandchildren, great-grandchildren, etc.
const { results } = await db . map ({
query: {
id: 'root' ,
$edge: { type: 'file' }
}
})
Deep or cyclic graphs can cause performance issues. Design your graph structure carefully and use specific filters in the $edge sub-query.
Optimize with Specific Filters
// ❌ Slow: Traverses everything then filters
const { results } = await db . map ({
query: {
type: 'folder' ,
$edge: {} // No filter - traverses all descendants
}
})
// ✅ Fast: Filters during traversal
const { results } = await db . map ({
query: {
type: 'folder' ,
$edge: {
type: 'file' , // Only traverse to files
extension: 'pdf' // Further filter
}
}
})
Limit Results
Always use $limit for large graphs:
const { results } = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: { type: 'post' }
},
$limit: 20 // Return at most 20 posts
})
Common Patterns
Find All Leaf Nodes
// Find files (leaf nodes) under a folder
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Root' ,
$edge: {
type: 'file' // Files are leaf nodes (no outgoing links)
}
}
})
Path Validation
// Check if a specific file is accessible from a folder
const { results } = await db . map ({
query: {
type: 'folder' ,
name: 'Shared' ,
$edge: {
id: 'file:secret.pdf' // Does this specific file exist in the tree?
}
}
})
const isAccessible = results . length > 0
Dependency Resolution
// Find all dependencies of a package
await db . put ({ type: 'package' , name: 'react' }, 'pkg:react' )
await db . put ({ type: 'package' , name: 'react-dom' }, 'pkg:react-dom' )
await db . put ({ type: 'package' , name: 'scheduler' }, 'pkg:scheduler' )
await db . link ( 'pkg:react' , 'pkg:react-dom' )
await db . link ( 'pkg:react-dom' , 'pkg:scheduler' )
const { results } = await db . map ({
query: {
type: 'package' ,
name: 'react' ,
$edge: {
type: 'package' // All dependencies (direct and transitive)
}
}
})
console . log ( 'Dependencies:' , results . map ( r => r . value . name ))
// ['react-dom', 'scheduler']
Bidirectional Traversal
GenosDB’s $edge only follows outgoing links . For bidirectional traversal, create links in both directions:
// Create bidirectional friendship
await db . link ( 'user:alice' , 'user:bob' ) // Alice -> Bob
await db . link ( 'user:bob' , 'user:alice' ) // Bob -> Alice
// Now both can traverse to each other
const aliceFriends = await db . map ({
query: {
type: 'user' ,
name: 'Alice' ,
$edge: { type: 'user' }
}
})
const bobFriends = await db . map ({
query: {
type: 'user' ,
name: 'Bob' ,
$edge: { type: 'user' }
}
})
Debugging Graph Queries
// Inspect the graph structure
const { result : node } = await db . get ( 'folder:docs' )
console . log ( 'Outgoing links:' , node . edges )
// Find all nodes linking to a specific node (reverse lookup)
const { results : incomingLinks } = await db . map ({
query: {} // All nodes
})
const pointingToFile = incomingLinks . filter ( n =>
n . edges . includes ( 'file:report' )
)
console . log ( 'Nodes pointing to file:report:' , pointingToFile )
Queries Learn query operators to use in $edge filters
CRUD Operations Understand how to create links with the link method
Real-Time Subscriptions Make graph queries reactive
Collaborative Editor See graph traversal in a complex application