Version Diffing
Tutorial
Version Diffing
Compare mapping versions and track schema evolution
What You'll Learn
- Version Comparison - Compare two mapping versions
- Diff Types - Added, removed, modified elements
- Schema Evolution - Track changes over time
- Migration Planning - Plan data migrations from diffs
# Cell 1 — ParametersUSERNAME = "_FILL_ME_IN_" # Set your email before running# Cell 2 — Connectfrom graph_olap import GraphOLAPClientclient = GraphOLAPClient(username=USERNAME)
# Cell 3 — Provisionfrom notebook_setup import provisionfrom graph_olap.models.mapping import NodeDefinition, EdgeDefinition, PropertyDefinitionfrom graph_olap.utils.diff import render_diff_summary, render_diff_details, diff_to_dict
personas, conn = provision(USERNAME)analyst = personas["analyst"]admin = personas["admin"]ops = personas["ops"]client = analyst
print(f"Connected to: {client._config.api_url}")
# Create a dedicated mapping for version-diffing demosmapping = client.mappings.create( name="DiffDemo", node_definitions=[ NodeDefinition( label="Customer", sql="SELECT '1' as id, 'Alice' as name", primary_key={"name": "id", "type": "STRING"}, properties=[PropertyDefinition(name="name", type="STRING")], ), ], edge_definitions=[],)
# Create version 2 by adding a Location nodeclient.mappings.update( mapping.id, change_description="Add Location node", node_definitions=mapping.node_definitions + [ NodeDefinition( label="Location", sql="SELECT 'HK' as id, 'Hong Kong' as name", primary_key={"name": "id", "type": "STRING"}, properties=[PropertyDefinition(name="name", type="STRING")], ), ], edge_definitions=mapping.edge_definitions,)print(f"Mapping: {mapping.name} (id={mapping.id}) -- 2 versions created")
1
Comparing Versions
Create a diff
# Compare version 1 to version 2diff = client.mappings.diff(mapping.id, from_version=1, to_version=2)
# The summary dict gives quick countsprint("=== Diff Summary (v1 → v2) ===")print(f"Nodes added: {diff.summary['nodes_added']}")print(f"Nodes removed: {diff.summary['nodes_removed']}")print(f"Nodes modified: {diff.summary['nodes_modified']}")print(f"Edges added: {diff.summary['edges_added']}")print(f"Edges removed: {diff.summary['edges_removed']}")
# Expected output:# === Diff Summary (v1 → v2) ===# Nodes added: 1# Nodes removed: 0# Nodes modified: 0# Edges added: 0# Edges removed: 0
2
Understanding Diffs
Interpreting changes
# Detailed changes are in diff.changes, keyed by "nodes" and "edges"print("=== Node Changes ===")for change in diff.changes["nodes"]: # change_type is one of: "added", "removed", "modified" prefix = {"added": "+", "removed": "-", "modified": "~"}[change.change_type] print(f" {prefix} {change.label} (change_type={change.change_type})")
# For modified nodes, fields_changed shows what specifically changed if change.fields_changed: for field in change.fields_changed: print(f" field: {field}")
print()print("=== Edge Changes ===")for change in diff.changes["edges"]: prefix = {"added": "+", "removed": "-", "modified": "~"}[change.change_type] print(f" {prefix} {change.type} (change_type={change.change_type})")
# Expected output:# === Node Changes ===# + Location (change_type=added)## === Edge Changes ===# (none — edges unchanged between v1 and v2)
3
Reverse Diffs
Swap from/to to invert the perspective
# Reverse diff: v2 → v1 inverts the change typesreverse = client.mappings.diff(mapping.id, from_version=2, to_version=1)
print("=== Reverse Diff Summary (v2 → v1) ===")print(f"Nodes added: {reverse.summary['nodes_added']}")print(f"Nodes removed: {reverse.summary['nodes_removed']}")
print()for change in reverse.changes["nodes"]: print(f" {change.change_type}: {change.label}")
# Expected output:# === Reverse Diff Summary (v2 → v1) ===# Nodes added: 0# Nodes removed: 1## removed: Location## Note: What was "added" in v1→v2 becomes "removed" in v2→v1
4
SDK Rendering Utilities
Built-in helpers for formatting and serialising diffs
# render_diff_summary prints a formatted summary tablerender_diff_summary(diff)
# render_diff_details shows each change with optional from/to valuesrender_diff_details(diff, show_from_to=True)
# diff_to_dict serialises the diff to a plain dict (useful for JSON export)d = diff_to_dict(diff)print(f"\nSerialized keys: {list(d.keys())}")# d['changes'] is a flat list of all changesprint(f"Total changes: {len(d['changes'])}")for change in d['changes']: print(f" {change.get('change_type', '?')}: {change.get('label', change.get('type', '?'))} ({change.get('kind', 'node')})")Key Takeaways
client.mappings.diff(id, from_version, to_version)compares any two versions of a mappingdiff.summarygives counts of added, removed, and modified nodes/edgesdiff.changes["nodes"]contains detailed change objects withchange_type,label, andfields_changed- Swapping
from_versionandto_versioninverts the change types (added becomes removed) - SDK utilities
render_diff_summary,render_diff_details, anddiff_to_dictformat and serialise diffs
# Cleanup: delete the DiffDemo mapping created in this notebooktry: client.mappings.delete(mapping.id) print(f"Deleted: {mapping.name}")except Exception as e: print(f"Mapping cleanup: {e}")