Performance Comparison: Query Interfaces¶
CongraphDB provides three query interfaces with different performance characteristics. This guide helps you choose the right interface based on your performance requirements.
Executive Summary¶
| Interface | Best For | Performance |
|---|---|---|
| Cypher | Complex queries, analytics | Optimized query execution |
| JavaScript API (CRUD) | Simple operations | Fast direct methods |
| Navigator | Graph traversal | Optimized traversal |
Operation-Level Performance¶
CRUD Operations¶
| Operation | Cypher | JavaScript API | Winner |
|---|---|---|---|
| Create Node | ~2ms | ~0.5ms | JavaScript API |
| Get Node by ID | ~1.5ms | ~0.3ms | JavaScript API |
| Update Node | ~2ms | ~0.5ms | JavaScript API |
| Delete Node | ~2.5ms | ~0.6ms | JavaScript API |
| Create Edge | ~2ms | ~0.7ms | JavaScript API |
| Get Edge | ~1.5ms | ~0.4ms | JavaScript API |
Recommendation: Use JavaScript API for all CRUD operations.
Single-Hop Traversal¶
| Operation | Cypher | JavaScript API | Navigator | Winner |
|---|---|---|---|---|
| Find friends | ~5ms | ~8ms* | ~3ms | Navigator |
| Count friends | ~4ms | ~10ms* | ~2ms | Navigator |
| Filter friends | ~5ms | ~12ms* | ~4ms | Navigator |
*Requires additional queries to resolve node IDs
Recommendation: Use Navigator for single-hop traversals.
Multi-Hop Traversal¶
| Operation | Cypher | JavaScript API | Navigator | Winner |
|---|---|---|---|---|
| Friends of friends (2-hop) | ~8ms | ~25ms* | ~6ms | Navigator |
| 3-hop traversal | ~12ms | ~40ms* | ~9ms | Navigator |
| 4-hop traversal | ~18ms | ~60ms* | ~14ms | Navigator |
*Requires chaining multiple find() calls
Recommendation: Use Navigator for multi-hop traversals (2-4 hops).
Complex Queries¶
| Operation | Cypher | JavaScript API | Navigator | Winner |
|---|---|---|---|---|
| Aggregations (COUNT, SUM) | ~6ms | N/A | N/A | Cypher |
| Pattern comprehension | ~8ms | N/A | N/A | Cypher |
| Path finding (shortestPath) | ~10ms | N/A | ~8ms | Navigator |
| Multi-condition filtering | ~7ms | ~15ms* | ~6ms | Navigator |
*Limited to simple pattern matching
Recommendation: - Aggregations → Cypher - Pattern comprehensions → Cypher - Path finding → Navigator - Multi-hop filtering → Navigator
Detailed Analysis¶
1. CRUD Performance¶
JavaScript API (Fastest)¶
Why it's fast: - Direct method calls - No query parsing overhead - Optimized internal lookups
Cypher (Slower for CRUD)¶
// Requires query parsing and execution
const result = await conn.query(`
MATCH (n:User {id: 'node-id'}) RETURN n
`); // ~1.5ms
Why it's slower: - Query parsing overhead - Query planning - Result transformation
2. Traversal Performance¶
Navigator (Optimized for Traversal)¶
// Fluent traversal with optimized execution
const fof = await api.nav(alice._id)
.out('KNOWS')
.out('KNOWS')
.values(); // ~6ms for 2-hop
Why it's fast: - Pre-optimized traversal patterns - Direct CSR structure access - Minimal intermediate allocations
Cypher (Good for Complex Traversals)¶
// Query optimizer handles complex patterns
const result = await conn.query(`
MATCH (a:User {id: 'alice'})-[:KNOWS]->()-[:KNOWS]->(fof:User)
RETURN fof
`); // ~8ms for 2-hop
Why it's good: - Query optimization - Index usage - Columnar storage benefits
JavaScript API find() (Not Recommended for Multi-hop)¶
// Requires multiple queries
const friends = await api.find({
subject: alice._id,
predicate: 'KNOWS',
object: api.v('friend')
}); // ~8ms
const fof = [];
for (const f of friends) {
const results = await api.find({
subject: f.friend._id,
predicate: 'KNOWS',
object: api.v('fof')
});
fof.push(...results);
} // ~25ms total
Why it's slow: - N+1 query pattern - Multiple round trips - No cross-query optimization
3. Aggregation Performance¶
Cypher (Best)¶
const result = await conn.query(`
MATCH (u:User)-[:KNOWS]->(f:User)
RETURN u.name, COUNT(f) AS friend_count
`); // ~6ms
Why it's fast: - Optimized aggregation operators - Single-pass execution - Columnar storage benefits
JavaScript API (Not Supported)¶
// No native aggregation support
const friends = await api.find({...});
const count = friends.length; // Manual, slow
Why it's slow: - Requires loading all results - Manual counting - No query optimization
4. Path Finding Performance¶
Navigator (Best for Simple Paths)¶
Why it's fast: - BFS algorithm implementation - Early termination when found - Optimized for graph storage
Cypher (Good for Complex Path Finding)¶
const result = await conn.query(`
MATCH p = shortestPath(
(alice:User {id: 'alice'})-[:KNOWS*]-(bob:User {id: 'bob'})
)
RETURN p
`); // ~10ms
Why it's good: - Built-in shortestPath function - Supports variable-length paths - Handles complex constraints
Performance Optimization Tips¶
For CRUD Operations¶
-
Use JavaScript API exclusively
-
Batch operations when possible
-
Use transactions for multiple writes
For Traversals¶
-
Use Navigator for multi-hop
-
Apply filters early
-
Use limit() to reduce result set
For Complex Queries¶
-
Use Cypher for aggregations
-
Use pattern comprehensions
-
Leverage indexes
Scalability Considerations¶
Memory Usage¶
| Interface | Memory Characteristics |
|---|---|
| Cypher | Efficient for large result sets (columnar) |
| JavaScript API | Low overhead for single operations |
| Navigator | Minimal allocations for traversal |
Database Size Impact¶
| Operation | Impact | Recommendation |
|---|---|---|
| Schema definition | Fixed overhead | Define schema upfront |
| Indexes | Increases size | Add indexes selectively |
| WAL file | Grows with writes | Configure checkpointing |
Concurrency¶
| Operation | Concurrent Support | Recommendation |
|---|---|---|
| Reads | Full concurrency | Use multiple connections |
| Writes | Serialized via WAL | Batch writes when possible |
| Transactions | Serializable isolation | Keep transactions short |
Performance Benchmarks¶
Test Environment¶
- CPU: 4-core @ 3.0GHz
- RAM: 16GB
- Database Size: 100K nodes, 500K edges
- Storage: SSD
Benchmark Results¶
Single-Hop Queries¶
Navigator.out().values(): 3,120 ops/sec
Cypher MATCH with RETURN: 2,450 ops/sec
JavaScript API find(): 1,850 ops/sec
Multi-Hop Queries (3-hop)¶
CRUD Operations¶
JavaScript API createNode(): 8,500 ops/sec
JavaScript API getNode(): 12,300 ops/sec
JavaScript API updateNode(): 7,800 ops/sec
Cypher CREATE: 3,200 ops/sec
Cypher MATCH + RETURN: 5,600 ops/sec
Aggregations¶
Cypher COUNT(): 2,100 ops/sec
Cypher AVG(): 1,850 ops/sec
Cypher pattern comprehension: 1,650 ops/sec
Decision Matrix¶
Based on your query type:
| Query Type | Recommended Interface | Reason |
|---|---|---|
| Create node | JavaScript API | 4x faster than Cypher |
| Get node by ID | JavaScript API | 5x faster than Cypher |
| Update node | JavaScript API | 4x faster than Cypher |
| Delete node | JavaScript API | 4x faster than Cypher |
| Single-hop traversal | Navigator | 1.5x faster than Cypher |
| Multi-hop (2-4 hops) | Navigator | 1.3x faster than Cypher |
| Multi-hop (5+ hops) | Cypher | Better optimization |
| Aggregations | Cypher | Only option |
| Pattern comprehensions | Cypher | Only option |
| Path finding | Navigator | Slightly faster |
| Complex filtering | Navigator | More efficient |
Best Practices¶
1. Use the Right Tool for the Job¶
// CRUD: JavaScript API
const user = await api.createNode('User', {...});
// Traversal: Navigator
const friends = await api.nav(user._id).out('KNOWS').values();
// Analytics: Cypher
const result = await conn.query(`
MATCH (u:User)-[:KNOWS]->(f:User)
RETURN COUNT(f)
`);
2. Minimize Round Trips¶
// Bad: Multiple queries
const friends = await api.find({...});
for (const f of friends) {
const friends2 = await api.find({...}); // N+1 problem
}
// Good: Single traversal
const friendsOfFriends = await api.nav(id)
.out('KNOWS')
.out('KNOWS')
.values();
3. Use Transactions for Batch Operations¶
// Bad: Multiple separate operations
await api.createNode('User', {...});
await api.createNode('User', {...});
await api.createNode('User', {...});
// Good: Single transaction
await api.transaction(async (tx) => {
await tx.createNode('User', {...});
await tx.createNode('User', {...});
await tx.createNode('User', {...});
});
4. Filter Early¶
// Bad: Filter in JavaScript
const all = await api.nav(id).out('KNOWS').values();
const filtered = all.filter(f => f.age > 25);
// Good: Filter in database
const filtered = await api.nav(id)
.out('KNOWS')
.where('age > 25')
.values();
See Also¶
- Choosing Your Query Interface - Decision guide
- Navigator API Reference - Navigator documentation
- JavaScript API Reference - Complete API docs