Mappings¶
This page explains the building blocks of runic.orm — Node, Edge, Field,
object states, dirty tracking, and the identity map.
Node and Edge¶
Every graph entity inherits from either Node
or Edge.
from runic.orm import Edge, Field, Node
class Person(Node, labels=["Person"]):
id: str = Field(primary_key=True, generated=True)
name: str
email: str = Field(index=True, unique=True)
class InvitationEdge(Edge, type="INVITED_TO"):
role: str
status: str
invited_at: str
labels controls which FalkorDB labels are applied. Multi-label nodes implement polymorphic hierarchies — see Relationships.
primary_label (optional) is the label used in MATCH predicates
when the node has more than one label:
class Location(Node, labels=["Location"], primary_label="Location"):
id: str
title: str
class Country(Location, labels=["Location", "Country"], primary_label="Location"):
iso_code: str = Field(unique=True)
See also
- examples/orm/01_simple_crud.py
Defines a
NodewithFielddescriptors and walks through all object states in one file.
Field and Relation descriptors¶
Properties and relationships are declared with separate functions for a clean separation of concerns:
Field()— scalar properties, index hints, and constraints.Relation()— graph relationships (edges).
Both return FieldDescriptor typed as
Any, so name: str = Field() is accepted by type checkers without
error.
Field parameters
Parameter |
Type |
Description |
|---|---|---|
|
|
Python default value (evaluated lazily via |
|
|
Create a |
|
|
|
|
|
Unique constraint |
|
|
Validated on save; raises |
|
Custom encode/decode; omit for |
|
|
|
FalkorDB assigns the node ID on |
|
|
Store via FalkorDB’s |
Relation parameters
Parameter |
Type |
Description |
|---|---|---|
|
|
Edge-type string (required) |
|
|
|
|
|
Entity class (or forward-reference string) for the other end (required) |
|
|
Optional |
|
|
Delay relationship loading (default |
|
|
Auto-add related entities when the owning entity is added to a session |
|
|
Default value (defaults to |
Object states¶
Each entity lives in exactly one state at any time, mirroring SQLAlchemy:
State |
When the object enters it |
|---|---|
Transient |
Created with |
Pending |
After |
Persistent |
Loaded from the graph, or after the first successful |
Deleted |
After |
Detached |
After |
Dirty tracking¶
Two private flags drive query selection:
_new—Trueuntil the first successful flush. Mapper emitsCREATEwhen this is true._dirty—Truewhen any field is written on a persistent entity. Mapper emitsMERGE … SETwhen this is true.
The descriptor __set__ sets _dirty = True automatically. The Session clears
_dirty after a successful flush().
Identity map¶
The Session keeps one Python instance per (EntityClass, primary_key) pair.
Calling session.get(Person, "alice") twice within the same session returns
the same object.
with Session(graph) as session:
a = session.get(Person, "alice")
b = session.get(Person, "alice")
assert a is b # True
Repository reads also register entities in the identity map.
See also
- examples/orm/02_polymorphic_locations.py
Multi-label nodes (
Location → Country, City),primary_label, and polymorphic repository queries.
Type converters¶
The ORM assigns converters automatically for well-known annotation types —
no converter= argument needed:
Annotation type |
Converter assigned automatically |
|---|---|
|
|
|
|
|
|
|
from datetime import datetime
from enum import StrEnum
from runic.orm import Field, GeoLocation, Node, Vector
class Status(StrEnum):
ACTIVE = "active"
ARCHIVED = "archived"
class Place(Node, labels=["Place"]):
id: str
status: Status # EnumConverter auto-assigned
created_at: datetime # DatetimeConverter auto-assigned
embedding: Vector = Field(index_type="VECTOR") # VectorConverter
location: GeoLocation # GeoLocationConverter
An explicit converter= always takes precedence over auto-assignment.
Interned strings
Use interned=True to store a string property via FalkorDB’s intern()
function, which deduplicates the value across the database. Useful for
high-cardinality-but-low-variety fields like country names or status codes:
class Person(Node, labels=["Person"]):
id: str = Field()
country: str = Field(interned=True)
Custom converters
Implement TypeConverter (to_graph /
from_graph) for any type not covered above. Set cypher_fn on the
converter class to wrap the Cypher parameter with a FalkorDB function:
from runic.orm import TypeConverter
class MyConverter(TypeConverter):
cypher_fn = "myFunc" # emits myFunc($field) in Cypher
def to_graph(self, value): ...
def from_graph(self, value): ...
See also
- examples/orm/06_native_types.py
Vector,GeoLocation, interned strings,datetimeandEnumauto-converters in action.
Metadata registry¶
All Node and Edge subclasses are registered automatically in the
global metadata singleton when the class is
defined. Forward references in target= strings are resolved at import
time.
from runic.orm import metadata
for node_meta in metadata.all_nodes():
print(node_meta.cls.__name__, node_meta.labels)