How GraphML, Neo4j, and Obsidian Export Formats Handle Metadata in Code Review Graphs
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 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 (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.
# 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 (lines 41–65), export_neo4j_cypher generates CREATE statements:
qualified_namebecomes a node property and matching key for edge creationkindbecomes a node label (:Class,:Function) and relationship typefile_path,language, andcommunity_idpopulate the property map
# 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 (lines 39–82), export_obsidian_vault generates one file per node:
---
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:
## Connections
- **CALLS**: [[target_module::TargetFunc]]
- **IMPORTS**: [[some_module::SomeClass]]
Community overview pages (_COMMUNITY_<id>.md) aggregate members, and _INDEX.md provides a complete directory. This format preserves all metadata while creating a human-readable, traversable documentation system.
Quick Export Examples
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– Core implementations for all export formats (export_graphml,export_neo4j_cypher,export_obsidian_vault)code_review_graph/visualization.py–export_graph_datasupplies unified metadata payloadtests/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_datapayload, 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). 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →