Skip to main content

Object-Graph Mapping for FalkorDB with Spring Data-inspired patterns

Project description

FalkorDB Python ORM

Object-Graph Mapping for FalkorDB with Spring Data-inspired patterns

Python Version License FalkorDB PyPI

FalkorDB Python ORM provides intuitive, annotation-based object-graph mapping for FalkorDB, enabling developers to work with graph databases using familiar ORM patterns inspired by Spring Data.

๐ŸŽฏ Project Status

Production Ready! โœ… The FalkorDB Python ORM is fully implemented, tested, and documented.

๐Ÿš€ Features

Core Capabilities

  • ๐Ÿท๏ธ Decorator-based Entity Mapping: Use @node and property decorators for intuitive object-graph mapping
  • ๐Ÿ“ฆ Repository Pattern: Complete CRUD operations with type-safe Repository[T]
  • ๐Ÿ”‘ ID Management: Auto-generated or manual IDs with generated_id()
  • ๐Ÿ”„ Type Conversion: Built-in converters for common Python types
  • ๐ŸŽฏ Multiple Labels: Support for multiple node labels per entity
  • ๐ŸŽจ Type Safety: Full type hints and generic repositories for IDE support

Query Features

  • ๐Ÿ” Derived Query Methods: Auto-generate queries from method names (e.g., find_by_name(), count_by_age_greater_than())
  • ๐Ÿ“Š Comparison Operators: 14+ operators (equals, greater_than, less_than, between, in, etc.)
  • ๐Ÿ”— Logical Operators: AND/OR combinations in queries
  • ๐Ÿ“ String Operations: CONTAINS, STARTS WITH, ENDS WITH, regex patterns
  • ๐Ÿ“Š Sorting & Limiting: ORDER BY multiple fields, first/top_N result limiting
  • โšก Query Caching: Automatic QuerySpec caching for performance
  • ๐Ÿ“ Custom Cypher Queries: @query decorator with parameter binding
  • ๐Ÿ“Š Aggregation Methods: Built-in sum(), avg(), min(), max() functions

Relationships

  • ๐Ÿ”— Relationship Declaration: relationship() decorator with full type support
  • ๐Ÿ’ค Lazy Loading: Relationships loaded on-demand with automatic caching
  • โšก Eager Loading: Solve N+1 queries with fetch=['rel1', 'rel2'] parameter
  • ๐Ÿ”„ Cascade Operations: Auto-save related entities with cascade=True
  • โ†”๏ธ Bidirectional Relationships: Full support for complex relationship graphs
  • ๐Ÿ”™ Reverse Relationships: direction='INCOMING' for inverse traversal
  • ๐Ÿ” Circular Reference Handling: Safe handling of circular relationships

Async Support

  • โšก AsyncRepository: Full async/await support for all CRUD operations
  • ๐Ÿ”„ Async Relationships: Async lazy loading with AsyncLazyList and AsyncLazySingle
  • ๐Ÿ“Š Async Derived Queries: Auto-generated async query methods
  • ๐ŸŒ Framework Ready: Perfect for FastAPI, aiohttp, and async Python applications

Advanced Features (v1.1.0)

  • โšก Transaction Support: Context managers with identity map and change tracking
  • ๐Ÿ—‚๏ธ Index Management: @indexed and @unique decorators with schema validation
  • ๐Ÿ“„ Pagination: Full pagination with sorting and navigation (Pageable, Page[T])
  • ๐Ÿ”„ Relationship Updates: Automatic deletion of old edges when relationships change

Security Features (v1.2.0 NEW! ๐Ÿ”’)

  • ๐Ÿ” Role-Based Access Control (RBAC): Enterprise-grade security with fine-grained permissions
  • ๐Ÿ‘ฅ User & Role Management: Built-in user, role, and privilege entities
  • ๐Ÿ›ก๏ธ Declarative Security: @secured decorator for entity-level access control
  • ๐Ÿ”‘ Property-Level Security: Control access to individual properties
  • ๐Ÿ”’ SecureSession: Security-aware sessions with automatic permission enforcement
  • ๐Ÿ‘จโ€๐Ÿ’ผ Admin API: Comprehensive RBACManager for runtime administration
  • ๐Ÿ“ Audit Logging: Complete audit trail for all security operations
  • ๐ŸŽญ Impersonation: Test permissions safely with context managers
  • โšก Performance: <10ms overhead with intelligent privilege caching

Production Features

  • ๐Ÿ“š Comprehensive Documentation: Complete API reference and migration guides
  • ๐Ÿง  Enhanced Exceptions: Contextual error messages with structured error information
  • ๐Ÿš€ CI/CD Workflows: Automated testing, linting, and publishing
  • ๐Ÿ’พ Memory Optimization: Interned strings for repeated values with @interned decorator
  • ๐Ÿงช Integration Tests: Full end-to-end tests with real FalkorDB

๐Ÿ“‹ Future Enhancements (Optional)

  • ๐Ÿ“ฆ Migration System: Schema version management and migrations
  • ๐Ÿ” Query Result Caching: Result caching for performance
  • โš™๏ธ Batch Optimization: UNWIND-based bulk operations

๐Ÿ“œ Usage

Entity Definition

from falkordb_orm import node, property, relationship, Repository
from typing import Optional, List

@node(labels=["Person", "Individual"])
class Person:
    id: Optional[int] = None
    name: str = property("full_name")  # Maps to 'full_name' in graph
    email: str
    age: int
    
    friends: List["Person"] = relationship(type="KNOWS", direction="OUTGOING")
    company: Optional["Company"] = relationship(type="WORKS_FOR", direction="OUTGOING")

@node("Company")
class Company:
    id: Optional[int] = None
    name: str
    employees: List[Person] = relationship(type="WORKS_FOR", direction="INCOMING")

Repository Usage

from falkordb import FalkorDB

# Connect to FalkorDB
db = FalkorDB(host='localhost', port=6379)
graph = db.select_graph('social')

# Create repository
repo = Repository(graph, Person)

# Create and save
person = Person(name="Alice", email="alice@example.com", age=25)
saved = repo.save(person)

# Derived queries (auto-implemented)
adults = repo.find_by_age_greater_than(18)
alice = repo.find_by_name("Alice")
count = repo.count_by_age(25)
exists = repo.exists_by_email("alice@example.com")

# Eager loading relationships (prevents N+1 queries)
person_with_friends = repo.find_by_id(1, fetch=["friends", "company"])
all_with_friends = repo.find_all(fetch=["friends"])  # Single query!

# Cascade save (auto-saves related entities)
company = Company(name="Acme Corp")
employee = Employee(name="Bob", company=company)
repo.save(employee)  # Company automatically saved!

Async Usage (Phase 5)

import asyncio
from falkordb.asyncio import FalkorDB
from falkordb_orm import node, AsyncRepository
from typing import Optional

@node("Person")
class Person:
    id: Optional[int] = None
    name: str
    age: int

async def main():
    # Connect to FalkorDB with async client
    from redis.asyncio import BlockingConnectionPool
    pool = BlockingConnectionPool(max_connections=16, timeout=None, decode_responses=True)
    db = FalkorDB(connection_pool=pool)
    graph = db.select_graph('social')
    
    # Create async repository
    repo = AsyncRepository(graph, Person)
    
    # All operations are async
    person = Person(name="Alice", age=25)
    saved = await repo.save(person)
    
    # Async derived queries
    adults = await repo.find_by_age_greater_than(18)
    count = await repo.count()
    
    # Async eager loading
    person_with_friends = await repo.find_by_id(1, fetch=["friends"])
    
    print(f"Found {count} people")

asyncio.run(main())

Transaction Support (v1.1.0 NEW!)

from falkordb_orm import Session

# Use session for transactions with identity map
with Session(graph) as session:
    # Get entity (cached in identity map)
    person = session.get(Person, 1)
    person.age = 31
    session._dirty.add(person)  # Mark as modified
    
    # Add new entities
    new_person = Person(name="Bob", age=25)
    session.add(new_person)
    
    # Auto-commit on success, auto-rollback on error
    session.commit()

Index Management (v1.1.0 NEW!)

from falkordb_orm import node, indexed, unique, IndexManager

@node("User")
class User:
    email: str = unique(required=True)       # Unique constraint
    age: int = indexed()                      # Regular index
    bio: str = indexed(index_type="FULLTEXT") # Full-text search

# Create indexes
manager = IndexManager(graph)
manager.create_indexes(User, if_not_exists=True)

# Schema validation
from falkordb_orm import SchemaManager
schema_manager = SchemaManager(graph)
result = schema_manager.validate_schema([User, Product])
if not result.is_valid:
    schema_manager.sync_schema([User, Product])

Pagination (v1.1.0 NEW!)

from falkordb_orm import Pageable

# Create pageable (page 0, 10 items, sorted by age)
pageable = Pageable(page=0, size=10, sort_by="age", direction="ASC")

# Get paginated results
page = repo.find_all_paginated(pageable)

print(f"Page {page.page_number + 1} of {page.total_pages}")
print(f"Total: {page.total_elements} items")

for person in page.content:
    print(person.name)

# Navigate pages
if page.has_next():
    next_page = repo.find_all_paginated(pageable.next())

Getting Started

For a complete walkthrough, see QUICKSTART.md.

Security Quick Start (v1.2.0)

Define a secured entity:

from falkordb_orm import node, generated_id
from falkordb_orm.security import secured

@node("Person")
@secured(
    read=["reader", "admin"],
    write=["editor", "admin"],
    deny_read_properties={
        "ssn": ["*"],   # Nobody can read
        "salary": ["reader"]  # Readers cannot read salary
    }
)
class Person:
    id: int | None = generated_id()
    name: str
    email: str
    ssn: str
    salary: float

Create roles, users, and grant privileges:

from datetime import datetime
from falkordb_orm.repository import Repository
from falkordb_orm.security import Role, User, SecurityPolicy

role_repo = Repository(graph, Role)
user_repo = Repository(graph, User)

reader = Role(name="reader", description="Read-only", created_at=datetime.now())
editor = Role(name="editor", description="Edit", created_at=datetime.now())
role_repo.save(reader)
role_repo.save(editor)

alice = User(username="alice", email="alice@example.com", created_at=datetime.now())
alice.roles = [reader]
user_repo.save(alice)

policy = SecurityPolicy(graph)
policy.grant("READ", "Person", to="reader")
policy.grant("WRITE", "Person", to="editor")
policy.deny("READ", "Person.ssn", to="reader")

Use SecureSession:

from falkordb_orm.security import SecureSession

session = SecureSession(graph, alice)
repo = session.get_repository(Person)

p = repo.find_by_id(1)
print(p.name)     # โœ“ Allowed
print(p.ssn)      # None (filtered)

Admin API example:

from falkordb_orm.security import RBACManager

admin_session = SecureSession(graph, admin_user)
rbac = RBACManager(graph, admin_session.security_context)

rbac.create_user("bob", "bob@example.com", roles=["editor"])  # create
rbac.assign_role("alice", "editor")                              # assign
rbac.grant_privilege("editor", "WRITE", "NODE", "Document")     # grant
logs = rbac.query_audit_logs(limit=10)                            # audit

Custom Queries (Phase 4)

from falkordb_orm import query

class PersonRepository(Repository[Person]):
    @query(
        "MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.name = $name RETURN f",
        returns=Person
    )
    def find_friends(self, name: str) -> List[Person]:
        pass
    
    @query(
        "MATCH (p:Person) WHERE p.age BETWEEN $min AND $max RETURN p",
        returns=Person
    )
    def find_by_age_range(self, min: int, max: int) -> List[Person]:
        pass

๐Ÿ“‹ Comparison with Current Approach

Before (Raw Cypher)

from falkordb import FalkorDB

db = FalkorDB(host='localhost', port=6379)
g = db.select_graph('social')

# Create manually
g.query('''
    CREATE (p:Person {name: $name, email: $email, age: $age})
    RETURN p, id(p)
''', {'name': 'Alice', 'email': 'alice@example.com', 'age': 25})

# Query manually
result = g.query('''
    MATCH (p:Person) WHERE p.age > $min_age RETURN p
''', {'min_age': 18})

# Manual result processing
for record in result.result_set:
    person = record[0]
    print(person.properties['name'])

After (ORM - Planned)

from falkordb import FalkorDB
from falkordb_orm import node, Repository

@node("Person")
class Person:
    id: Optional[int] = None
    name: str
    email: str
    age: int

db = FalkorDB(host='localhost', port=6379)
graph = db.select_graph('social')
repo = Repository(graph, Person)

# Create with ORM
person = Person(name="Alice", email="alice@example.com", age=25)
saved = repo.save(person)

# Query with derived method
adults = repo.find_by_age_greater_than(18)

# Automatic object mapping
for person in adults:
    print(person.name)

๐Ÿ“š Documentation

๐Ÿค Comparison with Spring Data FalkorDB

This project is inspired by Spring Data FalkorDB, bringing similar patterns to Python:

Feature Spring Data FalkorDB falkordb-orm (v1.2.0)
Core Mapping
Entity Mapping @Node annotation @node decorator
Property Mapping @Property property() function
Relationships @Relationship relationship() function
ID Generation @GeneratedValue generated_id()
Repository Pattern
Repository FalkorDBRepository<T, ID> Repository[T]
Query Methods Method name derivation Method name derivation
Custom Queries @Query annotation @query decorator
Pagination Pageable, Page<T> Pageable, Page[T] โœ…
Transactions & Sessions
Transactions @Transactional Session (unit of work) โœ…
Identity Map โœ… โœ… (via Session)
Change Tracking โœ… โœ… (dirty checking)
Advanced Features
Async Support โŒ โœ… AsyncRepository
Index Management Manual @indexed, @unique โœ…
Schema Validation โŒ โœ… SchemaManager
Lazy Loading โœ… โœ… (lazy/eager)
Cascade Operations โœ… โœ… (cascade save)
Security (v1.2.0)
RBAC โŒ โœ… Role-Based Access Control
Entity Security โŒ โœ… @secured decorator
Property Security โŒ โœ… Property-level controls
Secure Sessions โŒ โœ… SecureSession
Admin API โŒ โœ… RBACManager
Audit Logging โŒ โœ… Complete audit trail
User/Role Management โŒ โœ… Built-in entities
Impersonation โŒ โœ… Testing support
Language & Ecosystem
Language Java Python 3.9+
Type Safety Java generics Python type hints
Framework Integration Spring Boot FastAPI, Django, Flask

๐ŸŽฏ Goals

  1. Developer Productivity: Reduce boilerplate code and manual Cypher writing
  2. Type Safety: Leverage Python type hints for better IDE support
  3. Familiarity: Use patterns developers know from SQLAlchemy, Django ORM, Spring Data
  4. Performance: Optimize queries, support batching, implement lazy loading
  5. Flexibility: Support both simple and complex use cases

๐Ÿ”ง Technology Stack

  • Python: 3.8+
  • FalkorDB Client: falkordb-py
  • Type Hints: For IDE support and validation
  • Decorators: For entity and query definition
  • Generics: For type-safe repositories

๐Ÿ“ฆ Installation

pip install falkordb-orm

Or install from source:

git clone https://github.com/FalkorDB/falkordb-py-orm.git
cd falkordb-py-orm
pip install -e .

๐Ÿ’ก Design Principles

  1. Convention over Configuration: Sensible defaults with customization options
  2. Explicit is Better: Clear, readable API over implicit magic
  3. Performance Matters: Optimize for common use cases
  4. Pythonic: Follow Python idioms and best practices
  5. Compatibility: Work seamlessly with existing falkordb-py code

๐Ÿ“„ License

MIT License - See LICENSE file for details

๐Ÿ™ Acknowledgments

๐Ÿค Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

๐Ÿ“ฆ Resources

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

falkordb_orm-1.2.1.tar.gz (65.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

falkordb_orm-1.2.1-py3-none-any.whl (75.8 kB view details)

Uploaded Python 3

File details

Details for the file falkordb_orm-1.2.1.tar.gz.

File metadata

  • Download URL: falkordb_orm-1.2.1.tar.gz
  • Upload date:
  • Size: 65.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for falkordb_orm-1.2.1.tar.gz
Algorithm Hash digest
SHA256 20a43043fcc8973df458fa97c5cf040da24a0a54542e1f224f9c66b20ae354df
MD5 2cf833c2af313bfcc2c231cc1d4ce320
BLAKE2b-256 7d211065144cee494c4d1083cc83d359fcc1c6d25f66dd12fff7c72997f70704

See more details on using hashes here.

Provenance

The following attestation bundles were made for falkordb_orm-1.2.1.tar.gz:

Publisher: publish.yml on FalkorDB/falkordb-py-orm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file falkordb_orm-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: falkordb_orm-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 75.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for falkordb_orm-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ce8fa8fb617f98ce4e887aa6bfe3ed49d06510adc6b0483b027d8288dff51a89
MD5 a6bb9efe67d18a60f5cd471bf422aa6d
BLAKE2b-256 7a98d023959b2548d0e1c2a1f8865cfca14af256fb5e5b660c188e8666ca7bda

See more details on using hashes here.

Provenance

The following attestation bundles were made for falkordb_orm-1.2.1-py3-none-any.whl:

Publisher: publish.yml on FalkorDB/falkordb-py-orm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page