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 NEW! ๐ŸŽ‰)

  • โšก 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

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.

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
Entity Mapping @Node annotation @node decorator
Property Mapping @Property property() function
Relationships @Relationship relationship() function
Repository FalkorDBRepository<T, ID> Repository[T]
Query Methods Method name derivation Method name derivation
Custom Queries @Query annotation @query decorator
Transactions @Transactional Session (unit of work)
Async Support โŒ โœ… Planned

๐ŸŽฏ 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.0.tar.gz (62.7 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.0-py3-none-any.whl (74.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: falkordb_orm-1.2.0.tar.gz
  • Upload date:
  • Size: 62.7 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.0.tar.gz
Algorithm Hash digest
SHA256 5aa85dbafcfb17598c7fa3e28255687c66b8ba5f0acc92d5c544148da3844f26
MD5 36c7155375eb159744071dd3fbce1f95
BLAKE2b-256 3fb02cdfb26edf74804966ed23b7cebd9e4a9ee233a02e6c88c40561acb216ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for falkordb_orm-1.2.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: falkordb_orm-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 74.5 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ba0355f3bcf46afbb0f32499ee9f819fa756273184004e5e03631f3a263f3c96
MD5 e87533acc41e8cabb90203ebce0eaa0d
BLAKE2b-256 b7dfb52604e3506604738b523728dc592c0ce3b15bfaa146a96dc421de3e4b7e

See more details on using hashes here.

Provenance

The following attestation bundles were made for falkordb_orm-1.2.0-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