Neo4j 5.x CSV Import with Constraints: Setup, Verification, and Recovery
Load CSV data into Neo4j 5.x safely: create constraints and indexes, run a verified import, and recover from partial loads without risking production data.
16 Mar 2026, 01:14 UTC

The problem: partial CSV loads and silent schema gaps
A CSV import that fails halfway through leaves a graph database with orphaned nodes, missing relationships, and no reliable way to tell which rows actually landed. The useful takeaway: create a uniqueness constraint before you load, compare node and relationship counts against the source file, and keep a recovery path that restores from a pre-import copy rather than improvising deletions.
Desired outcome
By the end of this procedure you will have a Neo4j 5.x Community Edition database online, a uniqueness constraint on :Person(id), a range index on :Person(name), Person nodes and KNOWS relationships loaded from CSV files, and count queries that confirm the row totals.
Prerequisites and version assumptions
- Neo4j 5.x Community Edition installed as Server or Desktop. The commands below assume the 5.x configuration namespace; 4.x and earlier use different setting names and database defaults.
- A supported JDK for your installed edition and version. Check the Neo4j documentation for the exact Java requirement.
- Write access to the
data,logs, andimportdirectories. The import directory is configured byserver.directories.importinneo4j.conffor Neo4j 5.x; older releases useddbms.directories.import. - Ports 7474 (HTTP/Browser) and 7687 (Bolt) available on the host.
- A headered CSV file placed in the configured import directory. The examples use
persons.csvwith columnsid,nameandknows.csvwith columnsfrom_id,to_id. - Access to Neo4j Browser or Cypher Shell with the
neo4jadministrative user.
Procedure: create the database and schema
Start Neo4j and connect as neo4j. If this is a fresh install, change the default password on first login. Run all Cypher statements in Neo4j Browser or Cypher Shell.
If you do not want to use the default database, create a target database. Neo4j 5.x changed default database naming and multi-database behavior compared with 4.x, so confirm the target name with SHOW DATABASES before running data commands.
CREATE DATABASE import_target IF NOT EXISTS;
Switch your client session to import_target using the database selector in Neo4j Browser or by connecting Cypher Shell directly to that database. Then create the schema objects. The IF NOT EXISTS clauses make the statements safe to re-run.
CREATE CONSTRAINT person_id_unique IF NOT EXISTS
FOR (p:Person) REQUIRE p.id IS UNIQUE;
CREATE INDEX person_name_index IF NOT EXISTS
FOR (p:Person) ON (p.name);
Load the CSV data
LOAD CSV reads files from the import directory. The WITH HEADERS clause maps the first row to variable names. MERGE prevents duplicates when the uniqueness constraint is active, but it is slower than CREATE for guaranteed-new data. Replace import_target, persons.csv, and knows.csv with your actual database name and filenames.
LOAD CSV WITH HEADERS FROM 'file:///persons.csv' AS row
MERGE (p:Person {id: toInteger(row.id)})
SET p.name = row.name;
LOAD CSV WITH HEADERS FROM 'file:///knows.csv' AS row
MATCH (a:Person {id: toInteger(row.from_id)})
MATCH (b:Person {id: toInteger(row.to_id)})
MERGE (a)-[:KNOWS]->(b);
If your identifiers are strings rather than integers, remove the toInteger() wrapper. Required permissions: the neo4j process must be able to read the import directory, and the file must reside inside the configured import path.
Expected checks
After loading, verify the database status and schema objects.
SHOW DATABASES YIELD name, status WHERE name = 'import_target';
SHOW CONSTRAINTS YIELD name, labelsOrTypes, properties, entityType;
SHOW INDEXES YIELD name, labelsOrTypes, properties, type;
Then compare graph counts to the source rows.
MATCH (n:Person) RETURN count(*) AS persons;
MATCH ()-[r:KNOWS]->() RETURN count(*) AS relationships;
The counts should match the number of non-header rows in each CSV. If they do not, inspect logs/debug.log for encoding, permission, or parsing errors.
Recovery from a failed or partial import
If the load stopped halfway, the safest recovery is to restore the database from a copy taken before the import. Do not attempt to delete individual nodes unless you are certain no other data shares the database.
- Stop the Neo4j server.
- Replace the data directory for the affected database with the pre-import backup.
- Fix the CSV issue: check headers, encoding (UTF-8 without BOM), file permissions, and that the file is inside the configured import directory.
- Restart Neo4j and confirm the database is online with
SHOW DATABASES. - Re-run the load into a test database first if the source file changed significantly.
- Repeat the constrained load on the target database.
Warning: Never run MATCH (n) DETACH DELETE n on a production database. Use that pattern only in isolated development databases after backing up the data directory.
Limitations and verification notes
LOAD CSVprocesses the file in memory. Large files can exhaust heap or page cache. Test with a sample subset and monitor memory before loading production-sized data.MERGErelies on the uniqueness constraint being online before loading; otherwise duplicatePersonnodes can appear if the load is retried.- Neo4j 5.x uses
server.directories.importand multi-database commands that differ from 4.x. Confirm the exact setting names and command syntax against your installed version before applying this procedure. - This guide describes Community Edition behavior. Enterprise Edition features such as fine-grained role-based access control are not covered.
A practical final check: run the count queries immediately after import and again after a server restart. If the counts remain stable and match the CSV row totals, the import is consistent.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.