TuringDB home pagelight logodark logo
  • Discord
  • GitHub
  • Website
TuringDB

Get Started

  • Introduction
  • Get Started
  • Claude Code Skill
  • Commands

Concepts

  • Overview
  • A Columnar Graph Database
  • Versioning System
  • The DataPart System
  • Zero-Locking Architecture
  • Snapshots Isolation

Benchmarks

  • Results Summary
  • Technical Report

Query Language

  • Overview
  • Matching
  • Paths
  • Results and Aggregation
  • Subqueries
  • Functions and Types
  • Writing Data
  • Procedures and Commands
  • CheatSheet

Importing data

  • JSONL
  • CSV
  • Neo4j
  • GML
  • Parquet

Graph Development

  • Create Graph
  • Load Graph
  • Load External data
  • Create a Change
  • List Changes
  • Create Nodes and Edges
  • Update Properties
  • Submit a Change
  • Time Travel
  • Graph Examples

Vector Search

  • Vector Search

Graph Algorithms

  • Shortest Path

Tutorials

  • Example Notebooks

Python SDK

  • Get Started
  • Reference

Troubleshooting

  • Troubleshooting

  • Syntax
  • With headers
  • Without headers (index access)
  • Combining with the rest of a query
  • Building a graph from CSV
Importing data

Load a CSV file

Stream rows from a CSV file in a query with LOAD CSV

LOAD CSV streams the rows of a CSV file into a query. Unlike LOAD JSONL / LOAD GML (which build a whole graph from a file), LOAD CSV is a reading statement that yields one row at a time, bound to a variable you can reference in RETURN, MATCH, or CREATE.

The file must live in the data subdirectory of the TuringDB working directory (set by -turing-dir, default $HOME/.turing). Absolute paths are re-rooted inside that directory.

​
Syntax

LOAD CSV "<file>" [WITH HEADERS] [ON ERROR SKIP | ON ERROR FAIL] AS <row>
ClauseDescription
WITH HEADERSTreat the first line as column names; access columns as row.<name>. Without it, access columns by 0-based index row[0], row[1], …
ON ERROR SKIPSkip malformed rows
ON ERROR FAILAbort on the first malformed row (the default)
AS <row>Variable bound to each row

All values come back as strings. Wrap them in toInteger(...) / toFloat(...) / datetime(...) if you need other types.

​
With headers

LOAD CSV "people.csv" WITH HEADERS AS row
RETURN row.name AS name, row.age AS age, row.city AS city

​
Without headers (index access)

LOAD CSV "people.csv" AS row
RETURN row[0] AS name, row[1] AS age
LIMIT 5

Read a row’s fields as row.col or row[<literal index>]. Without WITH HEADERS, the header line comes back as an ordinary data row.

​
Combining with the rest of a query

LOAD CSV composes with MATCH, WITH / WHERE, aggregates, ORDER BY and UNWIND. For example, join the rows with nodes that are already in the graph:

LOAD CSV "people.csv" WITH HEADERS AS row
MATCH (p:Person {name: row.name})
RETURN p.name, row.city

To use row.x inside an OPTIONAL MATCH pattern that directly follows LOAD CSV, project the field first: ... AS row WITH row.name AS nm OPTIONAL MATCH (p:Person {name: nm}) ....

​
Building a graph from CSV

Because LOAD CSV yields rows, you can drive CREATE from it inside a change:

client.new_change()
client.query('LOAD CSV "people.csv" WITH HEADERS AS row CREATE (:Person {name: row.name, age: toInteger(row.age)})')
client.query("COMMIT")   # make the new nodes visible to the next query
client.query("CHANGE SUBMIT")
client.checkout()

This approach suits small files and additions to an existing graph. Creating edges with LOAD CSV matches both endpoints for every row, so it is best kept to a few thousand edges. For bulk loads, convert the CSV to Parquet and use LOAD PARQUET, which loads 100,000 nodes and 200,000 edges in about 0.2 seconds.

To skip malformed rows, add ON ERROR SKIP (the default, ON ERROR FAIL, stops at the first one). See Writing data for a single-pass MERGE pattern that needs no COMMIT.

JSONLNeo4j
githublinkedinyoutubediscord