# How GraphML, Neo4j, and Obsidian Export Formats Handle Metadata in Code Review Graphs

> Learn how GraphML, Neo4j, and Obsidian export formats manage metadata for code review graphs. Discover how core fields like qualified name, kind, and file path are preserved.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: deep-dive
- Published: 2026-08-16

---

**All three export formats—GraphML, Neo4j Cypher, and Obsidian—preserve the same core metadata fields including `qualified_name`, `kind`, `file_path`, `language`, `community_id`, and edge relationship types, each encoded according to format-specific conventions.**

The `code_review_graph` package provides multiple export backends for analyzing code relationships as graph structures. Each format transforms the unified payload from `export_graph_data(store)` differently while maintaining complete semantic fidelity. Understanding these metadata mappings helps you select the right export format for your visualization or database needs.

## Core Metadata Fields Exported Across All Formats

Every export format preserves six essential metadata fields that describe nodes and edges in the code review graph.

| Metadata Field | Description |
|----------------|-------------|
| `qualified_name` | Fully-qualified identifier (e.g., `module::Class::method`) |
| `kind` | Entity type (`File`, `Class`, `Function`, `Method`, etc.) |
| `file_path` | Absolute or project-relative source file location |
| `language` | Programming language of the symbol |
| `community_id` | Optional community clustering identifier |
| Edge `kind` | Semantic relationship type (`CALLS`, `IMPORTS`, `INHERITS`, etc.) |

These fields originate in [`code_review_graph/visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/visualization.py) through the `export_graph_data` function, which supplies the unified payload consumed by all exporters.

## GraphML Export: XML-Based Metadata Encoding

The **GraphML export** encodes metadata as typed XML data attributes. In [`code_review_graph/exports.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/exports.py) (lines 72–108), the `export_graphml` function constructs an XML document with explicit `<key>` declarations for each metadata attribute.

Node metadata is written as `<data>` children:
- `kind` → `<data key="kind">`
- `file_path` → `<data key="file">`
- `language` → `<data key="language">`
- `community_id` → `<data key="community">` (optional)

Edge metadata uses `<data key="edge_kind">` to preserve relationship types. Values are escaped with `html.escape()` to ensure XML safety.

```python

# GraphML Lines 72-73: Writing node kind

xml_data += f'    <data key="kind">{html.escape(str(data.get("kind", "Unknown")))}</data>\n'
xml_data += f'    <data key="file">{html.escape(str(data.get("file_path", "")))}</data>\n'

```

The resulting `.graphml` file imports directly into **Gephi**, **yEd**, **Cytoscape**, and other graph visualization tools with full metadata preservation.

## Neo4j Cypher Export: Property Maps and Relationship Labels

The **Neo4j Cypher export** translates metadata into Cypher-native structures. In [`code_review_graph/exports.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/exports.py) (lines 41–65), `export_neo4j_cypher` generates `CREATE` statements:

- `qualified_name` becomes a node property and matching key for edge creation
- `kind` becomes a **node label** (`:Class`, `:Function`) and **relationship type**
- `file_path`, `language`, and `community_id` populate the property map

```python

# Neo4j Cypher Lines 41-42, 58-60: Label and relationship creation

node_lines.append(f'CREATE (n:{kind} {{qualified_name: "{qualified_name}", ...}})')
rel_lines.append(f'MATCH (a), (b) WHERE a.qualified_name = "{source}" AND b.qualified_name = "{target}" CREATE (a)-[:{edge_kind}]->(b)')

```

Empty `language` values default to empty strings. Edge kinds default to `RELATES_TO` when unspecified. The output is a plain-text `.cypher` script ready for `cypher-shell` or Neo4j Browser execution.

## Obsidian Vault Export: Front-Matter and Wikilinks

The **Obsidian export** creates a navigable knowledge base with metadata embedded in markdown front-matter. In [`code_review_graph/exports.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/exports.py) (lines 39–82), `export_obsidian_vault` generates one file per node:

```yaml
---
kind: Function
file: src/core/parser.py
language: python
community: 3
---

```

The `qualified_name` drives **Wikilink resolution**—`[[module::Class::method|Display Name]]`—enabling bidirectional navigation. The "Connections" section lists adjacent nodes with their relationship types:

```markdown

## Connections

- **CALLS**: [[target_module::TargetFunc]]
- **IMPORTS**: [[some_module::SomeClass]]

```

Community overview pages (`_COMMUNITY_<id>.md`) aggregate members, and [`_INDEX.md`](https://github.com/tirth8205/code-review-graph/blob/main/_INDEX.md) provides a complete directory. This format preserves all metadata while creating a **human-readable, traversable documentation system**.

## Quick Export Examples

```python
from code_review_graph.exports import export_graphml, export_neo4j_cypher, export_obsidian_vault
from pathlib import Path

# GraphML for network visualization tools

export_graphml(store, Path("output/graph.graphml"))

# Cypher script for Neo4j graph database

export_neo4j_cypher(store, Path("output/graph.cypher"))

# Complete Obsidian vault with metadata-rich markdown

export_obsidian_vault(store, Path("output/obsidian_vault"))

```

## Metadata Handling Comparison

| Aspect | GraphML | Neo4j Cypher | Obsidian |
|--------|---------|--------------|----------|
| **Storage model** | XML attributes | Property maps + labels | YAML front-matter |
| **Qualified name usage** | Node/edge ID | Matching key + property | Filename + Wikilinks |
| **Kind representation** | `<data key="kind">` | Node/relationship labels | `kind:` front-matter field |
| **Edge semantics** | `edge_kind` data attribute | Relationship type label | Markdown list with bold kind |
| **Community support** | Optional `<data>` element | Optional property | Front-matter + overview pages |
| **Best for** | Visualization tools (Gephi, yEd) | Graph databases, complex queries | Documentation, manual exploration |

## Key Source Files

- **[`code_review_graph/exports.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/exports.py)** – Core implementations for all export formats (`export_graphml`, `export_neo4j_cypher`, `export_obsidian_vault`)
- **[`code_review_graph/visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/visualization.py)** – `export_graph_data` supplies unified metadata payload
- **[`tests/test_visualization.py`](https://github.com/tirth8205/code-review-graph/blob/main/tests/test_visualization.py)** – Validates metadata preservation across formats

## Summary

- **GraphML** uses XML `<data>` elements with typed keys, suitable for interchange with visualization platforms
- **Neo4j Cypher** maps metadata to property maps and labels, optimized for graph database queries
- **Obsidian** embeds metadata in YAML front-matter with Wikilink navigation, designed for human-readable knowledge bases
- All three formats originate from the same `export_graph_data` payload, ensuring consistent metadata availability regardless of output format

## Frequently Asked Questions

### Does GraphML preserve community clustering information?

Yes. When `community_id` exists, GraphML emits `<data key="community">` on nodes (lines 78–99 in [`exports.py`](https://github.com/tirth8205/code-review-graph/blob/main/exports.py)). The key is optional and only included when the community assignment exists in the source graph.

### How does Neo4j handle missing relationship types?

The Neo4j exporter defaults edge labels to `RELATES_TO` when the `kind` field is empty or unspecified (lines 58–60). Explicit kinds like `CALLS` or `IMPORTS` become the actual relationship type in the generated Cypher.

### Can Obsidian vaults be regenerated incrementally?

The `export_obsidian_vault` function creates a complete vault from scratch each run. Existing files are overwritten. For incremental updates, you would need to implement custom merge logic outside the core exporter.

### Which format best preserves metadata for programmatic analysis?

**Neo4j Cypher** offers the strongest programmatic access—property maps support indexed lookups, and Cypher's query language enables complex traversals. **GraphML** works well for tool interoperability, while **Obsidian** prioritizes human readability over programmatic access.