neo4j-contrib/neo4j-skills5 files

Neo4j Import Skill

Import structured data into Neo4j — LOAD CSV, CALL IN TRANSACTIONS, neo4j-admin

Specification
Skill ID
neo4j-contrib/neo4j-skills/neo4j-import-skill
Publisher
neo4j-contrib
Repository
neo4j-skills
Installs
0
Files
5
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

neo4j-contrib/neo4j-skills/neo4j-import-skillInstalls these files
  • README.md
  • SKILL.md
  • references/data-importer-gui.md
  • references/driver-batch-write.md
  • references/post-import-refactoring.md

What this skill tells the agent

Neo4j Import Skill

When to Use

  • Importing CSV, JSON, or Parquet files into Neo4j
  • Batch-upserting nodes and relationships (UNWIND + CALL IN TRANSACTIONS)
  • Migrating relational data (SQL → graph)
  • Bulk-loading large datasets offline (neo4j-admin import)
  • Choosing between online (Cypher) and offline (admin) import methods
  • Verifying import completeness (counts, constraints, index states)

When NOT to Use

  • Unstructured docs, PDFs, vector chunks → neo4j-document-import-skill
  • Live application writes (MERGE/CREATE in app code) → neo4j-cypher-skill
  • neo4j-admin backup/restore/config → neo4j-cli-tools-skill
  • GDS algorithm projection from existing graph → neo4j-gds-skill

Method Decision Table

Dataset sizeDB stateSourceMethod
Any sizeOnlineCSV (Aura or local)LOAD CSV + CALL IN TRANSACTIONS
< 1M rowsOnlineList/API responseUNWIND + CALL IN TRANSACTIONS
> 10M rowsOffline (local/self-managed)CSV / Parquetneo4j-admin database import full
Any sizeOnlineAPOC availableapoc.periodic.iterate + apoc.load.csv
Any sizeOnlineJSON/APIapoc.load.json or driver batching
Incremental deltaOffline (Enterprise)CSVneo4j-admin database import incremental

Aura: only https:// URLs — no file:///. Use neo4j-admin import only on self-managed.


Pre-Import Checklist

Run in this exact order — skipping causes hard-to-debug duplicates or missed index usage:

Constraints BEFORE import. Additional indexes AFTER import.

  • Constraints create implicit RANGE indexes used by MERGE during load + enforce uniqueness
  • Additional non-unique indexes (TEXT, RANGE on non-key props, FULLTEXT) created after load — Neo4j populates them async from the committed data; poll populationPercent until 100%
  • Creating extra indexes before import slows every write during load with no benefit
  1. Create uniqueness constraints (enables index used by MERGE): ``cypher CREATE CONSTRAINT IF NOT EXISTS FOR (n:Person) REQUIRE n.id IS UNIQUE; CREATE CONSTRAINT IF NOT EXISTS FOR (n:Movie) REQUIRE n.movieId IS UNIQUE; ` > **Neo4j 2026.06+ (Enterprise/Aura, GA):** ALTER CURRENT GRAPH TYPE SET { … } replaces all individual constraint statements with a single declarative block. See neo4j-cypher-skill/references/graph-type.md. Use individual CREATE CONSTRAINT` on Community Edition or pre-2026.02.
  1. *Verify APOC if using apoc. procedures**: ``cypher RETURN apoc.version(); `` If fails → APOC not installed. Use plain LOAD CSV instead.
  1. Confirm target is PRIMARY (not replica): ``cypher CALL dbms.cluster.role() YIELD role RETURN role; ` If role ≠ PRIMARY` → stop. Redirect write to PRIMARY endpoint.
  1. Count source file rows before import (catch encoding issues early): ``bash wc -l data/persons.csv # Linux/macOS ``
  1. Verify UTF-8 encoding — LOAD CSV requires UTF-8. Re-encode if needed: ``bash file -i persons.csv # Check encoding iconv -f latin1 -t utf-8 persons.csv > persons_utf8.csv ``

LOAD CSV Patterns

Basic node import with type coercion and null handling

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///persons.csv' AS row
CALL (row) {
  MERGE (p:Person {id: row.id})
  ON CREATE SET
    p.name       = row.name,
    p.age        = toIntegerOrNull(row.age),
    p.score      = toFloatOrNull(row.score),
    p.active     = toBoolean(row.active),
    p.born       = CASE WHEN row.born IS NOT NULL AND row.born <> '' THEN date(row.born) ELSE null END,
    p.createdAt  = datetime()
  ON MATCH SET
    p.updatedAt  = datetime()
} IN TRANSACTIONS OF 10000 ROWS
  ON ERROR CONTINUE
  REPORT STATUS AS s
RETURN s.transactionId, s.committed, s.errorMessage

Null/empty-string rules:

  • CSV missing column → null (safe)
  • CSV empty string "" → stored as "" not null — use nullIf(row.x, '') to convert
  • toInteger(null) throws → always use toIntegerOrNull()
  • toFloat(null) throws → always use toFloatOrNull()
  • Neo4j never stores null properties — they are silently dropped on SET

Relationship import (nodes must exist first)

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///knows.csv' AS row
CALL (row) {
  MATCH (a:Person {id: row.fromId})
  MATCH (b:Person {id: row.toId})
  MERGE (a)-[:KNOWS {since: toIntegerOrNull(row.year)}]->(b)
} IN TRANSACTIONS OF 5000 ROWS
  ON ERROR CONTINUE
  REPORT STATUS AS s

Always import ALL nodes before ANY relationships — MATCH fails on missing nodes.

Tab-separated or custom delimiter

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///data.tsv' AS row FIELDTERMINATOR '\t'
CALL (row) { MERGE (p:Person {id: row.id}) }
IN TRANSACTIONS OF 10000 ROWS ON ERROR CONTINUE

Compressed files (ZIP / gzip — local files only)

LOAD CSV WITH HEADERS FROM 'file:///archive.csv.gz' AS row ...

Cloud storage (Enterprise Edition)

SchemeExample
AWS S3s3://my-bucket/data/persons.csv
Google Cloud Storagegs://my-bucket/persons.csv
Azure Blobazb://account/container/persons.csv

Useful built-in functions inside LOAD CSV

linenumber()   // current line number — use as fallback ID
file()         // absolute path of file being loaded

CALL IN TRANSACTIONS — Full Reference

Syntax

CALL (row) {
  // write logic
} IN [n CONCURRENT] TRANSACTIONS
  [OF batchSize ROW[S]]
  [ON ERROR {CONTINUE | BREAK | FAIL | RETRY [FOR duration SECONDS] [THEN {CONTINUE|BREAK|FAIL}]}]
  [REPORT STATUS AS statusVar]

ON ERROR modes

ModeBehaviorUse when
ON ERROR FAILDefault. Rolls back entire outer tx on first errorAll-or-nothing strict import
ON ERROR CONTINUESkips failed batch, continues remaining batchesResilient bulk load — track errors via REPORT STATUS
ON ERROR BREAKStops after first failed batch; keeps completed workSemi-strict: stop early, keep successful batches
ON ERROR RETRYExponential backoff retry (default 30s) + fallbackConcurrent writes with deadlock risk

ON ERROR CONTINUE/BREAK → outer transaction succeeds even if inner batches fail. ON ERROR FAIL → cannot be combined with REPORT STATUS AS.

CONCURRENT TRANSACTIONS (parallel batches)

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///large.csv' AS row
CALL (row) {
  MERGE (p:Person {id: row.id}) SET p.name = row.name
} IN 4 CONCURRENT TRANSACTIONS OF 5000 ROWS
  ON ERROR RETRY FOR 30 SECONDS THEN CONTINUE
  REPORT STATUS AS s

Use CONCURRENT for read-heavy MERGE on non-overlapping key spaces. Risk: deadlocks on overlapping writes → combine with ON ERROR RETRY.

REPORT STATUS columns

ColumnTypeMeaning
s.startedBOOLEANBatch transaction started
s.committedBOOLEANBatch committed successfully
s.transactionIdSTRINGTransaction ID
s.errorMessageSTRING or nullError detail if batch failed

Batch size guidance

Row countRecommended batch sizeNotes
< 100k10 000Default is fine
100k – 1M10 000 – 50 000Monitor heap; increase if fast
1M – 10M50 000 – 100 000Enable CONCURRENT if CPUs available
> 10M online50 000Consider neo4j-admin import instead
Relationship import5 000Lower — each batch does 2x MATCH

neo4j-admin import (Offline Bulk Load)

Fastest method: ~3 min for 31M nodes / 78M rels on SSD. DB must be stopped or non-existent.

Command structure

neo4j-admin database import full \
  --nodes=Person="persons_header.csv,persons.csv" \
  --nodes=Movie="movies_header.csv,movies.csv" \
  --relationships=ACTED_IN="acted_in_header.csv,acted_in.csv" \
  --relationships=DIRECTED="directed_header.csv,directed.csv" \
  --delimiter=, \
  --id-type=STRING \

Use this skill in a project

Open any RiverX project, search the Skills panel for neo4j-contrib/neo4j-skills/neo4j-import-skill, and install it in one click.

Use this skill