Loading learning content...
When the developers of Neo4j set out to build a database optimized for connected data in 2000, they were solving problems that the broader industry wouldn't recognize for another decade. Today, Neo4j powers fraud detection at financial institutions, recommendation engines at e-commerce giants, and knowledge graphs at research organizations worldwide.
Neo4j isn't just the most popular graph database—it defined the property graph model now adopted across the industry. Understanding Neo4j means understanding the patterns, practices, and query language that have become the de facto standard for graph database work.
By the end of this page, you will understand Neo4j's architecture, master the Cypher query language from fundamentals to advanced patterns, learn how to perform CRUD operations, implement indexes and constraints, and understand deployment options including AuraDB (cloud), Desktop, and Enterprise configurations.
Neo4j is a native graph database—designed from the ground up for graph storage and processing, not a graph layer atop relational or document storage. This native approach provides the performance characteristics that distinguish Neo4j from non-native alternatives.
Core Architectural Components:
NEO4J ARCHITECTURE OVERVIEW============================ ┌─────────────────────────────────────────────────────────────────────┐│ CLIENT LAYER │├─────────────────────────────────────────────────────────────────────┤│ • Bolt Protocol (binary, TLS-encrypted) ││ • HTTP API (REST, for legacy compatibility) ││ • Official Drivers: Java, Python, JavaScript, .NET, Go ││ • Neo4j Browser (web-based query interface) ││ • Neo4j Bloom (visual exploration tool) │└────────────────────────────────┬────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ QUERY ENGINE │├─────────────────────────────────────────────────────────────────────┤│ • Cypher Parser → AST → Logical Plan → Physical Plan ││ • Cost-based Query Optimizer ││ • Query Cache (compiled query plans) ││ • Parallel Execution Engine (Enterprise) │└────────────────────────────────┬────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ TRANSACTION LAYER │├─────────────────────────────────────────────────────────────────────┤│ • ACID Transactions (full durability) ││ • MVCC (Multi-Version Concurrency Control) ││ • Transaction Log (write-ahead log) ││ • Lock Manager (node/relationship level) ││ • Deadlock Detection │└────────────────────────────────┬────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ STORAGE ENGINE │├─────────────────────────────────────────────────────────────────────┤│ Native Graph Storage: ││ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ││ │ Node Store │ │ Relationship │ │ Property │ ││ │ (15B/node) │ │ Store │ │ Store │ ││ │ │ │ (34B/rel) │ │ (variable) │ ││ └──────────────┘ └──────────────┘ └──────────────┘ ││ ││ Index Infrastructure: ││ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ││ │ Range Index │ │ Full-Text │ │ Spatial │ ││ │ (B+ Tree) │ │ (Lucene) │ │ (R-Tree) │ ││ └──────────────┘ └──────────────┘ └──────────────┘ ││ ││ Page Cache (memory-mapped I/O, configurable heap) │└─────────────────────────────────────────────────────────────────────┘ CLUSTER ARCHITECTURE (Enterprise):┌───────────────────────────────────────────────────────────────────┐│ RAFT CLUSTER │├───────────────────────────────────────────────────────────────────┤│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││ │ LEADER │◄──►│ FOLLOWER │◄──►│ FOLLOWER │ ││ │ (writes) │ │ (replicas) │ │ (replicas) │ ││ └─────────────┘ └─────────────┘ └─────────────┘ ││ ▲ ││ │ Automatic Leader Election ││ │ Synchronous Replication (configurable) ││ ││ READ REPLICAS (async): ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Read │ │ Read │ │ Read │ │ Read │ ││ │ Replica │ │ Replica │ │ Replica │ │ Replica │ ││ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │└───────────────────────────────────────────────────────────────────┘Neo4j performance depends heavily on having enough memory for the page cache to hold the working set. Optimal configuration: page cache should hold the entire graph store files (node, relationship, property, and index files). For a 50GB graph, configure at least 50GB page cache if possible. Monitor cache hit rates—below 95% typically indicates undersized memory.
Cypher is Neo4j's declarative query language, designed to be expressive and intuitive for working with graph patterns. Its ASCII-art syntax makes pattern matching readable:
(node)-[:RELATIONSHIP]->(anotherNode)
Cypher has been open-sourced as openCypher and adopted by other graph databases (Amazon Neptune, SAP HANA Graph, Redis Graph), making it the SQL of the graph world.
Core Clauses:
| Clause | Purpose | SQL Equivalent |
|---|---|---|
| MATCH | Pattern matching to find data | SELECT ... FROM ... WHERE |
| WHERE | Filter matched patterns | WHERE clause |
| RETURN | Specify output data | SELECT columns |
| CREATE | Create nodes and relationships | INSERT |
| MERGE | Match or create (upsert) | INSERT ... ON CONFLICT |
| SET | Update properties | UPDATE |
| DELETE | Remove nodes/relationships | DELETE |
| WITH | Chain query parts (subquery) | CTE / subquery |
| ORDER BY | Sort results | ORDER BY |
| LIMIT/SKIP | Pagination | LIMIT/OFFSET |
| UNWIND | Expand lists to rows | LATERAL / UNNEST |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
// PATTERN SYNTAX EXPLAINED// ========================= // Node pattern: (variable:Label {properties})(p:Person {name: "Alice"}) // Node labeled Person with name Alice(p:Person:Employee) // Node with multiple labels(p) // Any node(:Person) // Any Person (no variable binding) // Relationship pattern: -[variable:TYPE {properties}]->-[:KNOWS]-> // Outgoing KNOWS relationship<-[:MANAGES]- // Incoming MANAGES relationship-[:FRIEND]- // Either direction-[r:WORKS_AT {since: 2020}]-> // Relationship bound to 'r' with property // Combined patterns:(alice:Person)-[:KNOWS]->(bob:Person)(a)-[:MANAGES]->(b)<-[:REPORTS_TO]-(c) // Variable-length paths:(a)-[:KNOWS*1..3]->(b) // 1 to 3 hops(a)-[:KNOWS*]->(b) // Any number of hops(a)-[:KNOWS*..5]->(b) // Up to 5 hops // BASIC QUERIES// ============= // 1. Find all Person nodesMATCH (p:Person)RETURN p.name, p.email // 2. Find specific personMATCH (p:Person {name: "Alice"})RETURN p // 3. Find Alice's friendsMATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend:Person)RETURN friend.name // 4. Find friends of friends (excluding direct friends)MATCH (me:Person {name: "Alice"})-[:KNOWS*2..2]->(foaf:Person)WHERE NOT (me)-[:KNOWS]->(foaf) AND me <> foafRETURN DISTINCT foaf.name // 5. Find shortest path between two peopleMATCH path = shortestPath( (alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"}))RETURN path, length(path) AS hops // 6. Pattern with relationship propertiesMATCH (e:Employee)-[w:WORKS_FOR {since: 2020}]->(c:Company)WHERE w.role = "Engineer"RETURN e.name, c.name, w.roleIn 2024, ISO ratified GQL (Graph Query Language) as the international standard for graph database queries. GQL is heavily influenced by Cypher, and Neo4j plans full GQL support. For now, Cypher remains the production-ready language, and GQL will be largely syntactically compatible. Skills in Cypher transfer directly to GQL.
Let's examine the full lifecycle of data manipulation in Neo4j, from creation through deletion, with attention to best practices and common pitfalls.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
// ========================================// CREATE - Adding Data// ======================================== // Create a single nodeCREATE (alice:Person:Employee { personId: "PER-001", name: "Alice Chen", email: "alice@techcorp.com", age: 32, skills: ["Python", "Go", "Machine Learning"], joinedAt: datetime("2019-03-15T09:00:00")})RETURN alice // Create multiple nodes in one queryCREATE (bob:Person:Employee {personId: "PER-002", name: "Bob Singh"})CREATE (carol:Person:Manager {personId: "PER-003", name: "Carol Davis"})CREATE (techcorp:Company {companyId: "COM-001", name: "TechCorp Inc."}) // Create nodes and relationships togetherCREATE (alice:Person {name: "Alice"})-[:KNOWS {since: date()}]->(bob:Person {name: "Bob"}) // Create relationships between existing nodesMATCH (a:Person {name: "Alice"}), (c:Company {name: "TechCorp Inc."})CREATE (a)-[:WORKS_FOR {since: date("2019-03-15"), role: "Senior Engineer"}]->(c) // ========================================// MERGE - Create If Not Exists (Upsert)// ======================================== // MERGE checks for existence before creating// Critical for idempotent data loading // Merge on unique property (recommended)MERGE (p:Person {personId: "PER-001"})ON CREATE SET p.name = "Alice Chen", p.createdAt = datetime()ON MATCH SET p.lastSeen = datetime()RETURN p // Merge with full pattern (creates both nodes and relationship)MERGE (a:Person {personId: "PER-001"})-[:WORKS_FOR]->(c:Company {companyId: "COM-001"}) // CAUTION: Merge relationship between existing nodes// DO THIS (correct - match nodes first):MATCH (a:Person {personId: "PER-001"})MATCH (c:Company {companyId: "COM-001"})MERGE (a)-[:WORKS_FOR]->(c) // NOT THIS (may create duplicate nodes):MERGE (a:Person {name: "Alice"})-[:WORKS_FOR]->(c:Company {name: "TechCorp"}) // ========================================// SET - Updating Data// ======================================== // Update single propertyMATCH (p:Person {personId: "PER-001"})SET p.email = "alice.chen@techcorp.com"RETURN p // Update multiple propertiesMATCH (p:Person {personId: "PER-001"})SET p.age = 33, p.lastModified = datetime()RETURN p // Replace all properties (destructive!)MATCH (p:Person {personId: "PER-001"})SET p = {personId: "PER-001", name: "Alice Chen", age: 33}RETURN p // Add to existing properties (non-destructive, recommended)MATCH (p:Person {personId: "PER-001"})SET p += {phone: "+1-555-0123", verified: true}RETURN p // Add/remove labelsMATCH (p:Person {personId: "PER-001"})SET p:Manager, p:TechLeadREMOVE p:EmployeeRETURN labels(p) // ========================================// REMOVE - Removing Properties/Labels// ======================================== // Remove a propertyMATCH (p:Person {personId: "PER-001"})REMOVE p.temporaryFieldRETURN p // Remove a labelMATCH (p:Person:Manager {personId: "PER-001"})REMOVE p:ManagerRETURN labels(p) // ========================================// DELETE - Removing Nodes/Relationships// ======================================== // Delete a relationshipMATCH (a:Person {personId: "PER-001"})-[r:WORKS_FOR]->(c:Company)DELETE r // Delete a node (must have no relationships)MATCH (p:Person {personId: "TEMP-001"})DELETE p // Delete node and ALL connected relationships (DETACH DELETE)MATCH (p:Person {personId: "TEMP-001"})DETACH DELETE p // Delete multiple nodes by conditionMATCH (p:Person)WHERE p.isInactive = true AND p.lastSeen < date() - duration('P1Y')DETACH DELETE p // ========================================// RETURNING RESULTS// ======================================== // Return specific propertiesMATCH (p:Person)-[r:WORKS_FOR]->(c:Company)RETURN p.name AS employee, c.name AS company, r.role AS position // Return with computed fieldsMATCH (p:Person)RETURN p.name, p.age, CASE WHEN p.age >= 65 THEN "Senior" ELSE "Active" END AS status // Aggregate resultsMATCH (p:Person)-[:WORKS_FOR]->(c:Company)RETURN c.name AS company, count(p) AS employeeCountORDER BY employeeCount DESC // Return graph structureMATCH path = (a:Person)-[:KNOWS*1..3]-(b:Person)RETURN pathCommon Mistake: MERGE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'}) creates new Alice and Bob nodes if ANY part of the pattern doesn't exist. Correct Approach: MATCH existing nodes first, then MERGE the relationship. Only MERGE with complete patterns during initial data load with indexed unique properties.
Beyond basic CRUD, Cypher provides sophisticated capabilities for complex graph analytics, path operations, and data transformation.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
// ========================================// AGGREGATION AND GROUPING// ======================================== // Basic aggregationsMATCH (p:Person)-[:WORKS_FOR]->(c:Company)RETURN c.name AS company, count(p) AS employees, avg(p.salary) AS avgSalary, min(p.joinedAt) AS firstHire, max(p.salary) AS highestSalary, collect(p.name) AS employeeNames // Count distinct valuesMATCH (p:Person)-[:PURCHASED]->(product:Product)RETURN p.name, count(DISTINCT product) AS uniqueProducts // Conditional aggregationMATCH (p:Person)RETURN count(CASE WHEN p.age < 30 THEN 1 END) AS under30, count(CASE WHEN p.age >= 30 AND p.age < 50 THEN 1 END) AS between30and50, count(CASE WHEN p.age >= 50 THEN 1 END) AS over50 // ========================================// WITH CLAUSE - Query Chaining// ======================================== // Pipeline query stagesMATCH (p:Person)-[:WORKS_FOR]->(c:Company {name: "TechCorp"})WITH p, cWHERE p.age > 30WITH p ORDER BY p.salary DESC LIMIT 10MATCH (p)-[:KNOWS]->(colleague:Person)RETURN p.name, collect(colleague.name) AS topColleagues // Subquery-like behaviorMATCH (dept:Department)CALL { WITH dept MATCH (dept)<-[:BELONGS_TO]-(e:Employee) RETURN count(e) AS empCount}RETURN dept.name, empCountORDER BY empCount DESC // ========================================// PATH OPERATIONS// ======================================== // All shortest paths between nodesMATCH paths = allShortestPaths( (alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"}))RETURN paths, length(paths) AS hops // Variable-length path with filtersMATCH path = (start:Person {name: "Alice"})-[:KNOWS*1..6]-(end:Person)WHERE ALL(node IN nodes(path) WHERE node.age > 25) AND ALL(rel IN relationships(path) WHERE rel.since > date("2020-01-01"))RETURN end.name, length(path) AS distance // Path operationsMATCH path = (a:Person)-[:KNOWS*2..4]->(b:Person)RETURN nodes(path) AS pathNodes, relationships(path) AS pathRels, length(path) AS hops, [n IN nodes(path) | n.name] AS names // ========================================// LIST OPERATIONS// ======================================== // List comprehensionMATCH (p:Person)RETURN p.name, [skill IN p.skills WHERE skill CONTAINS "Python" | skill] AS pythonSkills // UNWIND - expand lists to rowsWITH ["Alice", "Bob", "Carol"] AS namesUNWIND names AS nameMERGE (p:Person {name: name})RETURN p // Pattern from listMATCH (p:Product)WHERE p.productId IN ["PROD-001", "PROD-002", "PROD-003"]RETURN p.name // Collect and processMATCH (c:Customer)-[:PURCHASED]->(p:Product)WITH c, collect(p) AS productsWHERE size(products) >= 3RETURN c.name, [prod IN products | prod.name] AS purchasedProducts // ========================================// GRAPH DATA SCIENCE PROCEDURES// ======================================== // PageRank (requires GDS library)CALL gds.pageRank.stream('myGraph')YIELD nodeId, scoreRETURN gds.util.asNode(nodeId).name AS name, scoreORDER BY score DESCLIMIT 10 // Community detection (Louvain)CALL gds.louvain.stream('socialGraph')YIELD nodeId, communityIdRETURN communityId, collect(gds.util.asNode(nodeId).name) AS membersORDER BY size(members) DESC // Shortest path (Dijkstra)MATCH (start:City {name: "New York"}), (end:City {name: "Los Angeles"})CALL gds.shortestPath.dijkstra.stream('roadNetwork', { sourceNode: start, targetNode: end, relationshipWeightProperty: 'distance'})YIELD totalCost, nodeIds, costsRETURN totalCost, [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path // ========================================// APOC UTILITIES (common procedures)// ======================================== // Batch processingCALL apoc.periodic.iterate( "MATCH (p:Person) WHERE p.needsUpdate = true RETURN p", "SET p.processed = true, p.processedAt = datetime()", {batchSize: 1000, parallel: true}) // Dynamic relationship creationMATCH (a:Person {name: "Alice"}), (b:Person {name: "Bob"})CALL apoc.create.relationship(a, "CUSTOM_REL", {weight: 0.5}, b)YIELD relRETURN rel // Load JSON dataCALL apoc.load.json("https://api.example.com/users.json")YIELD valueMERGE (p:Person {userId: value.id})SET p.name = value.name, p.email = value.email1. Use PROFILE/EXPLAIN to understand query plans. 2. Anchor patterns on indexed properties to avoid full scans. 3. Filter early with WHERE after the first MATCH when possible. 4. Limit variable-length paths (*1..5 not just *). 5. Use APOC for batch operations instead of thousands of individual queries.
Proper indexing is critical for Neo4j performance. Without indexes, MATCH clauses with property filters require full label scans—acceptable for development but prohibitive at scale.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
// ========================================// INDEX CREATION (Neo4j 5.x syntax)// ======================================== // Range index (default, most common)CREATE INDEX person_email FOR (p:Person) ON (p.email) // Named index with optionsCREATE INDEX person_id_idx IF NOT EXISTS FOR (p:Person) ON (p.personId)OPTIONS { indexConfig: { `spatial.wgs-84.min`: [-180.0, -90.0], `spatial.wgs-84.max`: [180.0, 90.0] }} // Composite index (multiple properties)CREATE INDEX product_category_status FOR (p:Product) ON (p.category, p.status) // Text index (for text search operations)CREATE TEXT INDEX person_bio FOR (p:Person) ON (p.bio) // Full-text index (Lucene-backed)CREATE FULLTEXT INDEX product_search FOR (p:Product) ON EACH [p.name, p.description, p.tags]OPTIONS { indexConfig: { `fulltext.analyzer`: "english" }} // Point index (spatial queries)CREATE POINT INDEX location_coords FOR (l:Location) ON (l.coordinates) // Relationship property index (Neo4j 5.7+)CREATE INDEX rel_since FOR ()-[w:WORKS_FOR]-() ON (w.since) // ========================================// CONSTRAINT CREATION// ======================================== // Unique constraint (creates index automatically)CREATE CONSTRAINT person_email_unique IF NOT EXISTSFOR (p:Person) REQUIRE p.email IS UNIQUE // Node key constraint (composite uniqueness + existence)CREATE CONSTRAINT order_keyFOR (o:Order) REQUIRE (o.orderId, o.region) IS NODE KEY // Existence constraintCREATE CONSTRAINT person_name_requiredFOR (p:Person) REQUIRE p.name IS NOT NULL // Type constraint (Neo4j 5.9+)CREATE CONSTRAINT person_age_integerFOR (p:Person) REQUIRE p.age IS :: INTEGER // Relationship property existenceCREATE CONSTRAINT employment_since_requiredFOR ()-[w:WORKS_FOR]-() REQUIRE w.since IS NOT NULL // ========================================// INDEX MANAGEMENT// ======================================== // List all indexesSHOW INDEXES // List with detailsSHOW INDEXES WHERE type = 'RANGE'YIELD name, type, entityType, labelsOrTypes, properties, state // Drop indexDROP INDEX person_email // Drop constraint (removes associated index)DROP CONSTRAINT person_email_unique // Check index usage in queryPROFILE MATCH (p:Person {email: "alice@example.com"}) RETURN p // Example output shows "NodeIndexSeek" when index is used// vs "NodeByLabelScan" + "Filter" when not indexed // ========================================// PERFORMANCE DIAGNOSTICS// ======================================== // Explain query plan (without executing)EXPLAIN MATCH (p:Person)-[:WORKS_FOR]->(c:Company)WHERE p.email STARTS WITH "alice"RETURN p.name, c.name // Profile query (executes and shows db hits)PROFILE MATCH (p:Person)-[:WORKS_FOR]->(c:Company)WHERE p.email STARTS WITH "alice"RETURN p.name, c.name // Monitor index population statusSHOW INDEXES WHERE state <> 'ONLINE'| Query Pattern | Index Type | When to Use |
|---|---|---|
| Exact match, range queries | Range (default) | Most lookup patterns |
| Multi-property lookup | Composite | WHERE a.x = ? AND a.y = ? |
| Text CONTAINS/ENDS WITH | Text | Substring matching |
| Natural language search | Full-text | Search box functionality |
| Distance/bounding box | Point | Geo-spatial queries |
| Label existence check | Token lookup | Fast MATCH (n:Label) scans |
Every index adds write overhead—properties are indexed synchronously during commits. For write-heavy workloads, balance read performance against write latency. Create indexes based on actual query patterns, not theoretical completeness. Monitor index population status after creation (SHOW INDEXES WHERE state <> 'ONLINE').
Neo4j provides official drivers for major programming languages, all using the Bolt binary protocol for efficient, encrypted communication.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
# Neo4j Python Driver# pip install neo4j from neo4j import GraphDatabasefrom contextlib import contextmanager class Neo4jConnection: def __init__(self, uri: str, user: str, password: str): self._driver = GraphDatabase.driver(uri, auth=(user, password)) def close(self): self._driver.close() @contextmanager def session(self, database: str = "neo4j"): session = self._driver.session(database=database) try: yield session finally: session.close() # Transaction functions are recommended for production def create_person(self, person_id: str, name: str, email: str): def _create(tx, person_id, name, email): result = tx.run(""" MERGE (p:Person {personId: $personId}) ON CREATE SET p.name = $name, p.email = $email, p.createdAt = datetime() RETURN p """, personId=person_id, name=name, email=email) return result.single() with self.session() as session: return session.execute_write(_create, person_id, name, email) def find_connections(self, person_id: str, max_hops: int = 3): def _find(tx, person_id, max_hops): result = tx.run(""" MATCH path = (start:Person {personId: $personId}) -[:KNOWS*1..$maxHops]- (connected:Person) WHERE start <> connected RETURN DISTINCT connected.name AS name, length(path) AS distance ORDER BY distance """, personId=person_id, maxHops=max_hops) return [dict(record) for record in result] with self.session() as session: return session.execute_read(_find, person_id, max_hops) # Usageif __name__ == "__main__": conn = Neo4jConnection( uri="bolt://localhost:7687", user="neo4j", password="password" ) try: # Create nodes conn.create_person("P001", "Alice Chen", "alice@example.com") conn.create_person("P002", "Bob Singh", "bob@example.com") # Query connections connections = conn.find_connections("P001", max_hops=2) for c in connections: print(f"{c['name']} - {c['distance']} hops away") finally: conn.close()1. Use transaction functions (executeRead, executeWrite) for automatic retry on transient failures. 2. Share driver instance across your application—it's thread-safe and manages connection pooling. 3. Close sessions after use to return connections to the pool. 4. Use parameterized queries to prevent injection and enable query plan caching.
Neo4j offers deployment options ranging from local development to managed cloud services for enterprise production workloads.
| Option | Best For | Features | Considerations |
|---|---|---|---|
| Neo4j Desktop | Development, learning, prototyping | Free, local graphs, multiple databases, plugin management | Single machine, no production support |
| Community Edition | Small projects, single-server production | Open source, full Cypher, APOC support | Single instance, no clustering, 34B node limit |
| Enterprise Edition | Production workloads, high availability | Clustering, causal consistency, advanced security, online backup | Commercial license required |
| AuraDB Free | Learning, small prototypes | Managed, 200K nodes, automatic suspend | Limited resources, pauses after 3 days inactive |
| AuraDB Professional | Production SaaS applications | Managed, auto-scaling, backups, SLA | Pay per use, limited configuration |
| AuraDB Enterprise | Enterprise/mission-critical | Dedicated infrastructure, VPC, SSO, compliance | Premium pricing, custom contracts |
1234567891011121314151617181920212223242526272829303132333435363738394041424344
# Docker Compose for Neo4j Developmentversion: '3.8' services: neo4j: image: neo4j:5.15-community container_name: neo4j-dev ports: - "7474:7474" # HTTP (Browser) - "7687:7687" # Bolt (Driver) environment: # Authentication - NEO4J_AUTH=neo4j/development_password # Memory configuration - NEO4J_dbms_memory_pagecache_size=1G - NEO4J_dbms_memory_heap_initial__size=512M - NEO4J_dbms_memory_heap_max__size=1G # Enable APOC - NEO4J_PLUGINS=["apoc"] - NEO4J_dbms_security_procedures_unrestricted=apoc.* # Allow CSV import from URL - NEO4J_dbms_security_allow__csv__import__from__file__urls=true volumes: - neo4j_data:/data - neo4j_logs:/logs - neo4j_import:/var/lib/neo4j/import healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:7474"] interval: 30s timeout: 10s retries: 5 volumes: neo4j_data: neo4j_logs: neo4j_import: # Run: docker-compose up -d# Access: http://localhost:7474 (Neo4j Browser)Page Cache: Should ideally hold entire graph store (node + relationship + property files). Check file sizes in data/databases/<db>/. Heap: 2-8GB typically sufficient; larger for heavy queries or GDS algorithms. OS Reserve: Leave 1-2GB for operating system. Formula: Total RAM = Page Cache + Heap + OS Reserve
We've thoroughly explored Neo4j—from architecture through practical implementation. Let's consolidate the key insights:
What's next:
With Neo4j fundamentals mastered, we'll explore Graph Queries in depth—advanced Cypher patterns, graph algorithms, path analysis, and the analytical capabilities that make graph databases indispensable for connected data problems.
You now have practical, production-ready knowledge of Neo4j—its architecture, Cypher query language, CRUD operations, indexing strategies, driver integration, and deployment options. Next, we'll dive into advanced graph query techniques.