Relationships API
Relationships connect nodes and represent how entities relate to each other.
Creating Relationships
Basic Relationship
# Create a directed relationship from Alice to Bob
knows = db.create_relationship(
source_id=alice.id, # From
target_id=bob.id, # To
rel_type='KNOWS' # Relationship type
)
print(f"Created relationship: {knows.id}")
print(f"Type: {knows.type}") # 'KNOWS'
print(f"From: {knows.source_id} -> To: {knows.target_id}")
Relationship with Properties
# Relationships can have properties just like nodes
works_at = db.create_relationship(
source_id=alice.id,
target_id=company.id,
rel_type='WORKS_AT',
properties={
'since': 2020,
'position': 'Senior Engineer',
'department': 'Engineering',
'salary_band': 'L5'
}
)
Common Relationship Types
| Type | Description | Example Properties |
|---|---|---|
KNOWS |
Social connection | since, strength |
WORKS_AT |
Employment | since, position, department |
MANAGES |
Management | since, level |
CREATED |
Authorship | date, role |
BELONGS_TO |
Categorization | priority, weight |
LOCATED_IN |
Geography | address_type |
Retrieving Relationships
Get by ID
rel = db.get_relationship(rel_id)
if rel:
print(f"Type: {rel.type}")
print(f"Properties: {rel.properties}")
print(f"Created: {rel.created_at}")
Match Relationships
# Find all relationships of a type
all_knows = db.match_relationships(rel_type='KNOWS')
# Find relationships from a specific node
from_alice = db.match_relationships(source_id=alice.id)
# Find relationships to a specific node
to_bob = db.match_relationships(target_id=bob.id)
# Combine filters
alice_to_bob = db.match_relationships(
source_id=alice.id,
target_id=bob.id,
rel_type='KNOWS'
)
Get Neighbors
The most common pattern is getting connected nodes:
# Get all outgoing connections
friends = db.get_neighbors(
node_id=alice.id,
direction='outgoing',
rel_type='KNOWS'
)
# Get all incoming connections (who knows Alice?)
followers = db.get_neighbors(
node_id=alice.id,
direction='incoming',
rel_type='KNOWS'
)
# Get connections in both directions
all_connected = db.get_neighbors(
node_id=alice.id,
direction='both'
)
# Get all neighbors regardless of relationship type
all_neighbors = db.get_neighbors(alice.id, direction='outgoing')
Deleting Relationships
# Delete a specific relationship
db.delete_relationship(rel_id)
# Note: Deleting a node automatically deletes all its relationships
Relationship Object
rel = db.get_relationship(rel_id)
rel.id # Unique identifier (int)
rel.type # Relationship type (str)
rel.source_id # Source node ID (int)
rel.target_id # Target node ID (int)
rel.properties # Dictionary of properties (dict)
rel.created_at # Creation timestamp (datetime)
rel.uri # Optional URI identifier (str | None)
Direction Matters
Relationships are directed. Remember the arrow direction:
# Alice WORKS_AT Company (correct direction)
db.create_relationship(alice.id, company.id, 'WORKS_AT')
# This is different! Company WORKS_AT Alice
db.create_relationship(company.id, alice.id, 'WORKS_AT')
# For bidirectional relationships, create two
db.create_relationship(alice.id, bob.id, 'KNOWS')
db.create_relationship(bob.id, alice.id, 'KNOWS') # Mutual
Advanced Patterns
Creating Linked Structures
def create_reporting_chain(db, manager, employees):
"""Create MANAGES relationships from manager to employees."""
with db:
for emp in employees:
db.create_relationship(
source_id=manager.id,
target_id=emp.id,
rel_type='MANAGES',
properties={'since': datetime.now().year}
)
Updating Relationship Properties
Currently, relationships are updated via Cypher:
# Update relationship properties
db.execute("""
MATCH (a:Person {name: 'Alice'})-[r:WORKS_AT]->(c:Company)
SET r.position = 'Staff Engineer'
""")
Finding Relationship Paths
# Find all paths between two nodes with specific relationship
results = db.execute("""
MATCH path = (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b:Person {name: 'Carol'})
RETURN path
""")
Best Practices
- Use Verbose Type Names:
WORKS_ATis clearer thanWORKS - Consider Direction: Always think about the natural direction
- Keep Properties Lean: Don't duplicate node data in relationships
- Use Transactions: For consistency when creating node + relationship
Common Workflows
Social Network
def make_friends(db, person1_id, person2_id):
"""Create bidirectional friendship."""
with db:
timestamp = datetime.now().isoformat()
db.create_relationship(
person1_id, person2_id, 'FRIEND',
properties={'since': timestamp, 'strength': 'strong'}
)
db.create_relationship(
person2_id, person1_id, 'FRIEND',
properties={'since': timestamp, 'strength': 'strong'}
)
Content Graph
def link_content(db, article_id, tag_ids):
"""Link an article to multiple tags."""
with db:
for tag_id in tag_ids:
db.create_relationship(
article_id, tag_id, 'TAGGED_WITH',
properties={'auto': False}
)
Organization Hierarchy
def create_department_structure(db, dept_data):
"""Create department with manager and employees."""
with db:
dept = db.create_node(
labels=['Department'],
properties={'name': dept_data['name']}
)
# Link manager
db.create_relationship(
dept_data['manager_id'], dept.id, 'MANAGES_DEPT'
)
# Link employees
for emp_id in dept_data['employee_ids']:
db.create_relationship(
emp_id, dept.id, 'BELONGS_TO',
properties={'type': 'member'}
)
return dept
Error Handling
from grafito.exceptions import RelationshipError, NodeNotFoundError
try:
rel = db.create_relationship(
source_id=9999, # Non-existent node
target_id=bob.id,
rel_type='KNOWS'
)
except NodeNotFoundError:
print("One or both nodes don't exist")
except RelationshipError as e:
print(f"Relationship error: {e}")