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.0.tar.gz (36.0 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.0-py3-none-any.whl (39.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: falkordb_orm-1.0.0.tar.gz
  • Upload date:
  • Size: 36.0 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.0.tar.gz
Algorithm Hash digest
SHA256 757de1468546178b663d1e9ee5a59ff3ee50fd84b4d2a33b34c9cffd4351c127
MD5 654e5b60f9a4ca431f26f2a22fa92099
BLAKE2b-256 36ccae40e79513ac61ef79379f6a9b40f55625da3e1197f94598b2e188f5f3b1

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: falkordb_orm-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 39.3 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e05a85c0872d6acbe34392c8b1b0ad1d47feda34a8166fa4d66a70f756691343
MD5 ca81d2ccd2c79e353296be1a5a7464ec
BLAKE2b-256 979bf6cc418e4628cc5e82f5b49fe6e668c755b6a8bf0057191a95830bb20ab3

See more details on using hashes here.

Provenance

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