diff --git a/README.md b/README.md index 7e827a8..39a5285 100644 --- a/README.md +++ b/README.md @@ -3,34 +3,18 @@ ![RushDB Logo](https://raw.githubusercontent.com/rush-db/rushdb/main/rushdb-logo.svg) # RushDB Python SDK -### The Instant Database for Modern Apps and DS/ML Ops +![PyPI - Version](https://img.shields.io/pypi/v/rushdb) -RushDB is an open-source database built on Neo4j, designed to simplify application development. +![PyPI - Python Version](https://img.shields.io/pypi/pyversions/rushdb) -It automates data normalization, manages relationships, and infers data types, enabling developers to focus on building features rather than wrestling with data. - -[🌐 Homepage](https://rushdb.com) — [📢 Blog](https://rushdb.com/blog) — [☁️ Platform ](https://app.rushdb.com) — [📚 Docs](https://docs.rushdb.com) — [🧑‍💻 Examples](https://github.com/rush-db/rushdb/examples) - - -## 🚀 Feature Highlights - -### 1. **Data modeling is optional** -Push data of any shape—RushDB handles relationships, data types, and more automatically. - -### 2. **Automatic type inference** -Minimizes overhead while optimizing performance for high-speed searches. +![PyPI - License](https://img.shields.io/pypi/l/rushdb) -### 3. **Powerful search API** -Query data with accuracy using the graph-powered search API. +RushDB is an instant database for modern apps and DS/ML ops built on top of Neo4j. -### 4. **Flexible data import** -Easily import data in `JSON`, `CSV`, or `JSONB`, creating data-rich applications fast. - -### 5. **Developer-Centric Design** -RushDB prioritizes DX with an intuitive and consistent API. +It automates data normalization, manages relationships, and infers data types, enabling developers to focus on building features rather than wrestling with data. -### 6. **REST API Readiness** -A REST API with SDK-like DX for every operation: manage relationships, create, delete, and search effortlessly. Same DTO everywhere. +[🌐 Homepage](https://rushdb.com) — [📢 Blog](https://rushdb.com/blog) — [☁️ Platform ](https://app.rushdb.com) — [📚 Docs](https://docs.rushdb.com/python-sdk/records-api) — [🧑‍💻 Examples](https://github.com/rush-db/examples) + --- @@ -38,6 +22,7 @@ A REST API with SDK-like DX for every operation: manage relationships, create, d Install the RushDB Python SDK via pip: + ```sh pip install rushdb ``` @@ -51,42 +36,52 @@ pip install rushdb ```python from rushdb import RushDB -db = RushDB("API_TOKEN", url="https://api.rushdb.com") +db = RushDB("API_TOKEN", base_url="https://api.rushdb.com") ``` --- ### **2. Push any JSON data** + ```python company_data = { - "label": "COMPANY", - "payload": { - "name": "Google LLC", - "address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA", - "foundedAt": "1998-09-04T00:00:00.000Z", - "rating": 4.9, - "DEPARTMENT": [{ - "name": "Research & Development", - "description": "Innovating and creating advanced technologies for AI, cloud computing, and consumer devices.", - "PROJECT": [{ - "name": "Bard AI", - "description": "A state-of-the-art generative AI model for natural language understanding and creation.", - "active": True, - "budget": 1200000000, - "EMPLOYEE": [{ - "name": "Jeff Dean", - "position": "Head of AI Research", - "email": "jeff@google.com", - "dob": "1968-07-16T00:00:00.000Z", - "salary": 3000000 - }] + "name": "Google LLC", + "address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA", + "foundedAt": "1998-09-04T00:00:00.000Z", + "rating": 4.9, + "DEPARTMENT": [{ + "name": "Research & Development", + "description": "Innovating and creating advanced technologies for AI, cloud computing, and consumer devices.", + "PROJECT": [{ + "name": "Bard AI", + "description": "A state-of-the-art generative AI model for natural language understanding and creation.", + "active": True, + "budget": 1200000000, + "EMPLOYEE": [{ + "name": "Jeff Dean", + "position": "Head of AI Research", + "email": "jeff@google.com", + "dob": "1968-07-16T00:00:00.000Z", + "salary": 3000000 }] }] - } + }] } -db.records.create_many(company_data) +db.records.create_many("COMPANY", company_data) +``` + +This operation will create 4 Records with proper data types and relationships according to this structure: + +```cypher +(Record:COMPANY) + -[r0:RUSHDB_DEFAULT_RELATION]-> + (Record:DEPARTMENT) + -[r1:RUSHDB_DEFAULT_RELATION]-> + (Record:PROJECT) + -[r2:RUSHDB_DEFAULT_RELATION]-> + (Record:EMPLOYEE) ``` --- @@ -115,36 +110,981 @@ company = db.records.find_uniq("COMPANY", {"where": {"name": "Google LLC"}}) --- -### **4. Use REST API with cURL** -```sh -curl -X POST https://api.rushdb.com/api/v1/records/search \ --H "Authorization: Bearer API_TOKEN" \ --H "Content-Type: application/json" \ --d '{ - "labels": ["EMPLOYEE"], - "where": { - "position": { "$contains": "AI" }, - "PROJECT": { - "DEPARTMENT": { - "COMPANY": { - "rating": { "$gte": 4 } - } - } +# Documentation + +# RecordsAPI Documentation + +The `RecordsAPI` class provides methods for managing records in RushDB. It handles record creation, updates, deletion, searching, and relationship management. + +## Methods + +### create() + +Creates a new record in RushDB. + +**Signature:** +```python +def create( + self, + label: str, + data: Dict[str, Any], + options: Optional[Dict[str, bool]] = None, + transaction: Optional[Transaction] = None +) -> Record +``` + +**Arguments:** +- `label` (str): Label for the record +- `data` (Dict[str, Any]): Record data +- `options` (Optional[Dict[str, bool]]): Optional parsing and response options + - `returnResult` (bool): Whether to return the created record + - `suggestTypes` (bool): Whether to suggest property types +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Record`: Created record object + +**Example:** +```python +# Create a new company record +data = { + "name": "Google LLC", + "address": "1600 Amphitheatre Parkway", + "foundedAt": "1998-09-04T00:00:00.000Z", + "rating": 4.9 +} + +record = db.records.create( + label="COMPANY", + data=data, + options={"returnResult": True, "suggestTypes": True} +) +``` + +### create_many() + +Creates multiple records in a single operation. + +**Signature:** +```python +def create_many( + self, + label: str, + data: Union[Dict[str, Any], List[Dict[str, Any]]], + options: Optional[Dict[str, bool]] = None, + transaction: Optional[Transaction] = None +) -> List[Record] +``` + +**Arguments:** +- `label` (str): Label for all records +- `data` (Union[Dict[str, Any], List[Dict[str, Any]]]): List or Dict of record data +- `options` (Optional[Dict[str, bool]]): Optional parsing and response options +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `List[Record]`: List of created record objects + +**Example:** +```python +# Create multiple company records +data = [ + { + "name": "Apple Inc", + "address": "One Apple Park Way", + "foundedAt": "1976-04-01T00:00:00.000Z", + "rating": 4.8 + }, + { + "name": "Microsoft Corporation", + "address": "One Microsoft Way", + "foundedAt": "1975-04-04T00:00:00.000Z", + "rating": 4.7 } - } -}' +] + +records = db.records.create_many( + label="COMPANY", + data=data, + options={"returnResult": True, "suggestTypes": True} +) ``` -
-You Rock 🚀 -
+### set() + +Updates a record by ID, replacing all data. + +**Signature:** +```python +def set( + self, + record_id: str, + data: Dict[str, Any], + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `record_id` (str): ID of the record to update +- `data` (Dict[str, Any]): New record data +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Update entire record data +new_data = { + "name": "Updated Company Name", + "rating": 5.0 +} + +response = db.records.set( + record_id="record-123", + data=new_data +) +``` + +### update() + +Updates specific fields of a record by ID. + +**Signature:** +```python +def update( + self, + record_id: str, + data: Dict[str, Any], + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `record_id` (str): ID of the record to update +- `data` (Dict[str, Any]): Partial record data to update +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Update specific fields +updates = { + "rating": 4.8, + "status": "active" +} + +response = db.records.update( + record_id="record-123", + data=updates +) +``` + +### find() + +Searches for records matching specified criteria. + +**Signature:** +```python +def find( + self, + query: Optional[SearchQuery] = None, + record_id: Optional[str] = None, + transaction: Optional[Transaction] = None +) -> List[Record] +``` + +**Arguments:** +- `query` (Optional[SearchQuery]): Search query parameters +- `record_id` (Optional[str]): Optional record ID to search from +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `List[Record]`: List of matching records + +**Example:** +```python +# Search for records with complex criteria +query = { + "where": { + "$and": [ + {"age": {"$gte": 18}}, + {"status": "active"}, + {"department": "Engineering"} + ] + }, + "orderBy": {"created_at": "desc"}, + "limit": 10 +} + +records = db.records.find(query=query) +``` + +### delete() + +Deletes records matching a query. + +**Signature:** +```python +def delete( + self, + query: SearchQuery, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `query` (SearchQuery): Query to match records for deletion +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Delete records matching criteria +query = { + "where": { + "status": "inactive", + "lastActive": {"$lt": "2023-01-01"} + } +} + +response = db.records.delete(query) +``` + +### delete_by_id() + +Deletes one or more records by ID. + +**Signature:** +```python +def delete_by_id( + self, + id_or_ids: Union[str, List[str]], + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `id_or_ids` (Union[str, List[str]]): Single ID or list of IDs to delete +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Delete single record +response = db.records.delete_by_id("record-123") + +# Delete multiple records +response = db.records.delete_by_id([ + "record-123", + "record-456", + "record-789" +]) +``` + +### attach() + +Creates relationships between records. + +**Signature:** +```python +def attach( + self, + source: Union[str, Dict[str, Any]], + target: Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]], + options: Optional[RelationshipOptions] = None, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `source` (Union[str, Dict[str, Any]]): Source record ID or data +- `target` (Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]]): Target record(s) +- `options` (Optional[RelationshipOptions]): Relationship options + - `direction` (Optional[Literal["in", "out"]]): Relationship direction + - `type` (Optional[str]): Relationship type +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Create relationship between records +options = RelationshipOptions( + type="HAS_EMPLOYEE", + direction="out" +) + +response = db.records.attach( + source="company-123", + target=["employee-456", "employee-789"], + options=options +) +``` + +### detach() + +Removes relationships between records. + +**Signature:** +```python +def detach( + self, + source: Union[str, Dict[str, Any]], + target: Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]], + options: Optional[RelationshipDetachOptions] = None, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `source` (Union[str, Dict[str, Any]]): Source record ID or data +- `target` (Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]]): Target record(s) +- `options` (Optional[RelationshipDetachOptions]): Detach options + - `direction` (Optional[Literal["in", "out"]]): Relationship direction + - `typeOrTypes` (Optional[Union[str, List[str]]]): Relationship type(s) +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Remove relationships between records +options = RelationshipDetachOptions( + typeOrTypes=["HAS_EMPLOYEE", "MANAGES"], + direction="out" +) + +response = db.records.detach( + source="company-123", + target="employee-456", + options=options +) +``` + +### import_csv() + +Imports records from CSV data. + +**Signature:** +```python +def import_csv( + self, + label: str, + csv_data: Union[str, bytes], + options: Optional[Dict[str, bool]] = None, + transaction: Optional[Transaction] = None +) -> List[Dict[str, Any]] +``` + +**Arguments:** +- `label` (str): Label for imported records +- `csv_data` (Union[str, bytes]): CSV data to import +- `options` (Optional[Dict[str, bool]]): Import options +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `List[Dict[str, Any]]`: Imported records data + +**Example:** +```python +# Import records from CSV +csv_data = """name,age,department,role +John Doe,30,Engineering,Senior Engineer +Jane Smith,28,Product,Product Manager +Bob Wilson,35,Engineering,Tech Lead""" + +records = db.records.import_csv( + label="EMPLOYEE", + csv_data=csv_data, + options={"returnResult": True, "suggestTypes": True} +) +``` --- -
+# Record Class Documentation -> Check the [Documentation](https://docs.rushdb.com) and [Examples](https://github.com/rush-db/rushdb/examples) to learn more 🧐 +The `Record` class represents a record in RushDB and provides methods for manipulating individual records, including updates, relationships, and deletions. -
+## Class Definition + +```python +class Record: + def __init__(self, client: "RushDB", data: Union[Dict[str, Any], None] = None) +``` + +## Properties + +### id + +Gets the record's unique identifier. + +**Type:** `str` + +**Example:** +```python +record = db.records.create("USER", {"name": "John"}) +print(record.id) # e.g., "1234abcd-5678-..." +``` + +### proptypes + +Gets the record's property types. + +**Type:** `str` + +**Example:** +```python +record = db.records.create("USER", {"name": "John", "age": 25}) +print(record.proptypes) # Returns property type definitions +``` + +### label + +Gets the record's label. + +**Type:** `str` + +**Example:** +```python +record = db.records.create("USER", {"name": "John"}) +print(record.label) # "USER" +``` +### timestamp + +Gets the record's creation timestamp from its ID. + +**Type:** `int` + +**Example:** +```python +record = db.records.create("USER", {"name": "John"}) +print(record.timestamp) # Unix timestamp in milliseconds +``` + +### date + +Gets the record's creation date. + +**Type:** `datetime` + +**Example:** +```python +record = db.records.create("USER", {"name": "John"}) +print(record.date) # datetime object +``` + +## Methods + +### set() + +Updates all data for the record. + +**Signature:** +```python +def set( + self, + data: Dict[str, Any], + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `data` (Dict[str, Any]): New record data +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +record = db.records.create("USER", {"name": "John"}) +response = record.set({ + "name": "John Doe", + "email": "john@example.com", + "age": 30 +}) +``` + +### update() + +Updates specific fields of the record. + +**Signature:** +```python +def update( + self, + data: Dict[str, Any], + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `data` (Dict[str, Any]): Partial record data to update +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +record = db.records.create("USER", { + "name": "John", + "email": "john@example.com" +}) +response = record.update({ + "email": "john.doe@example.com" +}) +``` + +### attach() + +Creates relationships with other records. + +**Signature:** +```python +def attach( + self, + target: Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], "Record", List["Record"]], + options: Optional[RelationshipOptions] = None, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `target` (Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]]): Target record(s) +- `options` (Optional[RelationshipOptions]): Relationship options + - `direction` (Optional[Literal["in", "out"]]): Relationship direction + - `type` (Optional[str]): Relationship type +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Create two records +user = db.records.create("USER", {"name": "John"}) +group = db.records.create("GROUP", {"name": "Admins"}) + +# Attach user to group +response = user.attach( + target=group, + options=RelationshipOptions( + type="BELONGS_TO", + direction="out" + ) +) +``` + +### detach() + +Removes relationships with other records. + +**Signature:** +```python +def detach( + self, + target: Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], "Record", List["Record"]], + options: Optional[RelationshipDetachOptions] = None, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `target` (Union[str, List[str], Dict[str, Any], List[Dict[str, Any]], Record, List[Record]]): Target record(s) +- `options` (Optional[RelationshipDetachOptions]): Detach options + - `direction` (Optional[Literal["in", "out"]]): Relationship direction + - `typeOrTypes` (Optional[Union[str, List[str]]]): Relationship type(s) +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +# Detach user from group +response = user.detach( + target=group, + options=RelationshipDetachOptions( + typeOrTypes="BELONGS_TO", + direction="out" + ) +) +``` + +### delete() + +Deletes the record. + +**Signature:** +```python +def delete( + self, + transaction: Optional[Transaction] = None +) -> Dict[str, str] +``` + +**Arguments:** +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Dict[str, str]`: Response data + +**Example:** +```python +user = db.records.create("USER", {"name": "John"}) +response = user.delete() +``` + +## Complete Usage Example + +Here's a comprehensive example demonstrating various Record operations: + +```python +# Create a new record +user = db.records.create("USER", { + "name": "John Doe", + "email": "john@example.com", + "age": 30 +}) + +# Access properties +print(f"Record ID: {user.id}") +print(f"Label: {user.label}") +print(f"Created at: {user.date}") + +# Update record data +user.update({ + "age": 31, + "title": "Senior Developer" +}) + +# Create related records +department = db.records.create("DEPARTMENT", { + "name": "Engineering" +}) + +project = db.records.create("PROJECT", { + "name": "Secret Project" +}) + +# Create relationships +user.attach( + target=department, + options=RelationshipOptions( + type="BELONGS_TO", + direction="out" + ) +) + +user.attach( + target=project, + options=RelationshipOptions( + type="WORKS_ON", + direction="out" + ) +) + +# Remove relationship +user.detach( + target=project, + options=RelationshipDetachOptions( + typeOrTypes="WORKS_ON", + direction="out" + ) +) + +# Delete record +user.delete() +``` + +## Working with Transactions + +Records can be manipulated within transactions for atomic operations: + +```python +# Start a transaction +with db.transactions.begin() as transaction: + # Create user + user = db.records.create( + "USER", + {"name": "John Doe"}, + transaction=transaction + ) + + # Update user + user.update( + {"status": "active"}, + transaction=transaction + ) + + # Create and attach department + dept = db.records.create( + "DEPARTMENT", + {"name": "Engineering"}, + transaction=transaction + ) + + user.attach( + target=dept, + options=RelationshipOptions(type="BELONGS_TO"), + transaction=transaction + ) + + # Transaction will automatically commit if no errors occur + # If an error occurs, it will automatically rollback +``` + +--- + +# PropertiesAPI Documentation + +The `PropertiesAPI` class provides methods for managing and querying properties in RushDB. + +## Class Definition + +```python +class PropertiesAPI(BaseAPI): +``` + +## Methods + +### find() + +Retrieves a list of properties based on optional search criteria. + +**Signature:** +```python +def find( + self, + query: Optional[SearchQuery] = None, + transaction: Optional[Transaction] = None +) -> List[Property] +``` + +**Arguments:** +- `query` (Optional[SearchQuery]): Search query parameters for filtering properties +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `List[Property]`: List of properties matching the search criteria + +**Example:** +```python +# Find all properties +properties = client.properties.find() + +# Find properties with specific criteria +query = { + "where": { + "name": {"$startsWith": "user_"}, # Properties starting with 'user_' + "type": "string" # Only string type properties + }, + "limit": 10 # Limit to 10 results +} +filtered_properties = client.properties.find(query) +``` + +### find_by_id() + +Retrieves a specific property by its ID. + +**Signature:** +```python +def find_by_id( + self, + property_id: str, + transaction: Optional[Transaction] = None +) -> Property +``` + +**Arguments:** +- `property_id` (str): Unique identifier of the property +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `Property`: Property details + +**Example:** +```python +# Retrieve a specific property by ID +property_details = client.properties.find_by_id("prop_123456") +``` + +### delete() + +Deletes a property by its ID. + +**Signature:** +```python +def delete( + self, + property_id: str, + transaction: Optional[Transaction] = None +) -> None +``` + +**Arguments:** +- `property_id` (str): Unique identifier of the property to delete +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `None` + +**Example:** +```python +# Delete a property +client.properties.delete("prop_123456") +``` + +### values() + +Retrieves values for a specific property with optional sorting and pagination. + +**Signature:** +```python +def values( + self, + property_id: str, + sort: Optional[Literal["asc", "desc"]] = None, + skip: Optional[int] = None, + limit: Optional[int] = None, + transaction: Optional[Transaction] = None +) -> PropertyValuesData +``` + +**Arguments:** +- `property_id` (str): Unique identifier of the property +- `sort` (Optional[Literal["asc", "desc"]]): Sort order of values +- `skip` (Optional[int]): Number of values to skip (for pagination) +- `limit` (Optional[int]): Maximum number of values to return +- `transaction` (Optional[Transaction]): Optional transaction object + +**Returns:** +- `PropertyValuesData`: Property values data, including optional min/max and list of values + +**Example:** +```python +# Get property values +values_data = client.properties.values( + property_id="prop_age", + sort="desc", # Sort values in descending order + skip=0, # Start from the first value + limit=100 # Return up to 100 values +) + +# Access values +print(values_data.get('values', [])) # List of property values +print(values_data.get('min')) # Minimum value (for numeric properties) +print(values_data.get('max')) # Maximum value (for numeric properties) +``` + +## Comprehensive Usage Example + +```python +# Find all properties +all_properties = client.properties.find() +for prop in all_properties: + print(f"Property ID: {prop['id']}") + print(f"Name: {prop['name']}") + print(f"Type: {prop['type']}") + print(f"Metadata: {prop.get('metadata', 'No metadata')}") + print("---") + +# Detailed property search +query = { + "where": { + "type": "number", # Only numeric properties + "name": {"$contains": "score"} # Properties with 'score' in name + }, + "limit": 5 # Limit to 5 results +} +numeric_score_properties = client.properties.find(query) + +# Get values for a specific property +if numeric_score_properties: + first_prop = numeric_score_properties[0] + prop_values = client.properties.values( + property_id=first_prop['id'], + sort="desc", + limit=50 + ) + print(f"Values for {first_prop['name']}:") + print(f"Min: {prop_values.get('min')}") + print(f"Max: {prop_values.get('max')}") + + # Detailed property examination + detailed_prop = client.properties.find_by_id(first_prop['id']) + print("Detailed Property Info:", detailed_prop) +``` + +## Property Types and Structures + +RushDB supports the following property types: +- `"boolean"`: True/False values +- `"datetime"`: Date and time values +- `"null"`: Null/empty values +- `"number"`: Numeric values +- `"string"`: Text values + +### Property Structure Example +```python +property = { + "id": "prop_unique_id", + "name": "user_score", + "type": "number", + "metadata": Optional[str] # Optional additional information +} + +property_with_value = { + "id": "prop_unique_id", + "name": "user_score", + "type": "number", + "value": 95.5 # Actual property value +} +``` + +## Transactions + +Properties API methods support optional transactions for atomic operations: + +```python +# Using a transaction +with client.transactions.begin() as transaction: + # Perform multiple property-related operations + property_to_delete = client.properties.find( + {"where": {"name": "temp_property"}}, + transaction=transaction + )[0] + + client.properties.delete( + property_id=property_to_delete['id'], + transaction=transaction + ) + # Transaction will automatically commit if no errors occur +``` + +## Error Handling + +When working with the PropertiesAPI, be prepared to handle potential errors: + +```python +try: + # Attempt to find or delete a property + property_details = client.properties.find_by_id("non_existent_prop") +except RushDBError as e: + print(f"Error: {e}") + print(f"Error Details: {e.details}") +``` diff --git a/src/rushdb/__init__.py b/src/rushdb/__init__.py index d6a8add..a146296 100644 --- a/src/rushdb/__init__.py +++ b/src/rushdb/__init__.py @@ -1,9 +1,9 @@ """RushDB Client Package -Exposes the RushDBClient class. +Exposes the RushDB class. """ -from .client import RushDBClient +from .client import RushDB from .common import RushDBError from .models.property import Property from .models.record import Record @@ -11,7 +11,7 @@ from .models.transaction import Transaction __all__ = [ - "RushDBClient", + "RushDB", "RushDBError", "Record", "Transaction", diff --git a/src/rushdb/api/base.py b/src/rushdb/api/base.py index 1632036..b549e49 100644 --- a/src/rushdb/api/base.py +++ b/src/rushdb/api/base.py @@ -1,11 +1,11 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from ..client import RushDBClient + from ..client import RushDB class BaseAPI: """Base class for all API endpoints.""" - def __init__(self, client: "RushDBClient"): + def __init__(self, client: "RushDB"): self.client = client diff --git a/src/rushdb/client.py b/src/rushdb/client.py index 9325040..20a32a0 100644 --- a/src/rushdb/client.py +++ b/src/rushdb/client.py @@ -13,7 +13,7 @@ from .common import RushDBError -class RushDBClient: +class RushDB: """Main client for interacting with RushDB.""" DEFAULT_BASE_URL = "https://api.rushdb.com" diff --git a/src/rushdb/models/record.py b/src/rushdb/models/record.py index 9b11268..d12966f 100644 --- a/src/rushdb/models/record.py +++ b/src/rushdb/models/record.py @@ -5,15 +5,13 @@ from .transaction import Transaction if TYPE_CHECKING: - from ..client import RushDBClient + from ..client import RushDB class Record: """Represents a record in RushDB with methods for manipulation.""" - def __init__( - self, client: "RushDBClient", data: Union[Dict[str, Any], None] = None - ): + def __init__(self, client: "RushDB", data: Union[Dict[str, Any], None] = None): self._client = client # Handle different data formats if isinstance(data, dict): diff --git a/src/rushdb/models/transaction.py b/src/rushdb/models/transaction.py index 984990c..bca7a21 100644 --- a/src/rushdb/models/transaction.py +++ b/src/rushdb/models/transaction.py @@ -3,13 +3,13 @@ from ..common import RushDBError if TYPE_CHECKING: - from ..client import RushDBClient + from ..client import RushDB class Transaction: """Represents a RushDB transaction.""" - def __init__(self, client: "RushDBClient", transaction_id: str): + def __init__(self, client: "RushDB", transaction_id: str): self.client = client self.id = transaction_id self._committed = False diff --git a/tests/test_base_setup.py b/tests/test_base_setup.py index d8af7d0..330b20f 100644 --- a/tests/test_base_setup.py +++ b/tests/test_base_setup.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv -from src.rushdb import RushDBClient, RushDBError +from src.rushdb import RushDB, RushDBError def load_env(): @@ -44,7 +44,7 @@ def setUpClass(cls): def setUp(self): """Set up test client.""" - self.client = RushDBClient(self.token, base_url=self.base_url) + self.client = RushDB(self.token, base_url=self.base_url) # Verify connection try: