How the Graph Export Feature in code-graph-rag Works for External Analysis Tools

The graph export feature in code-graph-rag converts an in-memory code graph into a portable JSON file that any external tool can ingest, using Cypher queries to extract nodes and relationships from Memgraph and serializing them through GraphService.export_graph_to_dict() and export_graph_to_file().

The code-graph-rag repository by vitali87 builds rich knowledge graphs from codebases and stores them in Memgraph. A critical capability is exporting these graphs for external analysis—whether for visualization, static analysis benchmarks, or feeding structured code relationships into LLM pipelines. This article explains the complete export architecture, from Cypher query execution to JSON file generation.


Core Export Architecture

The export pipeline consists of five coordinated components that transform database state into a self-contained JSON artifact.

1. MemgraphIngestor Triggers Export

The MemgraphIngestor class manages the lifecycle of graph data in Memgraph. When export is requested, it delegates to GraphService.export_graph_to_dict() to materialize the entire graph into Python data structures.

In codebase_rag/graph_updater.py, the ingestor maintains the database connection handle that GraphService uses for query execution.

2. GraphService Executes Cypher Queries

The export_graph_to_dict() method in codebase_rag/services/graph_service.py (lines 60–78) performs the heavy lifting:


# From codebase_rag/services/graph_service.py

def export_graph_to_dict(self) -> Dict[str, Any]:
    """Export the entire graph as a dictionary for external use."""
    nodes = self._fetch_nodes()
    relationships = self._fetch_relationships()
    metadata = self._build_metadata(len(nodes), len(relationships))
    
    return {
        "nodes": nodes,
        "relationships": relationships,
        "metadata": metadata
    }

Two pre-defined Cypher queries retrieve the raw graph data:

  • CYPHER_EXPORT_NODES – Returns every node with its ID, labels, and property map
  • CYPHER_EXPORT_RELATIONSHIPS – Returns every relationship with source ID, target ID, type, and properties

These queries use Memgraph's map projection to ensure JSON-serializable output formats directly from the database.

3. Metadata and Timestamp Generation

The _build_metadata() helper constructs a GraphMetadata dataclass containing:

Field Description
total_nodes Count of nodes exported
total_relationships Count of relationships exported
exported_at ISO 8601 timestamp from _get_current_timestamp() (UTC)

The timestamp is generated once per export, ensuring consistency across all serialised components.

4. export_graph_to_file Serializes to Disk

The export_graph_to_file() function in codebase_rag/main.py (lines 1400–1410) wraps the dictionary export with file I/O:


# From codebase_rag/main.py

def export_graph_to_file(ingestor, output_path: str) -> bool:
    """Export graph to a JSON file for external analysis tools."""
    try:
        graph_data = ingestor.export_graph_to_dict()
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(graph_data, f, indent=2, ensure_ascii=False)
        logger.info(f"Graph exported successfully to {output_path}")
        return True
    except Exception as e:
        logger.error(f"Failed to export graph: {e}")
        return False

Error handling is explicit: exceptions are logged and converted to boolean status, allowing CLI and programmatic callers to implement retry logic or fallback behavior.

5. CLI Entry Point

The --export flag in codebase_rag/cli.py (lines 320–340) exposes this functionality to shell users:


# From codebase_rag/cli.py

if args.export:
    success = export_graph_to_file(ingestor, args.export)
    sys.exit(0 if success else 1)

JSON Output Schema

Exported files follow a predictable structure that external tools can rely on:

{
  "nodes": [
    {
      "id": "func://src/utils.py::calculate_hash",
      "labels": ["Function", "Callable"],
      "properties": {
        "name": "calculate_hash",
        "file_path": "src/utils.py",
        "line_number": 45,
        "docstring": "Compute BLAKE2b hash of input data."
      }
    }
  ],
  "relationships": [
    {
      "source_id": "func://src/utils.py::calculate_hash",
      "target_id": "func://src/crypto.py::blake2b",
      "type": "CALLS",
      "properties": {
        "confidence": 1.0,
        "static": true
      }
    }
  ],
  "metadata": {
    "total_nodes": 1247,
    "total_relationships": 3892,
    "exported_at": "2024-01-15T09:23:17.342Z"
  }
}

This schema is self-describing: no external schema registry or Memgraph connection is required to interpret the file.


Practical Usage Examples

Export via Command Line


# Basic export to current directory

python -m codebase_rag.main --export ./my_project_graph.json

# Verify export succeeded

jq '.metadata' my_project_graph.json

Programmatic Export in Python

from codebase_rag.graph_updater import MemgraphIngestor
from codebase_rag.main import export_graph_to_file

# Initialize with existing Memgraph connection

ingestor = MemgraphIngestor(uri="bolt://localhost:7687")

# Export to file

success = export_graph_to_file(ingestor, "code_graph.json")
assert success, "Export failed"

# Or work with the dictionary directly

graph_dict = ingestor.export_graph_to_dict()
print(f"Exported {graph_dict['metadata']['total_nodes']} nodes")

Loading into NetworkX for Analysis

import json
import networkx as nx

# Load exported graph

with open("code_graph.json") as f:
    data = json.load(f)

# Build NetworkX directed graph

G = nx.DiGraph()

# Add nodes with all properties

for node in data["nodes"]:
    G.add_node(node["id"], labels=node["labels"], **node["properties"])

# Add edges with relationship type and properties

for rel in data["relationships"]:
    G.add_edge(
        rel["source_id"],
        rel["target_id"],
        relation_type=rel["type"],
        **rel["properties"]
    )

# Run external analysis: find most called functions

in_degree = dict(G.in_degree())
most_called = max(in_degree, key=in_degree.get)
print(f"Most called function: {most_called} ({in_degree[most_called]} callers)")

Importing into Neo4j for Visualization

from py2neo import Graph, Node, Relationship
import json

g = Graph("bolt://localhost:7687", auth=("neo4j", "password"))

with open("code_graph.json") as f:
    data = json.load(f)

# Create nodes

node_map = {}
for n in data["nodes"]:
    node = Node(*n["labels"], **n["properties"])
    g.create(node)
    node_map[n["id"]] = node

# Create relationships

for r in data["relationships"]:
    src = node_map[r["source_id"]]
    tgt = node_map[r["target_id"]]
    g.create(Relationship(src, r["type"], tgt, **r.get("properties", {})))

Reliability and Performance Considerations

Memory Limits for Large Graphs

GraphService applies settings.QUERY_MEMORY_LIMIT_MB before executing Cypher queries, preventing unbounded memory consumption when exporting massive codebases.

Consistent Error Handling

Export failures are logged with structured messages (ls.MG_EXPORTING, ls.MG_EXPORTED from localization strings) and propagated as boolean return values. This supports:

  • Retry logic with exponential backoff
  • Fallback to partial exports
  • Pipeline failure notifications in CI/CD systems

Timestamp Consistency

The UTC timestamp is captured once at export start, not per-query. This ensures that metadata.exported_at accurately represents a single logical snapshot, even if node and relationship queries execute sequentially.


Key Source Files

File Purpose Direct Link
codebase_rag/services/graph_service.py Core export logic: export_graph_to_dict(), Cypher query execution, metadata construction View source
codebase_rag/main.py File serialization wrapper: export_graph_to_file() with error handling View source
codebase_rag/cli.py Command-line interface exposing --export flag View source
codebase_rag/graph_updater.py MemgraphIngestor class that orchestrates database operations View source
evals/cgr_graph.py Evaluation harness consuming exported graphs for RAG benchmarks View source
codebase_rag/tests/test_main_helpers_unit.py Unit tests verifying export functionality and edge cases View source

Summary

  • export_graph_to_dict() in graph_service.py queries Memgraph using CYPHER_EXPORT_NODES and CYPHER_EXPORT_RELATIONSHIPS, returning a structured dictionary with nodes, relationships, and metadata
  • export_graph_to_file() in main.py handles JSON serialization and file I/O with explicit error handling
  • CLI integration via --export flag makes the feature accessible without writing code
  • Self-describing JSON output requires no live database connection for external tools to consume
  • Memory limits and consistent timestamps ensure reliable operation on large codebases

Frequently Asked Questions

What format does the graph export produce?

The export produces JSON with a stable three-part structure: nodes (array of node objects), relationships (array of relationship objects), and metadata (export statistics and timestamp). This format is natively supported by Python's json module, JavaScript, and most graph analysis libraries.

Can I export without running the full ingestion pipeline?

No—the export requires an active MemgraphIngestor instance with a populated database. The export queries read from Memgraph, not from cached intermediate files. To export a codebase, you must first complete ingestion, then call the export function.

How large can exported graphs be?

The system handles large graphs through two mechanisms: query-level memory limits (settings.QUERY_MEMORY_LIMIT_MB) prevent database-side overload, and streaming JSON serialization avoids loading the entire export into RAM before writing. For multi-million node graphs, consider exporting subgraphs using filtered Cypher queries directly against Memgraph.

Which external tools are compatible with the export format?

Any tool that ingests JSON works directly. Verified use cases include NetworkX (Python graph algorithms), Neo4j (import via apoc.import.json or py2neo), Graphistry (visualization), Gephi (via JSON → GEXF conversion), and custom LLM pipelines that parse the structure for retrieval-augmented generation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →