Skip to main content

iris-vector-graph

Turn InterSystems IRIS into a graph + vector database — openCypher queries, HNSW/BM25 hybrid search, temporal property graph, RDF/SHACL/PROV-O semantic layer, and Neo4j Bolt protocol compatibility.

PyPI Python 3.10+ IRIS 2024.1+ License: MIT


Getting Started

Five minutes from zero to running graph queries.

1. Start IRIS

docker compose up -d

Starts IRIS Community Edition on localhost:1972. No license required. Default credentials: _SYSTEM / SYS.

2. Install

pip install iris-vector-graph

3. Run your first query

import iris
from iris_vector_graph.engine import IRISGraphEngine

conn = iris.connect("localhost", 1972, "USER", "_SYSTEM", "SYS")
engine = IRISGraphEngine(conn, embedding_dimension=768)
engine.initialize_schema()

engine.create_node("alice", labels=["Person"], properties={"name": "Alice"})
engine.create_node("bob",   labels=["Person"], properties={"name": "Bob"})
engine.create_edge("alice", "KNOWS", "bob")

result = engine.execute_cypher(
    "MATCH (a {node_id:$id})-[:KNOWS]->(b) RETURN b.name AS name",
    {"id": "alice"}
)
print(result["rows"])  # [('Bob',)]

Note: initialize_schema() prints compile warnings on Community Edition — safe to ignore. Enterprise-only classes (Graph.KG.MCPService, Graph.KG.MCPToolSet) are not required.


What It Does

Feature Notes
openCypher MATCH, CREATE, MERGE, DELETE, WITH, UNWIND, variable-length paths, subqueries
Temporal property graph Time-windowed edges, pre-aggregated bucket analytics, O(1) window queries
Vector search HNSW (native IRIS VECTOR), IVFFlat, PLAID multi-vector, BM25 full-text
Graph analytics Betweenness, closeness, eigenvector, degree centrality; Leiden community detection; SCC; k-core; PPR
Shortest path Unweighted BFS (shortestPath), weighted Dijkstra (ivg.shortestPath.weighted)
NKG fast-path [*1..N] Cypher patterns route to integer-keyed ^NKG index, bypassing SQL translation
Bulk loader 190–312K edges/s direct ^KG write; incremental ^NKG rebuild
FHIR bridge ICD-10 → knowledge graph mapping via FHIR R4
Bolt protocol neo4j-driver compatible wire protocol (TCP + WebSocket)
Embedded Python Graph algorithms run server-side via IRIS embedded Python (igraph, leidenalg)
IPM / ZPM ObjectScript-only install via InterSystems Package Manager
RDF export export_rdf() — full or filtered graph to Turtle/NT/NQuads/JSON-LD
SHACL validation validate_shacl() — SHACL Core via PySHACL; ValidationReport dataclass
PROV-O prov_export() — temporal edges as W3C PROV-O provenance graph

Performance

Hardware: M3 Ultra, Community IRIS 2026.1, ARM64 Docker.

Query latency

Query Latency Notes
1-hop neighbor lookup ~0.4ms $Order on ^KG
NKG fast-path [*1..N], hops 2–5 1.4–2.0ms 4.9–13.4x faster than SQL path
IC3 2-hop with LIMIT (LDBC SF10) 1.2ms 3.5x faster than GES/GraphScope
IC13 shortest path (LDBC SF10) 2.1–3.2ms Comparable to GES at SF1000 on cluster
HNSW vector search (768-dim) 1.7ms Native IRIS VECTOR index
BM25 full-text (174 nodes, 3-term) 0.3ms Posting-list $Order
Temporal window query 0.1ms O(results), B-tree
Pre-aggregated bucket (24hr/288 buckets) 0.16ms O(buckets), not O(edges)

Algorithm comparison (vs Neo4j GDS and networkx)

IVG is competitive with or faster than Neo4j GDS on degree centrality, betweenness, and Leiden community detection, producing results identical to networkx (Pearson r = 1.0). Validated on DRKG biomedical KG (~97K nodes / ~5.9M edges).

Full methodology and numbers: docs/performance/BENCHMARKS.md and docs/performance/GRAPH_ALGORITHMS.md.


Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                          iris-vector-graph                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌───────────────┐   ┌───────────────┐   ┌───────────────────┐    │
│   │  Python SDK   │   │  Cypher/AQL   │   │   Bolt (wire)     │    │
│   │  IRISGraph    │   │  translator   │   │   neo4j-driver    │    │
│   │  Engine       │   │  + executor   │   │   compatible      │    │
│   └───────┬───────┘   └───────┬───────┘   └────────┬──────────┘    │
│           └──────────────┬────┘                    │               │
│                          ▼                          │               │
│             ┌────────────────────────┐              │               │
│             │   GraphStore protocol  │◄─────────────┘               │
│             │   (pluggable backend)  │                              │
│             └───────────┬────────────┘                              │
│                         │                                           │
│          ┌──────────────┼──────────────┐                           │
│          ▼              ▼              ▼                            │
│   ┌─────────────┐ ┌──────────┐ ┌───────────────┐                  │
│   │  SQL layer  │ │  ^KG     │ │  ^NKG         │                  │
│   │  Graph_KG.* │ │  globals │ │  integer adj  │                  │
│   │  (nodes,    │ │  (edges, │ │  index        │                  │
│   │   edges,    │ │   temp,  │ └───────┬───────┘                  │
│   │   vectors)  │ │   PPR)   │         │                          │
│   └─────────────┘ └──────────┘         │                          │
│                                         ▼                          │
│                              ┌────────────────────┐               │
│                              │  Algorithm tiers   │               │
│                              ├────────────────────┤               │
│                              │ 1. Rust accelerator│ ← fastest     │
│                              │    (rayon parallel)│               │
│                              │ 2. ObjectScript    │               │
│                              │    parallel 8×     │               │
│                              │ 3. Python LazyKG   │ ← always works│
│                              └────────────────────┘               │
│                                                                     │
│   Centrality:  betweenness (Brandes) · closeness · eigenvector     │
│                degree                                              │
│   Community:   Leiden · triangle count · SCC · k-core             │
│   Search:      vector (HNSW/IVF/PLAID) · BM25 · temporal · PPR   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Full schema and ObjectScript class reference: docs/architecture/ARCHITECTURE.md.


Semantic Layer (RDF / SHACL / PROV-O)

pip install 'iris-vector-graph[rdf]'
# Export graph as Turtle (full or filtered)
engine.export_rdf("kg.ttl")
engine.export_rdf("proteins.nt", format="nt", label_filter=["Protein", "Disease"])
engine.export_rdf_from_cypher("MATCH (p:Patient)-[r]->(e) RETURN p,r,e", "patients.ttl")

# Persistent namespace prefixes
engine.register_namespace("fhir", "http://hl7.org/fhir/")

# SHACL Core validation
report = engine.validate_shacl("shapes/patient.ttl")
if not report.conforms:
    for v in report.violations:
        print(f"{v.focus_node}: {v.message} [{v.severity}]")

# PROV-O temporal provenance
engine.prov_export("provenance.ttl", ts_start=1700000000)
prov = engine.prov_as_dict(edge_id=42)

Every write to ivg is stored as W3C-aligned SPO triples (rdf_edges, rdf_props, rdf_labels) with OWL 2 RL inference, named graph support, and RDF-star style edge qualifiers. See docs/SEMANTIC_LAYER.md for the full guide: format reference, SHACL shape writing, PROV-O vocabulary mapping, and integration patterns.


Named Graphs

Scope nodes and edges to a named graph — useful for multi-tenant data, staging vs. production snapshots, or materializing a ledger reconstruction into an isolated subgraph.

# Create a node in the "umls" named graph
engine.create_node("C0027051", labels=["Concept"], graph="umls")

# Create an edge in the same named graph
engine.create_edge("C0027051", "ISA", "C0085580", graph="umls")

# Cypher: USE GRAPH targets the context for CREATE and MERGE
engine.execute_cypher("USE GRAPH umls CREATE (n:Concept {id: 'C0001234'})")

# Import an NDJSON file into a named graph
engine.import_graph_ndjson("export.ndjson", graph="staging")

# Erase one graph's content from every store: SQL rows, ^KG adjacency,
# degree counters, all five temporal trees. drop_graph() is a deprecated alias.
engine.erase_graph("staging")

# Erase every graph, including the stores no per-graph erase can reach
engine.erase_all()

# Independent per-graph, bidirectional drift check between SQL and ^KG
engine.verify_graph("umls")["ok"]

# delete_edge defaults to the default graph; pass all_graphs=True to sweep all
engine.delete_edge("C0027051", "ISA", "C0085580", graph="umls")
engine.delete_edge("C0027051", "ISA", "C0085580", all_graphs=True)

Nodes in the default graph use graph="" (empty-string sentinel); the BFS adjacency index (^KG) partitions by graph key so traversals stay within their graph. Existing code that never passes graph= is unaffected.

See docs/USER_GUIDE.md for the full guide.


Revision Ledger (Transaction-Time History)

The structural graph (nodes, labels, properties, relationships, qualifiers) can be versioned with an opt-in, immutable revision ledger. A changeset applies atomically and produces exactly one revision; a failed operation applies nothing. This is transaction time (when the ledger recorded a change) and is independent of the temporal property graph's event time (ts on temporal edges), which is unchanged and unversioned.

from iris_vector_graph.ledger import Changeset

genesis = engine.ledger.enable()              # idempotent; captures the existing graph
head = engine.ledger.head()

cs = Changeset(actor="ingest:etl-42", actor_type="ingest",
               message="equipment sync",
               expected_head=head.revision_id,      # optimistic concurrency
               idempotency_key="etl-42-2026-09-05")  # safe retry
cs.create_node("pump-7", labels=["Equipment"], properties={"status": "ok"})
cs.create_node("tank-2")
r = cs.create_relationship("pump-7", "FEEDS", "tank-2", qualifiers={"weight": "1.0"})
cs.set_qualifier(r, "capacity", "100")
result = engine.ledger.commit(cs)              # StaleHeadError if another writer won

engine.ledger.history(limit=20)                # deterministic, pageable, filterable
engine.ledger.get_revision(result.revision.revision_id).records
engine.ledger.diff(genesis.revision_id, result.revision.revision_id)
engine.ledger.reconstruct(genesis.revision_id) # read-only graph as of a revision
engine.ledger.export_reconstruction(result.revision.revision_id, "at-rev.ndjson")
engine.ledger.verify()                         # replay from genesis == current tables?
engine.ledger.verify(adopt=True)               # record any unrecorded (legacy) writes

Existing APIs keep working with a ledger enabled; writes made through them are "unrecorded" and are surfaced by verify(). enable(strict=True) (or set_strict(True)) rejects non-ledger structural writes with LedgerStrictModeError; reads, temporal writes, and index maintenance are unaffected. Relationships get a stable statement identity that survives qualifier changes and snapshot restore. Counters are in engine.status().ledger, the Graph_KG.ledger_stats table, and a metrics hook (engine.ledger.register_metrics_hook, see docs/ledger-prometheus-hook.md).


Non-USER Namespace Deployment

By default IVG connects to the USER namespace. When deploying against a HealthShare, Ensemble, or multi-tenant IRIS instance where data lives in a different namespace, pass namespace= at engine construction:

from iris_vector_graph import IRISGraphEngine
import iris.dbapi as dbapi

conn = dbapi.connect(hostname="...", port=1972, namespace="MYGRAPH", ...)
engine = IRISGraphEngine(conn, namespace="MYGRAPH")

MYGRAPH is a placeholder — use a namespace you own and have deployed IVG into. A namespace an installed product manages (a HealthShare/Ensemble platform or customization namespace) is the wrong home for IVG's schema: that product's own upgrade process owns the class and table set there.

On first use, the engine probes $Data(^KG("deg")) to verify the ^KG globals are accessible. If they are absent a WARNING is logged naming the namespace and the fix. Set IVG_STRICT_NAMESPACE=1 to upgrade the warning to a raised NamespaceMismatchWarning.

A namespace is an IVG namespace only once the classes are deployed

Graph.KG.* ObjectScript is what gives a namespace IVG's behaviour. A namespace whose schema was built by DDL alone — plain CREATE TABLE, or initialize_schema(auto_deploy_objectscript=False) — is a shell that looks right and behaves differently:

Property Classes deployed DDL only
rdf_edges.graph_id required, defaults to the default-graph key nullable
rdf_edges primary key ID edge_id
Graph.KG.Edge / Eraser / TemporalIndex present absent
Schema migrations (tighten_graph_id_column, add_graph_id_to_nodes) applied never reach it
Traversal, temporal and erasure acceleration native ObjectScript none

Check a namespace before trusting it:

SELECT COUNT(*) FROM %Dictionary.ClassDefinition WHERE Name = 'Graph.KG.Edge'

Zero means DDL-only. Deploy the classes into that namespace — initialize_schema() with auto-deploy, or $SYSTEM.OBJ.LoadDir("<path>/iris_src/src", "ck", .err, 1) from a session in that namespace — before writing data.

Also note IRIS auto-generates a compatibility view SQLUser.rdf_edges over Graph_KG.rdf_edges. When you query the catalog, filter TABLE_SCHEMA = 'Graph_KG'; the view carries no column defaults, so reading the wrong row makes a required graph_id look nullable.

CPF global mapping

If your graph data lives in a separate database, map the ^KG global in the IRIS CPF file so the probe passes without copying data:

[Map.MYGRAPH]
Global=^KG,Directory=/db/IRISLOCALDATA/

After mapping, $Data(^KG("deg")) returns non-zero in that namespace and no warning is emitted. Global mapping brings the data, not the behaviour — the classes still have to be deployed into the namespace.

Env var controls

Env var Effect
(neither) logging.warning on mismatch, execution continues
IVG_STRICT_NAMESPACE=1 logging.warning + raises NamespaceMismatchWarning
IVG_IGNORE_NAMESPACE_CHECK=1 Skip probe entirely (HealthShare platform-managed namespaces)

IVG_IGNORE_NAMESPACE_CHECK=1 takes precedence over IVG_STRICT_NAMESPACE=1.


Documentation

Document Contents
User Guide Cypher examples, named graphs, ledger, temporal edges, vector search
Admin Guide Container setup, schema management, index rebuilding
Admin API Python API reference for engine administration
Benchmarks Full methodology, LDBC SNB results, ingestion throughput
Graph Algorithms Centrality and community detection benchmark details
Semantic Layer RDF export, SHACL validation, PROV-O provenance
Changelog Full version history

AI Agent Development

When building agents that use IVG as a knowledge graph backend, install the iris-agentic-dev MCP toolkit for ObjectScript compilation, global inspection, and IRIS method invocation from your agent's tool loop:

pip install iris-agentic-dev

This gives your agent iris_compile, iris_execute, iris_global, and related tools that work alongside IVG's Python API.

Skill files with agent-oriented quickstarts:


License

MIT. See LICENSE.

Metadata

Release files for iris-vector-graph 3.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for iris-vector-graph 3.2.0
File Size Uploaded
iris_vector_graph-3.2.0.tar.gz 31.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for iris-vector-graph 3.2.0
File Interpreter ABI Platform
iris_vector_graph-3.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.9 MB

Release files / iris_vector_graph-3.2.0.tar.gz

Download URL iris_vector_graph-3.2.0.tar.gz
Size 31.5 MB
Tags Source
SHA-256 checksum
How to use checksums
529cccf6bf967e2906a80707943a38a8827bf6568663c30645f3c649d0b592d9
BLAKE2b-256 checksum
How to use checksums
0da67267f6f00663edc8e2cfef57b8260850cb635004b04ce4576497528afb4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / iris_vector_graph-3.2.0-py3-none-any.whl

Download URL iris_vector_graph-3.2.0-py3-none-any.whl
Size 472.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c60a6d4813da77e2a4b8d91346f4229abaa89d97b77f1438146a3cf0b2b5608
BLAKE2b-256 checksum
How to use checksums
1de35c40bd411f84618362465cb28b6c48acaea7e2c106d5ffc9feb82aefffa1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release history Release notifications | RSS feed

4.2.2

2 release files

4.2.1

2 release files

4.2.0

2 release files

4.1.1

2 release files

4.1.0

2 release files

4.0.0

2 release files

This release

3.2.0 This release

2 release files

3.1.0

2 release files

3.0.1

2 release files

2.20.0

2 release files

2.19.1

2 release files

2.19.0

2 release files

2.18.9

2 release files

2.18.8

2 release files

2.8.0

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.5.1

2 release files

2.5.0

2 release files

2.4.8

2 release files

2.4.6

2 release files

2.4.5

2 release files

2.4.3

2 release files

2.4.2

2 release files

2.4.1

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.0.0

2 release files

1.99.3

2 release files

1.99.2

2 release files

1.99.1

2 release files

1.99.0

2 release files

1.98.0

2 release files

1.97.10

1 release file

1.97.9

1 release file

1.97.8

1 release file

1.97.7

1 release file

1.97.6

1 release file

1.97.5

1 release file

1.97.4

1 release file

1.97.3

1 release file

1.97.2

1 release file

1.97.0

1 release file

1.96.2

1 release file

1.96.1

1 release file

1.96.0

1 release file

1.95.0

2 release files

1.94.0

2 release files

1.93.0

2 release files

1.92.2

2 release files

1.92.1

2 release files

1.92.0

2 release files

1.91.4

2 release files

1.91.3

2 release files

1.91.2

2 release files

1.91.1

2 release files

1.91.0

1 release file

1.81.0

1 release file

1.66.4

2 release files

1.66.3

2 release files

1.66.2

2 release files

1.66.1

2 release files

1.66.0

2 release files

1.65.4

2 release files

1.65.3

2 release files

1.65.2

2 release files

1.65.1

2 release files

1.65.0

2 release files

1.64.9

2 release files

1.64.8

2 release files

1.64.7

2 release files

1.64.6

2 release files

1.64.5

2 release files

1.64.4

2 release files

1.64.3

2 release files

1.64.2

2 release files

1.64.1

2 release files

1.64.0

2 release files

1.63.6

2 release files

1.63.5

2 release files

1.63.4

2 release files

1.63.3

2 release files

1.63.2

2 release files

1.63.1

2 release files

1.63.0

2 release files

1.62.1

2 release files

1.62.0

2 release files

1.61.1

2 release files

1.61.0

2 release files

1.60.0

2 release files

1.59.2

2 release files

1.59.1

2 release files

1.59.0

2 release files

1.58.1

2 release files

1.58.0

2 release files

1.57.0

2 release files

1.56.0

2 release files

1.55.3

2 release files

1.55.2

2 release files

1.55.1

2 release files

1.55.0

2 release files

1.54.2

2 release files

1.54.1

2 release files

1.54.0

2 release files

1.53.1

2 release files

1.53.0

2 release files

1.52.1

2 release files

1.52.0

2 release files

1.51.1

2 release files

1.51.0

2 release files

1.50.2

2 release files

1.50.1

2 release files

1.50.0

2 release files

1.49.0

2 release files

1.48.0

2 release files

1.47.1

2 release files

1.47.0

2 release files

1.36.0

2 release files

1.35.0

2 release files

1.34.0

2 release files

1.33.0

2 release files

1.32.0

2 release files

1.31.0

2 release files

1.30.0

2 release files

1.29.0

2 release files

1.28.0

2 release files

1.27.0

2 release files

1.26.4

2 release files

1.26.3

2 release files

1.26.2

2 release files

1.26.1

2 release files

1.26.0

2 release files

1.25.1

2 release files

1.25.0

2 release files

1.24.1

2 release files

1.24.0

2 release files

1.23.0

2 release files

1.22.1

2 release files

1.22.0

2 release files

1.21.1

2 release files

1.21.0

2 release files

1.20.2

2 release files

1.20.1

2 release files

1.20.0

2 release files

1.19.2

2 release files

1.19.1

2 release files

1.19.0

2 release files

1.18.0

2 release files

1.17.0

2 release files

1.16.2

2 release files

1.16.1

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.1

2 release files

1.14.0

2 release files

1.13.1

2 release files

1.13.0

2 release files

1.12.0

2 release files

1.11.0

2 release files

1.10.2

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.0

2 release files

1.8.2

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.5

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.4

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.9

2 release files

1.4.8

2 release files

1.4.7

2 release files

1.4.6

2 release files

1.4.5

2 release files

1.4.4

2 release files

1.4.3

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.9

2 release files

1.1.8

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page