#3674 Exporting from Neo4j

#3674 Exporting from Neo4j

#3674 How do I export a JSON representation of a Neo4j knowledge graph?

There are a few good ways to export a JSON representation of your Neo4j knowledge graph, depending on whether you want the whole graph or just query results. Here are the main approaches:

1. Export Query Results as JSON (via Cypher)

You can directly return JSON-like structures in Cypher:

MATCH (n)-[r]->(m) RETURN { source: id(n), source_labels: labels(n), source_properties: properties(n), relationship: type(r), rel_properties: properties(r), target: id(m), target_labels: labels(m), target_properties: properties(m) } AS json_row;

In the Neo4j Browser or Neo4j Desktop, this will give you JSON objects per row.
In Python (with neo4j-driver) you can collect and dump them:

import json from neo4j import GraphDatabase driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password")) with driver.session() as session: result = session.run(""" MATCH (n)-[r]->(m) RETURN { source: id(n), source_labels: labels(n), source_properties: properties(n), relationship: type(r), rel_properties: properties(r), target: id(m), target_labels: labels(m), target_properties: properties(m) } AS row """) data = [record["row"] for record in result] with open("graph.json", "w") as f: json.dump(data, f, indent=2)

2. APOC Export (Recommended for Whole Graphs)

If you have the APOC plugin, you can export directly:

CALL apoc.export.json.all("graph.json", {useTypes:true});

Other variations:

  • apoc.export.json.data([nodes],[rels], "graph.json", {})

  • apoc.export.json.graph(graph, "graph.json", {})

This saves the JSON file in the Neo4j import/ directory by default.


3. Browser "Export JSON"

In Neo4j Browser or Bloom, after running a query, you can click the export menu (download icon) → JSON. This is great for ad hoc exports but not for automation.


4. Python or JavaScript Custom Exporter

If you want a nested JSON structure (like {"nodes": [...], "links": [...]} for D3.js or visualization):

with driver.session() as session: nodes = session.run("MATCH (n) RETURN id(n) AS id, labels(n) AS labels, properties(n) AS props") rels = session.run("MATCH (n)-[r]->(m) RETURN id(r) AS id, type(r) AS type, id(n) AS source, id(m) AS target, properties(r) AS props") graph_json = { "nodes": [dict(record) for record in nodes], "links": [dict(record) for record in rels] } with open("graph.json", "w") as f: json.dump(graph_json, f, indent=2)

Recommendation:

  • For automation / full dumps, use APOC export.

  • For custom shapes (like D3.js or knowledge graph pipelines), use the Python/JavaScript driver method.


Do you want the JSON to be raw Neo4j data (like APOC gives) or structured for visualization / downstream use (like nodes + links format)?


The HTML produces a blank canvas from a JSON file exported directly from Neo4j

A blank canvas almost always means your HTML expects a different JSON shape (or it never loads the file). Here’s a fast fix + a checklist.


Quick checklist (common causes)

  1. Wrong JSON shape
    Libraries like D3/vis.js typically expect:

{ "nodes": [{ "id": "n1", "label": "Person", "props": { ... } }, ...], "links": [{ "source": "n1", "target": "n2", "type": "KNOWS", "props": { ... } }, ...] }

APOC’s raw export is usually a flat list of records, not nodes/links. That mismatch = blank.

  1. IDs don’t match
    links[].source/target must match nodes[].id exactly (string vs number mismatches or using internal ids in one place and external ids in another will break the layout).

  2. Fetch didn’t happen
    Opening HTML from disk (file://) blocks fetch('graph.json'). Serve files via a tiny server (e.g., python -m http.server 8080) and load http://localhost:8080/.

  3. Container has no size
    If your <svg> or <canvas> is 0×0 (no width/height or CSS), nothing renders.

  4. Silent JS errors
    Open DevTools → Console. Any error about data.nodes or data.links means (1) above.


Option A — Export D3-ready JSON directly from Neo4j

If you have APOC, run a single export that returns the exact shape your HTML needs:

CALL apoc.export.json.query( " MATCH (n) WITH collect(DISTINCT n) AS ns MATCH (n)-[r]->(m) WITH ns, collect(DISTINCT r) AS rs WITH [x IN ns | {id: toString(id(x)), labels: labels(x), props: properties(x)}] AS nodes, [x IN rs | { id: toString(id(x)), type: type(x), source: toString(id(startNode(x))), target: toString(id(endNode(x))), props: properties(x) }] AS links RETURN {nodes:nodes, links:links} AS graph ", "graph.json", {useTypes:true, stream:false} );

This writes graph.json (usually into Neo4j’s import/ directory) with {nodes:[...], links:[...]}.


Option B — Transform an existing APOC export into nodes/links

If you already exported raw APOC JSON (list of node/rel objects), transform it before drawing. Here’s a small browser-side transformer you can drop into your page:

<script> async function loadGraph() { const raw = await fetch('apoc_raw.json').then(r => r.json()); // raw could be an array of mixed entries or two arrays; normalize: const nodes = []; const links = []; const nodeSeen = new Set(); for (const row of raw) { // Heuristics for APOC shapes — adjust if your file differs: // Node-like if ((row.type === 'node' || row.labels) && row.properties) { const id = String(row.id ?? row._id ?? row.elementId ?? row.properties.id ?? row.identity); if (id && !nodeSeen.has(id)) { nodeSeen.add(id); nodes.push({ id, labels: row.labels || row.label || [], props: row.properties }); } } // Relationship-like if ((row.type === 'relationship' || row.rel || row.relationship) && row.properties) { const rid = String(row.id ?? row._id ?? row.elementId ?? row.properties.id ?? row.identity); const source = String(row.start ?? row.startNode ?? row.startNodeId ?? row.from); const target = String(row.end ?? row.endNode ?? row.endNodeId ?? row.to); links.push({ id: rid, type: row.label || row.type || row.relationship, source, target, props: row.properties }); } } // Ensure that every link endpoint exists as a node const have = new Set(nodes.map(n => n.id)); for (const k of ['source','target']) { for (const e of links) { if (!have.has(e[k])) { nodes.push({ id: e[k], labels: ['Unresolved'], props: {} }); have.add(e[k]); } } } return { nodes, links }; } </script>

Minimal, known-good D3 template (works if your JSON is {nodes,links})

Save this as index.html, put graph.json next to it, and serve via a local server:

<!doctype html> <html> <head> <meta charset="utf-8" /> <title>Graph</title> <style> html, body { height:100%; margin:0; } #viz { width:100%; height:100vh; } svg { width:100%; height:100%; display:block; } .node circle { r:6; } .link { stroke-opacity:0.6; } </style> </head> <body> <div id="viz"></div> <script src="https://unpkg.com/d3@7"></script> <script> (async function() { // If you used Option B, replace next line with: const data = await loadGraph(); const data = await fetch('graph.json').then(r => r.json()); // Basic validation if (!data.nodes || !data.links) { throw new Error('Expected {nodes:[], links:[]} in graph.json'); } const width = document.getElementById('viz').clientWidth; const height = document.getElementById('viz').clientHeight; const svg = d3.select('#viz').append('svg'); const sim = d3.forceSimulation(data.nodes) .force('link', d3.forceLink(data.links).id(d => String(d.id)).distance(60).strength(0.1)) .force('charge', d3.forceManyBody().strength(-160)) .force('center', d3.forceCenter(width/2, height/2)); const link = svg.append('g') .attr('stroke', '#999') .attr('class', 'link') .selectAll('line') .data(data.links) .join('line'); const node = svg.append('g') .attr('class', 'node') .selectAll('circle') .data(data.nodes) .join('circle') .attr('r', 5) .attr('fill', '#4682b4') .call(d3.drag() .on('start', (event, d) => { if (!event.active) sim.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }) .on('drag', (event, d) => { d.fx = event.x; d.fy = event.y; }) .on('end', (event, d) => { if (!event.active) sim.alphaTarget(0); d.fx = null; d.fy = null; }) ) .append('title') .text(d => (d.labels?.[0] || 'Node') + ' ' + d.id); sim.on('tick', () => { link .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); svg.selectAll('circle') .attr('cx', d => d.x) .attr('cy', d => d.y); }); window.addEventListener('resize', () => { const w = document.getElementById('viz').clientWidth; const h = document.getElementById('viz').clientHeight; svg.attr('width', w).attr('height', h); sim.force('center', d3.forceCenter(w/2, h/2)).alpha(0.2).restart(); }); })(); </script> </body> </html>

If you prefer to build the JSON via Cypher (no post-processing)

Run this in Neo4j Browser to copy/paste the JSON result directly (or export via APOC as shown earlier):

MATCH (n) WITH collect(DISTINCT {id: toString(id(n)), labels: labels(n), props: properties(n)}) AS nodes MATCH (a)-[r]->(b) WITH nodes, collect(DISTINCT { id: toString(id(r)), type: type(r), source: toString(id(startNode(r))), target: toString(id(endNode(r))), props: properties(r) }) AS links RETURN {nodes:nodes, links:links} AS graph;

Copy the graph value to graph.json.


Debug fast

  • Open console: look for Expected {nodes:[], links:[]} or Cannot read properties of undefined (reading 'x') → means your links don’t resolve to node IDs.

  • Log shapes: console.log(data, data.nodes?.[0], data.links?.[0]).

  • Check ID types: cast everything to String(id(...)) in Cypher and use .id(d => String(d.id)) in D3.


If you share a tiny snippet of your current JSON (first two elements of whatever it contains), I’ll tailor the exact one-liner transform for your format.


From <https://chatgpt.com/>  ChatGPT 5

Comments

Popular posts from this blog

#2892 Example of ChatGPT o1 Prompt

#3105 Federal and State Spending Idiocy