IsonGraph 1.0.0

dotnet add package IsonGraph --version 1.0.0
                    
NuGet\Install-Package IsonGraph -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IsonGraph" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IsonGraph" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="IsonGraph" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add IsonGraph --version 1.0.0
                    
#r "nuget: IsonGraph, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package IsonGraph@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=IsonGraph&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=IsonGraph&version=1.0.0
                    
Install as a Cake Tool

<p align="center"> <img src="../assets/github_logo_stretched.png" alt="ISONGraph Logo"> </p>

ison-graph-cs

.NET C# License: MIT

ISONGraph - A token-efficient property graph store with ISON persistence for C# / .NET 8.

Features

  • Property Graph Model: Nodes and edges with string properties
  • O(1) Lookups: Fast node access by (type, id)
  • Multi-Hop Traversal: 1-hop, N-hop, and range queries
  • Path Finding: BFS shortest path and iterative all-paths DFS
  • ISONQL Query Language: Declarative graph queries
  • Schema Validation: Type-safe graph constraints (Graphantic)
  • Deterministic Visualization: Seeded force layout with bit-identical cross-language geometry, SVG and interactive HTML rendering
  • Fluent API: Chainable traversal and query builders
  • ISON Persistence: Token-efficient serialization (ISON and ISONL)
  • Zero Dependencies: Pure .NET base class library

Installation

dotnet add package IsonGraph

Or build from source:

dotnet build -c Release
dotnet test

Quick Start

using IsonGraph;

// Create a graph
var graph = new ISONGraph("social");

// Add nodes
graph.AddNode("person", "1", new() { ["name"] = "Alice", ["age"] = "30" });
graph.AddNode("person", "2", new() { ["name"] = "Bob", ["age"] = "25" });
graph.AddNode("person", "3", new() { ["name"] = "Charlie", ["age"] = "35" });
graph.AddNode("company", "100", new() { ["name"] = "TechCorp" });

// Add edges
graph.AddEdge("KNOWS", new("person", "1"), new("person", "2"), new() { ["since"] = "2020" });
graph.AddEdge("KNOWS", new("person", "2"), new("person", "3"), new() { ["since"] = "2021" });
graph.AddEdge("WORKS_AT", new("person", "1"), new("company", "100"), new() { ["role"] = "Engineer" });

// Query neighbors
List<NodeRef> friends = graph.Neighbors(new("person", "1"), "KNOWS");

// Multi-hop traversal
List<NodeRef> fof = graph.MultiHop(new("person", "1"), "KNOWS", 2);

// Shortest path
Path? path = graph.ShortestPath(new("person", "1"), new("person", "3"), "KNOWS");
if (path is not null)
    Console.WriteLine($"Path length: {path.Length}");

// Serialize
string ison = graph.ToIson();
graph.Save("social.ison");
var loaded = ISONGraph.Load("social.ison");

API Reference

ISONGraph

var graph = new ISONGraph("name");                  // Directed graph
var graph = new ISONGraph("name", directed: false); // Undirected graph
Node Operations
Method Description
AddNode(type, id, props) Add a node (type/id must not contain :)
GetNode(type, id) / GetNode(ref) Get a node
HasNode(type, id) / HasNode(ref) Check if exists
RemoveNode(type, id) Remove node and its edges
UpdateNode(type, id, props) Merge properties
Nodes(type?) Iterate nodes (optionally by type)
NodeCount(type?) Count nodes
NodeTypes() Get all node types
Edge Operations
Method Description
AddEdge(rel, src, tgt, props) Add an edge (undirected graphs auto-add the reverse)
GetEdge(rel, src, tgt) Get an edge
HasEdge(rel, src, tgt) Check if exists
RemoveEdge(rel, src, tgt) Remove edge (both directions when undirected)
Edges(rel?, source?, target?) Iterate edges with filters
EdgeCount(rel?) Count edges
EdgeTypes() Get all edge types
Traversal
Method Description
Neighbors(ref, rel?, dir) Get neighbors
MultiHop(start, rel?, hops, dir) N-hop traverse
MultiHopRange(start, rel?, min, max, dir) Range traverse
Traverse(start, pattern, filter?) Pattern traverse
Start(ref) Begin a fluent traversal
Path Finding
Method Description
ShortestPath(start, end, rel?, max, dir) BFS shortest path
AllPaths(start, end, rel?, max, dir) All simple paths (iterative DFS)
PathExists(start, end, rel?, max) Check reachability
Graph Analysis
Method Description
InDegree(ref) / OutDegree(ref) / Degree(ref) Degree counts
IsConnected() Check connectivity
HasCycle(rel?) Detect cycles (parent-edge tracking for undirected)
ConnectedComponents() All connected components
Serialization
Method Description
ToIson() / ToIsonl() Serialize to ISON / ISONL
ISONGraph.FromIson(text) / FromIsonl(text) Parse (strict; malformed input throws)
Save(path) / ISONGraph.Load(path) File I/O (format by extension)

Types

public readonly record struct NodeRef(string Type, string Id);

// Properties are string dictionaries
Dictionary<string, string> props;

public enum Direction { Out, In, Both }

Fluent Traversal

var companies = graph.Start(new("person", "1"))
    .Hop("KNOWS")
    .Hop("WORKS_AT")
    .Filter(n => n.Properties.GetValueOrDefault("industry") == "Tech")
    .Collect();

ISONQL Query Language

ISONQL is a declarative query language for ISONGraph, providing SQL-like queries for property graphs.

Basic Usage

using IsonGraph;

var graph = new ISONGraph("social");
graph.AddNode("person", "alice", new() { ["name"] = "Alice", ["age"] = "30", ["city"] = "NYC" });
graph.AddNode("person", "bob", new() { ["name"] = "Bob", ["age"] = "25", ["city"] = "LA" });
graph.AddEdge("KNOWS", new("person", "alice"), new("person", "bob"), new() { ["since"] = "2020" });

var engine = new QueryEngine(graph);

QueryResult result = engine.Execute("NODES person WHERE age > 25");
Console.WriteLine($"Found {result.Count} people");

Supported Query Types

-- NODES: select and filter nodes
NODES person
NODES person WHERE age > 25 AND city = NYC
NODES person WHERE city = LA OR city = Chicago
NODES person ORDER BY age DESC LIMIT 10 OFFSET 5
NODES person WHERE city = NYC RETURN name, age
NODES person(city="NYC")

-- EDGES: select and filter edges
EDGES KNOWS WHERE since > 2020 LIMIT 10

-- TRAVERSE: graph traversal
TRAVERSE person:alice -> KNOWS -> person
TRAVERSE person:bob <- KNOWS <- person
TRAVERSE person:alice -> KNOWS -> person MAX 3

-- PATH: shortest path
PATH person:alice TO person:bob VIA KNOWS MAX 5

-- COUNT and aggregations
COUNT person WHERE age > 20
SUM person.salary WHERE city = NYC
AVG person.age
MIN person.age
MAX person.age

Query Operators

Operator Description Example
=, == Equal name = Alice
!=, <> Not equal city != LA
> >= < <= Comparisons (numeric-aware) age >= 25
IN / NOT IN List membership city IN (NYC, LA)
CONTAINS Contains substring name CONTAINS ali
STARTS_WITH / ENDS_WITH Prefix / suffix name STARTS_WITH A
MATCHES Regex match (anchored at start) email MATCHES ^[a-z]+@
EXISTS field / field EXISTS Field exists EXISTS email
NOT EXISTS field / field NOT EXISTS Field missing phone NOT EXISTS

AND binds tighter than OR: a AND b OR c evaluates as (a AND b) OR c.

Fluent Query Builder

var result = engine.Match("person")
    .Where("age", ">", 25)
    .Where("city", "=", "NYC")
    .OrderBy("age", "DESC")
    .Limit(10)
    .Execute();

long count = engine.Match("person").Where("city", "=", "NYC").Count();

var edges = engine.MatchEdges("KNOWS")
    .Where("since", ">", 2020)
    .Limit(10)
    .Execute();

Unknown operator strings in Where() throw ArgumentException instead of silently matching everything.


Schema Validation (Graphantic)

using IsonGraph;

var person = new NodeType("person")
    .Id(new IntField())
    .Field("name", new StringField().Required().Max(100))
    .Field("age", new IntField().Min(0).Max(150))
    .Field("email", new StringField().Email())
    .Field("status", new StringField().Enum("active", "inactive").Default("active"));

var company = new NodeType("company")
    .Id(new IntField())
    .Field("name", new StringField().Required());

var knows = new EdgeType("KNOWS")
    .FromNode(person)
    .ToNode(person)
    .NoSelfLoop()
    .Unique();

var worksAt = new EdgeType("WORKS_AT")
    .FromNode(person)
    .ToNode(company)
    .Cardinality(Cardinality.ManyToOne);

var reportsTo = new EdgeType("REPORTS_TO")
    .FromNode(person)
    .ToNode(person)
    .Acyclic()                      // per-relationship-type DAG check
    .Field("since", new IntField().Required());

var schema = new GraphSchema("social")
    .NodeTypes(person, company)
    .EdgeTypes(knows, worksAt, reportsTo)
    .NoOrphans();

ValidationResult result = schema.Validate(graph);
if (!result.Valid)
{
    foreach (var error in result.Errors)
        Console.WriteLine($"[{error.Location}] {error.Code}: {error.Message}");
}

Field validators: StringField (Required, Min, Max, Pattern, Email, Enum, Default), IntField, FloatField, BoolField, and RefField (To). Declared defaults are written into missing properties during validation. Edge constraints: NoSelfLoop, Unique, Acyclic (checked per relationship type), Bidirectional, and Cardinality (OneToOne, OneToMany, ManyToOne, ManyToMany). Graph constraints: Connected, NoOrphans, MaxDepth, and custom Constraint callbacks.


Visualization

Deterministic force-directed layout plus SVG and self-contained interactive HTML rendering. The PRNG is a portable 32-bit LCG, so the same graph, size, and seed produce bit-identical coordinates across every ISONGraph language port.

using IsonGraph;

var layout = Viz.ComputeLayout(graph, width: 900, height: 600, seed: 42);

string svg = Viz.RenderSvg(graph, layout, title: "My Graph");
string html = Viz.RenderHtml(graph, layout);   // hover tooltips, zoom, pan

Viz.Save(graph, "graph.svg");
Viz.Save(graph, "graph.html");

// Radius-aware collision pass: keep node centers >= (rA + rB) * spacing apart
var radii = graph.Nodes().ToDictionary(n => n.Ref, _ => 24.0);
var spaced = Viz.ComputeLayout(graph, radii: radii, spacing: 1.2);

Exception Handling

try
{
    graph.AddNode("person", "1");
    graph.AddNode("person", "1");  // throws DuplicateNodeError
}
catch (DuplicateNodeError e)
{
    Console.Error.WriteLine($"Duplicate node: {e.Message}");
}

try
{
    var node = graph.GetNode("person", "999");  // throws NodeNotFoundError
}
catch (NodeNotFoundError e)
{
    Console.Error.WriteLine($"Node not found: {e.Message}");
}

// AddNode("bad:type", ...) throws ArgumentException (':' is reserved)
// FromIson / FromIsonl throw GraphError on malformed input - never a silent skip

ISON Format

ISONGraph uses a token-efficient serialization format:

nodes.person
id name age
1 Alice 30
2 Bob 25

edges.KNOWS
source target since
:person:1 :person:2 2020

ISONL (Line-Oriented Format)

For streaming/appending:

nodes.person|id name|1 Alice
nodes.person|id name age|2 Bob 25
edges.KNOWS|source target since|:person:1 :person:2 2020

Values containing a space, |, ", a newline, or the empty string are double-quoted with \", \n, and \\ escapes; round-trips are lossless.


Requirements

  • .NET 8.0 SDK or later
  • No external dependencies (base class library only)

Running Tests

dotnet test

License

MIT License - see LICENSE for details.

Author

Mahesh Vaikri

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 84 7/23/2026