Getting started
Defining models
Nodes
Subclass Node and declare properties as type annotations:
Labels default to the class name. To set custom labels:
Relationships
Subclass Relationship with a __type__ attribute:
import pylpg.relationship
class Knows(pylpg.relationship.Relationship):
__type__ = "KNOWS"
since: str | None = None
Relationship descriptors
Attach traversal capabilities to nodes using descriptors:
class Person(pylpg.node.Node):
name: str
friends = pylpg.relationship.RelationshipTo(Knows)
known_by = pylpg.relationship.RelationshipFrom(Knows)
Available descriptors:
RelationshipTo— outgoing relationshipsRelationshipFrom— incoming relationshipsRelationshipUndirected— undirected relationships
Connecting to a database
Neo4j
import pylpg.backend.neo4j
backend = pylpg.backend.neo4j.Neo4jBackend(
hostname="localhost",
port=7687,
database="neo4j",
username="neo4j",
password="password",
protocol="bolt",
)
FalkorDB
import pylpg.backend.falkordb
backend = pylpg.backend.falkordb.FalkorDBBackend(
hostname="localhost",
port=6379,
database="default",
)
FalkorDBLite (embedded)
No server required:
import pylpg.backend.falkordblite
backend = pylpg.backend.falkordblite.FalkorDBLiteBackend(
path="/tmp/my_graph.db",
database="default",
)
Sessions
A session manages the connection between your Python objects and the database. Use it as a context manager:
import pylpg.session
with pylpg.session.Session(backend) as session:
alice = Person(name="Alice")
session.save(alice)
When a node is saved, it becomes bound to the session. This allows traversal and relationship operations to work without explicitly passing the session around.
CRUD operations
Creating nodes
Updating nodes
Deleting nodes
Creating relationships
Two approaches:
Direct instantiation:
relationship = Knows(source=alice, target=bob, since="2024")
session.save(relationship) # auto-saves unsaved source/target nodes
Via descriptor:
Traversal
Batch operations
Save multiple items at once using session.save(list):
Batch save also works with relationships and automatically saves unsaved referenced nodes:
relationships = [
Knows(source=alice, target=bob),
Knows(source=alice, target=carol),
]
session.save(relationships)
Each backend implements batch operations optimally:
- Neo4j — uses
UNWINDqueries in a single transaction for atomicity and performance - FalkorDB / FalkorDBLite — uses individual queries (FalkorDB does not support multi-query transactions)
Raw queries
Execute Cypher queries directly:
results = session.execute_query(
"MATCH (n:Person) WHERE n.age > $min_age RETURN n",
parameters={"min_age": 25},
)
Use resolve_nodes=True to automatically hydrate driver node objects into Python instances:
results = session.execute_query(
"MATCH (n:Person) RETURN n",
resolve_nodes=True,
)
for row in results:
person = row["n"] # a Person instance
print(person.name)
Property types
Allowed property types are:
- Primitives:
str,int,float,bool,None - Lists of primitives:
list[str],list[int], etc. - Unions of the above:
str | None,int | float, etc.
Properties with default values are optional. Properties without defaults are required: