Skip to content

Core Concepts

Understanding the Property Graph Model used by GrafitoDB.

What is a Property Graph?

A Property Graph is a graph data model where:

  • Nodes (also called vertices) represent entities
  • Relationships (also called edges) connect nodes and represent how entities relate
  • Both nodes and relationships can have properties (key-value pairs)
  • Nodes can have labels that categorize them

Nodes

Nodes are the primary entities in your graph.

Labels

Nodes can have multiple labels, similar to how an object can implement multiple interfaces:

# Alice is both a Person and an Employee
alice = db.create_node(
    labels=['Person', 'Employee'],
    properties={'name': 'Alice'}
)

Common use cases for multiple labels: - Person + Employee + Manager (role hierarchy) - Product + Featured (categorization) - User + Admin (permissions)

Properties

Properties are JSON-serializable key-value pairs:

node = db.create_node(
    labels=['Person'],
    properties={
        'name': 'Alice',
        'age': 30,
        'active': True,
        'tags': ['developer', 'python'],
        'metadata': {'joined': '2024-01-15'}
    }
)

Supported property types: - int - Integer numbers - float - Floating point numbers - str - Strings - bool - Boolean values - list - Arrays (must contain supported types) - dict - Maps/objects (keys must be strings) - None - Null values

Temporal types (stored as ISO 8601 strings): - date - ISO date: '2024-01-15' - datetime - ISO datetime: '2024-01-15T10:30:00' - time - ISO time: '10:30:00' - duration - ISO duration: 'P1D' (1 day)

Relationships

Relationships are directed connections between nodes.

Direction

Relationships have a direction (from source to target):

# Alice KNOWS Bob (direction matters)
db.create_relationship(alice.id, bob.id, 'KNOWS')

# Bob is not necessarily knowing Alice in this model

Type

Relationship types describe the nature of the connection:

db.create_relationship(alice.id, company.id, 'WORKS_AT')
db.create_relationship(alice.id, bob.id, 'KNOWS')
db.create_relationship(alice.id, project.id, 'MANAGES')

Properties

Like nodes, relationships can have properties:

db.create_relationship(
    alice.id, company.id, 'WORKS_AT',
    properties={
        'since': 2020,
        'position': 'Senior Engineer',
        'department': 'Engineering'
    }
)

Graph Patterns

Graph patterns describe the structure you're looking for:

Basic Pattern

(a:Person)-[:KNOWS]->(b:Person)

This pattern matches: - A node labeled Person (bound to variable a) - A KNOWS relationship (direction: ab) - Another node labeled Person (bound to variable b)

Variable-Length Patterns

Match paths of variable length:

# 1 to 3 hops
(a:Person)-[:KNOWS*1..3]->(b:Person)

# Any number of hops (uses default max limit)
(a:Person)-[:KNOWS*..]->(b:Person)

Multiple Patterns

Combine multiple patterns in one query:

MATCH
  (a:Person)-[:KNOWS]->(b:Person),
  (b)-[:WORKS_AT]->(c:Company)
RETURN a.name, b.name, c.name

Traversal

Graph traversal means navigating through the graph by following relationships.

Direction

When traversing, you can specify direction:

# Outgoing: Alice → ?
outgoing = db.get_neighbors(alice.id, direction='outgoing')

# Incoming: ? → Alice
incoming = db.get_neighbors(alice.id, direction='incoming')

# Both directions
all_neighbors = db.get_neighbors(alice.id, direction='both')

Path Finding

Find paths between nodes:

# Shortest path (BFS algorithm)
path = db.find_shortest_path(alice.id, bob.id)

# Any path with depth limit (DFS algorithm)
path = db.find_path(alice.id, bob.id, max_depth=5)

Indexes and Constraints

Property Indexes

Speed up queries on frequently accessed properties:

# Create index on Person.name
db.create_node_index('Person', 'name')

# Or via Cypher
db.execute("CREATE INDEX FOR (n:Person) ON (n.name)")

Constraints

Enforce data integrity:

# Unique constraint
db.execute("CREATE CONSTRAINT FOR (n:User) REQUIRE n.email IS UNIQUE")

# Type constraint
db.execute("CREATE CONSTRAINT FOR (n:Person) REQUIRE n.age IS INTEGER")

# Existence constraint
db.execute("CREATE CONSTRAINT FOR (n:Person) REQUIRE n.name IS NOT NULL")

Storage Model

GrafitoDB uses a normalized SQLite schema:

-- Nodes table
CREATE TABLE nodes (
    id INTEGER PRIMARY KEY,
    properties TEXT,  -- JSON
    created_at REAL
);

-- Labels (normalized)
CREATE TABLE labels (
    id INTEGER PRIMARY KEY,
    name TEXT UNIQUE
);

-- Node-Label junction
CREATE TABLE node_labels (
    node_id INTEGER,
    label_id INTEGER
);

-- Relationships
CREATE TABLE relationships (
    id INTEGER PRIMARY KEY,
    source_node_id INTEGER,
    target_node_id INTEGER,
    type TEXT,
    properties TEXT  -- JSON
);

Benefits of this design: - Efficient queries via indexes on labels and types - Flexible properties via JSON storage - ACID compliance via SQLite transactions - Small footprint suitable for embedding

Comparison with Other Models

vs. Relational (SQL)

Aspect Relational Property Graph
Schema Rigid tables Flexible labels/properties
Relationships Foreign keys First-class entities
Traversal JOINs Direct navigation
Best for Structured data Connected data

vs. Document (MongoDB)

Aspect Document Property Graph
Structure Nested documents Nodes + edges
Relationships Embedded references Native connections
Queries Document-based Pattern-based
Best for Content management Relationship analysis

vs. RDF (Triple Stores)

Aspect RDF Property Graph
Model Subject-Predicate-Object Nodes + labeled relationships
Schema Ontologies Labels + properties
Standards W3C standards De facto (Neo4j)
Best for Semantic web Application graphs

Next Steps