Neo4j
Neo4j is a native graph database designed to store and navigate relationships efficiently, perfect for connected data and network analysis.
Overview
- Versions: 2025.08, 2025.07, 5.26 (default: 5.26)
- Default Port: 7687 (Bolt protocol)
- Cluster Support: No (Single node only)
- Use Cases: Graph databases, relationships, networks, recommendation engines
- Features: Cypher queries, APOC procedures, graph algorithms
Key Features
- Native Graph Storage: Optimized for storing and querying connected data
- Cypher Query Language: Expressive, SQL-like query language for graphs
- ACID Transactions: Full transaction support for data integrity
- Graph Algorithms: Built-in algorithms for path finding, centrality, community detection
- APOC Library: Awesome Procedures On Cypher - extensive utility functions
- Index-Free Adjacency: Traverse relationships without index lookups
- Schema Flexibility: Optional schema with constraints and indexes
- Full-Text Search: Built-in full-text indexing capabilities
Resources
Choose the add-on's resources on the create form:
| Setting | Options | Default |
|---|---|---|
| CPU (vCPU) | Any number of cores, e.g. 0.5, 1, 2 | 0.5 |
| Memory | Any amount in GB, at least the type's minimum | 1 GB |
| Disk | Any amount in GB | 10 GB |
| GPU Count | 0-8 (0 for CPU-only) | 0 |
Creating a Neo4j Add-on
- Navigate to Add-ons and click Create Add-on
- On the Create New Add-on page, select Neo4j as the type
- Choose a version (2025.08, 2025.07, or 5.26)
- Configure:
- Add-on Label (required): descriptive name (e.g., "knowledge-graph")
- Description (optional): purpose and notes
- Resources: CPU, memory, and disk for your workload
- Optionally enable automatic backups:
- Schedule: Hourly, Daily, Weekly, or Monthly
- Retention: number of backups to keep (1-30, default 7)
- Click Create Add-on
Connection Information
Once the add-on is running, the Connection tab of the add-on details page shows the internal host (for apps), port, username, and password. The same details are exposed to your apps via STRONGLY_SERVICES, where the connection string uses the Bolt protocol:
bolt://neo4j:password@host:7687
Neo4j add-ons always use neo4j as the username (fixed by the Neo4j image). Only the password is auto-generated during add-on creation as a 32-character random secret.
Accessing Connection Details
In STRONGLY_SERVICES, add-ons are grouped by type under services.addons, and each entry is one provisioned instance:
{
"id": "addon-abc123defg",
"name": "knowledge-graph",
"type": "neo4j",
"category": "add-on",
"status": "running",
"version": "5.26",
"connection": {
"connection_string": "bolt://neo4j:<password>@<internal-host>:7687",
"uri": "bolt://neo4j:<password>@<internal-host>:7687",
"host": "<internal-host>",
"port": 7687
},
"auth": {
"method": "username_password",
"credentials": { "username": "neo4j", "password": "<password>" }
},
"limits": { "max_connections": 100, "storage_gb": 10 },
"metadata": { "cpu": "0.5", "memory": "1GB", "disk": "10GB", "backup_enabled": false }
}
- Python
- Node.js
- Go
import os
import json
from neo4j import GraphDatabase
# Parse STRONGLY_SERVICES
services = json.loads(os.environ['STRONGLY_SERVICES'])
# Pick your Neo4j add-on by name (the label you gave it)
neo4j_addon = next(
a for a in services['services']['addons']['neo4j']
if a['name'] == 'knowledge-graph'
)
# Connect using host, port, and credentials
driver = GraphDatabase.driver(
f"bolt://{neo4j_addon['connection']['host']}:{neo4j_addon['connection']['port']}",
auth=(
neo4j_addon['auth']['credentials']['username'],
neo4j_addon['auth']['credentials']['password']
)
)
# Run a query
with driver.session() as session:
result = session.run(
"MATCH (p:Person) WHERE p.name = $name RETURN p",
name="Alice"
)
for record in result:
print(record["p"])
driver.close()
const neo4j = require('neo4j-driver');
// Parse STRONGLY_SERVICES
const services = JSON.parse(process.env.STRONGLY_SERVICES);
const neo4jAddon = services.services.addons.neo4j
.find(a => a.name === 'knowledge-graph');
// Connect
const driver = neo4j.driver(
`bolt://${neo4jAddon.connection.host}:${neo4jAddon.connection.port}`,
neo4j.auth.basic(
neo4jAddon.auth.credentials.username,
neo4jAddon.auth.credentials.password
)
);
// Run a query
const session = driver.session();
try {
const result = await session.run(
'MATCH (p:Person) WHERE p.name = $name RETURN p',
{ name: 'Alice' }
);
result.records.forEach(record => {
console.log(record.get('p'));
});
} finally {
await session.close();
}
await driver.close();
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
type Connection struct {
Host string `json:"host"`
Port int `json:"port"`
}
type Auth struct {
Credentials struct {
Username string `json:"username"`
Password string `json:"password"`
} `json:"credentials"`
}
type Addon struct {
Name string `json:"name"`
Connection Connection `json:"connection"`
Auth Auth `json:"auth"`
}
type Services struct {
Services struct {
Addons map[string][]Addon `json:"addons"`
} `json:"services"`
}
func main() {
var services Services
json.Unmarshal([]byte(os.Getenv("STRONGLY_SERVICES")), &services)
neo4jAddon := services.Services.Addons["neo4j"][0]
ctx := context.Background()
// Connect
driver, err := neo4j.NewDriverWithContext(
fmt.Sprintf("bolt://%s:%d", neo4jAddon.Connection.Host, neo4jAddon.Connection.Port),
neo4j.BasicAuth(neo4jAddon.Auth.Credentials.Username, neo4jAddon.Auth.Credentials.Password, ""),
)
if err != nil {
panic(err)
}
defer driver.Close(ctx)
// Run a query
session := driver.NewSession(ctx, neo4j.SessionConfig{})
defer session.Close(ctx)
result, err := session.Run(ctx,
"MATCH (p:Person) WHERE p.name = $name RETURN p",
map[string]interface{}{"name": "Alice"},
)
if err != nil {
panic(err)
}
for result.Next(ctx) {
fmt.Println(result.Record().Values[0])
}
}
Cypher Query Language
Cypher is Neo4j's declarative query language for working with graph data.
Basic Syntax
// Create nodes
CREATE (p:Person {name: 'Alice', age: 30})
CREATE (c:Company {name: 'Acme Corp'})
// Create relationship
MATCH (p:Person {name: 'Alice'})
MATCH (c:Company {name: 'Acme Corp'})
CREATE (p)-[:WORKS_FOR {since: 2020}]->(c)
// Or create everything at once
CREATE (p:Person {name: 'Bob', age: 25})-[:WORKS_FOR {since: 2021}]->(c:Company {name: 'Tech Inc'})
// Match pattern
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN p.name, c.name
// Match with where clause
MATCH (p:Person)
WHERE p.age > 25
RETURN p.name, p.age
// Or inline where
MATCH (p:Person {age: 30})
RETURN p
Common Operations
Creating Nodes and Relationships
// Create multiple nodes
CREATE (alice:Person {name: 'Alice', email: 'alice@example.com'})
CREATE (bob:Person {name: 'Bob', email: 'bob@example.com'})
CREATE (python:Skill {name: 'Python', category: 'Programming'})
CREATE (ml:Skill {name: 'Machine Learning', category: 'AI'})
// Create relationships
MATCH (p:Person {name: 'Alice'})
MATCH (s:Skill {name: 'Python'})
CREATE (p)-[:HAS_SKILL {level: 'Expert', years: 5}]->(s)
// Create if not exists (MERGE)
MERGE (p:Person {email: 'charlie@example.com'})
ON CREATE SET p.name = 'Charlie', p.created = timestamp()
ON MATCH SET p.lastSeen = timestamp()
Querying Patterns
// Find direct relationships
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN p.name AS employee, c.name AS company
// Find paths with multiple hops
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)-[:LOCATED_IN]->(city:City)
RETURN p.name, c.name, city.name
// Variable length paths
MATCH (p:Person)-[:KNOWS*1..3]->(friend:Person)
WHERE p.name = 'Alice'
RETURN DISTINCT friend.name
// Shortest path
MATCH path = shortestPath(
(alice:Person {name: 'Alice'})-[:KNOWS*]-(bob:Person {name: 'Bob'})
)
RETURN path
// All paths
MATCH path = (alice:Person {name: 'Alice'})-[:KNOWS*..4]-(bob:Person {name: 'Bob'})
RETURN path
Filtering and Conditions
// WHERE clause
MATCH (p:Person)
WHERE p.age >= 25 AND p.age <= 40
RETURN p.name, p.age
// String matching
MATCH (p:Person)
WHERE p.name STARTS WITH 'A'
RETURN p.name
MATCH (p:Person)
WHERE p.email CONTAINS '@example.com'
RETURN p.name, p.email
// Pattern matching in WHERE
MATCH (p:Person)
WHERE (p)-[:WORKS_FOR]->(:Company {name: 'Acme Corp'})
RETURN p.name
// NOT pattern
MATCH (p:Person)
WHERE NOT (p)-[:WORKS_FOR]->(:Company)
RETURN p.name AS freelancers
// IN operator
MATCH (p:Person)
WHERE p.name IN ['Alice', 'Bob', 'Charlie']
RETURN p
Updating Data
// Update properties
MATCH (p:Person {name: 'Alice'})
SET p.age = 31, p.lastUpdated = timestamp()
// Add label
MATCH (p:Person {name: 'Alice'})
SET p:Manager
// Remove property
MATCH (p:Person {name: 'Alice'})
REMOVE p.temporaryFlag
// Remove label
MATCH (p:Person {name: 'Alice'})
REMOVE p:Manager
Deleting Data
// Delete node (must delete relationships first)
MATCH (p:Person {name: 'Alice'})-[r]-()
DELETE r, p
// Or use DETACH DELETE
MATCH (p:Person {name: 'Alice'})
DETACH DELETE p
// Delete relationship
MATCH (p:Person {name: 'Alice'})-[r:WORKS_FOR]->()
DELETE r
// Delete all (careful!)
MATCH (n)
DETACH DELETE n
Aggregation
// Count
MATCH (p:Person)
RETURN count(p) AS totalPeople
// Group by and aggregate
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN c.name, count(p) AS employeeCount
ORDER BY employeeCount DESC
// Multiple aggregations
MATCH (p:Person)
RETURN
count(p) AS total,
avg(p.age) AS averageAge,
min(p.age) AS youngest,
max(p.age) AS oldest
// Collect into list
MATCH (c:Company)<-[:WORKS_FOR]-(p:Person)
RETURN c.name, collect(p.name) AS employees
// DISTINCT
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN count(DISTINCT c) AS numberOfCompanies
Ordering and Limiting
// Order by
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
// Multiple order fields
MATCH (p:Person)
RETURN p
ORDER BY p.age DESC, p.name ASC
// Limit
MATCH (p:Person)
RETURN p.name
ORDER BY p.age DESC
LIMIT 10
// Skip and limit (pagination)
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
SKIP 20
LIMIT 10
Indexes and Constraints
// Create index
CREATE INDEX person_email FOR (p:Person) ON (p.email)
// Create composite index
CREATE INDEX person_name_age FOR (p:Person) ON (p.name, p.age)
// Create full-text index
CREATE FULLTEXT INDEX person_search FOR (p:Person) ON EACH [p.name, p.email]
// Use full-text index
CALL db.index.fulltext.queryNodes('person_search', 'alice*')
YIELD node, score
RETURN node.name, score
// Create unique constraint
CREATE CONSTRAINT person_email_unique FOR (p:Person) REQUIRE p.email IS UNIQUE
// Create existence constraint (Enterprise Edition)
CREATE CONSTRAINT person_name_exists FOR (p:Person) REQUIRE p.name IS NOT NULL
// List indexes
SHOW INDEXES
// List constraints
SHOW CONSTRAINTS
// Drop index
DROP INDEX person_email
// Drop constraint
DROP CONSTRAINT person_email_unique
APOC Procedures
APOC (Awesome Procedures On Cypher) provides additional utility functions.
// Date formatting
RETURN apoc.date.format(timestamp(), 'ms', 'yyyy-MM-dd HH:mm:ss') AS formattedDate
// Generate UUID
CREATE (p:Person {id: apoc.create.uuid(), name: 'Alice'})
// JSON operations
MATCH (p:Person {name: 'Alice'})
RETURN apoc.convert.toJson(p) AS personJson
// Load JSON from URL
CALL apoc.load.json('https://api.example.com/data')
YIELD value
RETURN value
// Periodic commit (batch processing)
CALL apoc.periodic.iterate(
"MATCH (p:Person) RETURN p",
"SET p.processed = true",
{batchSize: 1000}
)
// Run Cypher from file
CALL apoc.cypher.runFile('import.cypher')
// Conditional execution
CALL apoc.when(
person.age >= 18,
"SET person:Adult RETURN person",
"SET person:Minor RETURN person",
{person: person}
)
Graph Algorithms
Common graph algorithms for analysis.
// PageRank (requires Graph Data Science library)
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
// Shortest path
MATCH (start:Person {name: 'Alice'}), (end:Person {name: 'Bob'})
CALL gds.shortestPath.dijkstra.stream('myGraph', {
sourceNode: start,
targetNode: end
})
YIELD path
RETURN path
// Community detection (Louvain)
CALL gds.louvain.stream('myGraph')
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
// Centrality measures
CALL gds.degree.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
Use Cases
Social Network
// Create social network
CREATE (alice:User {name: 'Alice', joined: date('2020-01-01')})
CREATE (bob:User {name: 'Bob', joined: date('2020-02-15')})
CREATE (charlie:User {name: 'Charlie', joined: date('2020-03-20')})
CREATE (alice)-[:FOLLOWS {since: date('2020-02-01')}]->(bob)
CREATE (bob)-[:FOLLOWS {since: date('2020-03-01')}]->(charlie)
CREATE (charlie)-[:FOLLOWS {since: date('2020-04-01')}]->(alice)
// Find mutual follows (friends)
MATCH (u1:User)-[:FOLLOWS]->(u2:User)-[:FOLLOWS]->(u1)
RETURN u1.name, u2.name
// Friend recommendations (friends of friends)
MATCH (user:User {name: 'Alice'})-[:FOLLOWS]->()-[:FOLLOWS]->(recommended:User)
WHERE NOT (user)-[:FOLLOWS]->(recommended) AND user <> recommended
RETURN recommended.name, count(*) AS mutualFriends
ORDER BY mutualFriends DESC
Recommendation Engine
// Product recommendations based on similar users
MATCH (user:User {name: 'Alice'})-[:PURCHASED]->(product:Product)
<-[:PURCHASED]-(other:User)-[:PURCHASED]->(recommendation:Product)
WHERE NOT (user)-[:PURCHASED]->(recommendation)
RETURN recommendation.name, count(*) AS score
ORDER BY score DESC
LIMIT 5
// Collaborative filtering
MATCH (user:User {name: 'Alice'})-[r1:RATED]->(product:Product)
<-[r2:RATED]-(other:User)
WHERE abs(r1.rating - r2.rating) < 2
WITH other, count(*) AS similarity
ORDER BY similarity DESC
LIMIT 10
MATCH (other)-[r:RATED]->(recommendation:Product)
WHERE NOT (user)-[:RATED]->(recommendation) AND r.rating >= 4
RETURN recommendation.name, avg(r.rating) AS avgRating, count(*) AS count
ORDER BY avgRating DESC, count DESC
Knowledge Graph
// Create knowledge graph
CREATE (python:Technology {name: 'Python', type: 'Language'})
CREATE (django:Technology {name: 'Django', type: 'Framework'})
CREATE (web:Domain {name: 'Web Development'})
CREATE (django)-[:BUILT_WITH]->(python)
CREATE (django)-[:USED_FOR]->(web)
// Find all technologies for a domain
MATCH (tech:Technology)-[:USED_FOR]->(domain:Domain {name: 'Web Development'})
RETURN tech.name, tech.type
// Find technology stack (dependencies)
MATCH path = (tech:Technology {name: 'Django'})-[:BUILT_WITH*]->(dependency)
RETURN path
Backups
A Neo4j backup is a Cypher export of the whole graph, nodes, relationships and properties (backup.cypher), plus the indexes and constraints under their own names (schema.cypher).
-
Back up now: click Backup Now on the status card, or Back Up Now on the Backup tab, while the add-on is running.
-
Automatic: on the Backup tab turn on Enable Automatic Backups, choose a Backup Schedule (Hourly, Daily, Weekly or Monthly) and a Retention (3, 7, 14 or 30 backups), and click Save Configuration. Older backups beyond the retention count are deleted automatically.
-
History: the Backup tab lists every backup with its status, size and any error.
-
Restore: click Restore next to a succeeded backup in Backup History and confirm. The backup is loaded back into this add-on while it keeps running: every node, relationship, index and constraint is deleted, then the backup is loaded. Queries find missing data until the restore finishes. Data written after the backup is lost. See Restoring a backup.
Performance Optimization
Query Optimization
// Use PROFILE to analyze query execution
PROFILE
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WHERE p.age > 25
RETURN p.name, c.name
// Use EXPLAIN to see execution plan
EXPLAIN
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WHERE p.age > 25
RETURN p.name, c.name
// Use indexes for better performance
CREATE INDEX person_age FOR (p:Person) ON (p.age)
// Limit early in the query
MATCH (p:Person)
WHERE p.age > 25
WITH p
ORDER BY p.age DESC
LIMIT 100
MATCH (p)-[:WORKS_FOR]->(c:Company)
RETURN p.name, c.name
Data Modeling Best Practices
- Model for Queries: Design graph based on how you'll query it
- Use Specific Relationship Types: More specific is better than generic
- Denormalize When Needed: Duplicate data for query performance
- Index Wisely: Index properties used in WHERE clauses
- Avoid Super Nodes: Nodes with millions of relationships slow down queries
Monitoring
The Metrics tab on the add-on details page measures the running add-on live: CPU, memory and disk use against its size, network traffic, open and new connections, response time, and instance health and uptime. See Metrics. The Logs tab shows its recent log output.
Database Statistics
// Database info
CALL dbms.queryJmx('org.neo4j:instance=kernel#0,name=Store sizes')
YIELD attributes
RETURN attributes
// Count nodes by label
MATCH (n:Person)
RETURN count(n) AS personCount
// Count relationships by type
MATCH ()-[r:WORKS_FOR]->()
RETURN count(r) AS worksForCount
// Database constraints and indexes
SHOW CONSTRAINTS
SHOW INDEXES
Best Practices
- Use Indexes: Index properties used frequently in lookups
- Specific Relationship Types: Use descriptive relationship names
- Limit Result Sets: Use LIMIT to prevent large result sets
- Profile Queries: Use PROFILE/EXPLAIN for optimization
- Batch Operations: Use APOC for large data imports
- Avoid Cartesian Products: Use proper MATCH patterns
- Use Parameters: Parameterize queries for security and performance
- Model Carefully: Design schema for your access patterns
- Monitor Performance: Track slow queries
- Regular Backups: Enable daily backups for production
Troubleshooting
Connection Issues
# Test connection
from neo4j import GraphDatabase
driver = GraphDatabase.driver(uri, auth=(username, password))
try:
driver.verify_connectivity()
print("Connected successfully")
except Exception as e:
print(f"Connection failed: {e}")
finally:
driver.close()
Performance Issues
// Find long-running queries
CALL dbms.listQueries()
YIELD queryId, query, elapsedTimeMillis
WHERE elapsedTimeMillis > 1000
RETURN queryId, query, elapsedTimeMillis
ORDER BY elapsedTimeMillis DESC
// Kill long-running query
CALL dbms.killQuery('query-id')
// Check memory usage
CALL dbms.queryJmx('org.neo4j:instance=kernel#0,name=Memory Pools')
YIELD attributes
RETURN attributes
Support
For issues or questions:
- Check add-on logs in the Logs tab of the add-on details page
- Review Neo4j official documentation
- Contact Strongly support through the platform