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

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

๐Ÿ“‹ Future Enhancements (Optional)

  • โšก Transaction Support: Context managers for transactional operations
  • ๐Ÿ—‚๏ธ Index Management: Decorator-based index and constraint creation
  • ๐Ÿ“ฆ Migration System: Schema version management
  • ๐Ÿ” Query Caching: Result caching for performance

๐Ÿ“œ 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())

Interned Strings for Memory Optimization (Phase 6)

from falkordb_orm import node, interned
from typing import Optional

@node("User")
class User:
    id: Optional[int] = None
    name: str
    email: str
    
    # Interned properties - deduplicated for memory savings
    country: str = interned()  # "United States" stored once for all US users
    city: str = interned()     # "New York" stored once for all NYC users  
    status: str = interned()   # "Active", "Inactive" stored once each
    
# Memory benefit: Repeated values stored only once!
# Perfect for: countries, cities, status fields, categories, tags

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.0.1.tar.gz (36.4 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.0.1-py3-none-any.whl (39.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: falkordb_orm-1.0.1.tar.gz
  • Upload date:
  • Size: 36.4 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.0.1.tar.gz
Algorithm Hash digest
SHA256 18c3a1f633fb5eba88da5bff22987104563739a84475714138edba78607a57f3
MD5 64f7538c4a2f56c3c46add4478811892
BLAKE2b-256 d6208ebe6f2dd865819947a526f958ad5d42e117f9405150453beb3a548d74f0

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: falkordb_orm-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 39.7 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.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7616869a9b975c85db56655c3e87a468c366994315b501b3a8e252b0cb905c38
MD5 ccab0c5e865c45c04fc7034f4c46b095
BLAKE2b-256 fec73e543fd4b2ef2d22ec86943f9c3dd17e2c53852e311d589f5e95e470945a

See more details on using hashes here.

Provenance

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