diff --git a/.changeset/old-hounds-flash.md b/.changeset/old-hounds-flash.md new file mode 100644 index 0000000..8951278 --- /dev/null +++ b/.changeset/old-hounds-flash.md @@ -0,0 +1,9 @@ +--- +'@rushdb/javascript-sdk': minor +'rushdb-dashboard': minor +'rushdb-core': minor +'rushdb-website': minor +'rushdb-docs': minor +--- + +Update readme, website, add python package and examples diff --git a/README.md b/README.md index 623af84..51975cd 100644 --- a/README.md +++ b/README.md @@ -5,33 +5,13 @@ # RushDB ### The Instant Database for Modern Apps and DS/ML Ops -RushDB is an open-source database built on Neo4j, designed to simplify application development. +RushDB is an instant database for modern apps and DS/ML ops built on top of Neo4j. 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/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. - -### 3. **Powerful search API** -Query data with accuracy using the graph-powered search API. - -### 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. - -### 6. **REST API Readiness** -A REST API with SDK-like DX for every operation: manage relationships, create, delete, and search effortlessly. Same DTO everywhere. - --- ## Setup @@ -123,6 +103,42 @@ Before running the container, ensure you provide the following required environm --- +
+ Development Setup with local Neo4j [DOCKER COMPOSE] + + ```yaml + version: '3.8' + services: + rushdb: + image: rushdb/platform + container_name: rushdb + depends_on: + neo4j: + condition: service_healthy + ports: + - "3000:3000" + environment: + - NEO4J_URL=bolt://neo4j + - NEO4J_USERNAME=neo4j + - NEO4J_PASSWORD=password + neo4j: + image: neo4j:5.25.1 + healthcheck: + test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1" ] + interval: 5s + retries: 30 + start_period: 10s + ports: + - "7474:7474" + - "7687:7687" + environment: + - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes + - NEO4J_AUTH=neo4j/password + - NEO4J_PLUGINS=["apoc"] + ``` +
+ + ### **CLI Commands** The RushDB CLI allows you to manage users in self-hosted installations. Below are the available commands: @@ -166,8 +182,82 @@ This command updates the password for an existing user identified by the provide 2. **Build Anything**: Easily push, search, and manage relationships within your data. +### With Python + +Explore the [Documentation](https://docs.rushdb.com/python-sdk/records-api) + +#### Install the SDK + +```bash +pip install rushdb +``` + +#### Push any json data + +```python +from rushdb import RushDB + +db = RushDB( + "rushdb-api-key", + # Default URL; only override if necessary. + base_url="https://api.rushdb.com", +) + +db.records.create_many( + "COMPANY", + { + "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.", + "tags": ["AI", "Cloud Computing", "Research"], + "profitable": true, + "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", + "salary": 3000000, + } + ], + } + ], + } + ], + }, +) +``` + +#### Find Records by specific criteria +```python +# Find Records by specific criteria +matched_employees = db.records.find( + { + "labels": ["EMPLOYEE"], + "where": { + "position": {"$contains": "AI"}, + "PROJECT": {"DEPARTMENT": {"COMPANY": {"rating": {"$gte": 4}}}}, + }, + } +) + +``` +--- + ### With TypeScript / JavaScript +Explore the [Documentation](https://docs.rushdb.com) + #### Install the SDK ```bash @@ -238,6 +328,8 @@ const company = await db.records.findUniq('COMPANY', { ### With REST API and cURL +Explore the [Documentation](https://docs.rushdb.com) + #### Specify API base URL - **RushDB Cloud**: `https://api.rushdb.com` @@ -298,10 +390,6 @@ curl -X POST https://api.rushdb.com/api/v1/records/search \ }' ``` -
-You Rock πŸš€ -
- ---
diff --git a/docs/docs/python-sdk/_category_.json b/docs/docs/python-sdk/_category_.json new file mode 100644 index 0000000..09a9125 --- /dev/null +++ b/docs/docs/python-sdk/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Python SDK", + "position": 3, + "collapsed": false, + "collapsible": false +} diff --git a/docs/docs/python-sdk/properties-api.md b/docs/docs/python-sdk/properties-api.md new file mode 100644 index 0000000..f600bac --- /dev/null +++ b/docs/docs/python-sdk/properties-api.md @@ -0,0 +1,243 @@ +--- +sidebar_position: 3 +--- + +# PropertiesAPI + +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/docs/docs/python-sdk/record.md b/docs/docs/python-sdk/record.md new file mode 100644 index 0000000..cc41390 --- /dev/null +++ b/docs/docs/python-sdk/record.md @@ -0,0 +1,338 @@ +--- +sidebar_position: 2 +--- + +# Record + +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 = client.records.create("USER", {"name": "John"}) +print(record.id) # e.g., "1234abcd-5678-..." +``` + +### proptypes + +Gets the record's property types. + +**Type:** `str` + +**Example:** +```python +record = client.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 = client.records.create("USER", {"name": "John"}) +print(record.label) # "USER" +``` + +### timestamp + +Gets the record's creation timestamp from its ID. + +**Type:** `int` + +**Example:** +```python +record = client.records.create("USER", {"name": "John"}) +print(record.timestamp) # Unix timestamp in milliseconds +``` + +### date + +Gets the record's creation date. + +**Type:** `datetime` + +**Example:** +```python +record = client.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 = client.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 = client.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 = client.records.create("USER", {"name": "John"}) +group = client.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 = client.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 = client.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 = client.records.create("DEPARTMENT", { + "name": "Engineering" +}) + +project = client.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 client.transactions.begin() as transaction: + # Create user + user = client.records.create( + "USER", + {"name": "John Doe"}, + transaction=transaction + ) + + # Update user + user.update( + {"status": "active"}, + transaction=transaction + ) + + # Create and attach department + dept = client.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 +``` diff --git a/docs/docs/python-sdk/records-api.md b/docs/docs/python-sdk/records-api.md new file mode 100644 index 0000000..438dfd2 --- /dev/null +++ b/docs/docs/python-sdk/records-api.md @@ -0,0 +1,406 @@ +--- +sidebar_position: 1 +--- + +# RecordsAPI + +The `RecordsAPI` class provides methods for managing records in RushDB. It handles record creation, updates, deletion, searching, and relationship management. + +## Class Definition + +```python +class RecordsAPI(BaseAPI): +``` + +## 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 = client.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 = client.records.create_many( + label="COMPANY", + data=data, + options={"returnResult": True, "suggestTypes": True} +) +``` + +### 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 = client.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 = client.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 = client.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 = client.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 = client.records.delete_by_id("record-123") + +# Delete multiple records +response = client.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 = client.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 = client.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 = client.records.import_csv( + label="EMPLOYEE", + csv_data=csv_data, + options={"returnResult": True, "suggestTypes": True} +) +``` diff --git a/packages/javascript-sdk/README.md b/packages/javascript-sdk/README.md index 44bab4c..0e1f215 100644 --- a/packages/javascript-sdk/README.md +++ b/packages/javascript-sdk/README.md @@ -5,7 +5,7 @@ # RushDB SDK for JavaScript and TypeScript [![NPM Version](https://img.shields.io/npm/v/%40rushdb%2Fjavascript-sdk)](https://www.npmjs.com/package/@rushdb/javascript-sdk) -[![License](https://img.shields.io/badge/License-MIT-blue)](#license "Go to license section") +[![NPM License](https://img.shields.io/npm/l/%40rushdb%2Fjavascript-sdk)](#license "Go to license section") ![NPM Downloads](https://img.shields.io/npm/dw/%40rushdb%2Fjavascript-sdk) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40rushdb%2Fjavascript-sdk) diff --git a/platform/core/README.md b/platform/core/README.md index 87f6b63..9e282aa 100644 --- a/platform/core/README.md +++ b/platform/core/README.md @@ -12,6 +12,8 @@ It streamlines application development by automating data normalization, managin [🌐 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/examples)
+--- + ## Prerequisites Before running the application, ensure that you have the following installed: diff --git a/platform/dashboard/README.md b/platform/dashboard/README.md index 8e440a1..d768293 100644 --- a/platform/dashboard/README.md +++ b/platform/dashboard/README.md @@ -12,6 +12,8 @@ It streamlines application development by automating data normalization, managin [🌐 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/examples) +--- + The `/platform/dashboard` directory contains the RushDB management interface, built with React and Vite. This README provides instructions for running, building, and configuring the dashboard both as a standalone app and as part of the core RushDB platform. ## Prerequisites diff --git a/website/public/sitemap-0.xml b/website/public/sitemap-0.xml index 597a325..844e540 100644 --- a/website/public/sitemap-0.xml +++ b/website/public/sitemap-0.xml @@ -1,9 +1,9 @@ -https://rushdb.com2025-01-31T13:06:30.667Zdaily0.7 -https://rushdb.com/blog2025-01-31T13:06:30.668Zdaily0.7 -https://rushdb.com/pricing2025-01-31T13:06:30.668Zdaily0.7 -https://rushdb.com/cookie-policy2025-01-31T13:06:30.668Zdaily0.7 -https://rushdb.com/privacy-policy2025-01-31T13:06:30.668Zdaily0.7 -https://rushdb.com/terms-of-service2025-01-31T13:06:30.668Zdaily0.7 +https://rushdb.com2025-02-03T18:13:22.179Zdaily0.7 +https://rushdb.com/blog2025-02-03T18:13:22.179Zdaily0.7 +https://rushdb.com/pricing2025-02-03T18:13:22.179Zdaily0.7 +https://rushdb.com/cookie-policy2025-02-03T18:13:22.179Zdaily0.7 +https://rushdb.com/privacy-policy2025-02-03T18:13:22.179Zdaily0.7 +https://rushdb.com/terms-of-service2025-02-03T18:13:22.179Zdaily0.7 \ No newline at end of file diff --git a/website/src/components/CodeBlock.tsx b/website/src/components/CodeBlock.tsx index 608fa5b..5c2840d 100644 --- a/website/src/components/CodeBlock.tsx +++ b/website/src/components/CodeBlock.tsx @@ -1,17 +1,9 @@ -import { CSSProperties, PropsWithoutRef, forwardRef, useState, ReactNode, Children } from 'react' -import { - materialDark as codeTheme - // atomDark, - // nightOwl, - // synthwave84, - // tomorrow, -} from 'react-syntax-highlighter/dist/cjs/styles/prism' +import { CSSProperties, PropsWithoutRef, forwardRef, ReactNode, Children } from 'react' +import { materialDark as codeTheme } from 'react-syntax-highlighter/dist/cjs/styles/prism' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import classNames from 'classnames' import cx from 'classnames' -import { copyToClipboard } from '~/utils/copyToClipboard' -import { CopyCheck, CopyIcon } from 'lucide-react' -import { Button } from '~/components/Button' + import { CopyButton } from '~/components/CopyButton' const PreStyles: CSSProperties = { diff --git a/website/src/components/CodeBlockWithLanguageSelector.tsx b/website/src/components/CodeBlockWithLanguageSelector.tsx new file mode 100644 index 0000000..0f6fb0e --- /dev/null +++ b/website/src/components/CodeBlockWithLanguageSelector.tsx @@ -0,0 +1,131 @@ +import { CSSProperties, PropsWithoutRef, forwardRef, ReactNode, useContext } from 'react' + +import { CodingLanguage } from '~/pages' +import { CodeBlock } from '~/components/CodeBlock' +import { Tabs, TabsContent, TabsList } from '~/components/Tabs' +import { TabsTrigger } from '@radix-ui/react-tabs' +import cn from 'classnames' + +type CodeBlockWithLanguageSelectorProps = { + data: Record + className?: string + wrapperClassName?: string + preClassName?: string + style?: CSSProperties + children?: ReactNode + copyButton?: boolean +} + +const PyLogo = ({ className }: { className: string }) => ( + + + + + + + + + + + + + + +) +const TsLogo = ({ className }: { className: string }) => ( + + + + +) + +const langLogoMap = { + python: PyLogo, + typescript: TsLogo +} + +const Logo = ({ lang, className }: { lang: string; className: string }) => { + // @ts-ignore + const Comp = langLogoMap[lang] + + return +} + +export const CodeBlockWithLanguageSelector = forwardRef< + HTMLDivElement, + PropsWithoutRef +>(({ data, className, preClassName, wrapperClassName, children: extra, copyButton, style }, ref) => { + const { language, setLanguage } = useContext(CodingLanguage) + + return ( +
+
+ {Object.keys(data).map((key) => ( +
setLanguage(key)} + className={cn('w-full grow py-2 transition hover:grayscale-0', { + grayscale: key !== language, + 'border-b': key === language, + 'text-content3': key !== language + })} + > + {key} +
+ ))} +
+ + + {extra} + +
+ ) +}) + +CodeBlockWithLanguageSelector.displayName = 'CodeBlockWithLanguageSelector' diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index 548555d..8949bc8 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -3,13 +3,27 @@ import { Hero } from '~/sections/Hero' import { Mission } from '~/sections/Mission' import { HowItWorks } from '~/sections/HowItWorks' +import { createContext, useState } from 'react' + +export const CodingLanguage = createContext<{ language: string; setLanguage: (value: string) => void }>({ + language: 'typescript', + setLanguage: (value: string) => {} +}) export default function Home() { + const [language, setLanguage] = useState('typescript') return ( - - - - - + + + + + + + ) } diff --git a/website/src/sections/Hero/index.tsx b/website/src/sections/Hero/index.tsx index d4eb34b..76e929e 100644 --- a/website/src/sections/Hero/index.tsx +++ b/website/src/sections/Hero/index.tsx @@ -6,6 +6,9 @@ import { Button, MainCta } from '~/components/Button' import { links, socials } from '~/config/urls' import { CodeBlock } from '~/components/CodeBlock' import { GitHub } from '~/components/Icons/GitHub' +import { CodeBlockWithLanguageSelector } from '~/components/CodeBlockWithLanguageSelector' +import { useContext } from 'react' +import { CodingLanguage } from '~/pages' const code = `import RushDB from '@rushdb/javascript-sdk' @@ -29,7 +32,35 @@ await db.records.find({ } })` +const codePy = `from rushdb import RushDB + +db = RushDB("API_TOKEN") + +user = db.records.create( + "USER", + { + "name": "Paul Schmitz", + "lastActive": "2024-12-02T11:38:29Z", + "verified": True, + "size": 9.5, + "favoriteTags": ["Daily Run", "Foam"] + } +) + +db.records.find( + { + "labels": ["USER"], + "where": { + "size": {"$gte": 9}, + "favoriteTags": {"$in": ["Foam"]} + } + } +) +` + export const Hero = () => { + const { language } = useContext(CodingLanguage) + return ( <>
@@ -73,8 +104,11 @@ export const Hero = () => {
- @@ -85,13 +119,17 @@ export const Hero = () => { RushDB is an open-source, graph-powered zero-config database

npm-version diff --git a/website/src/sections/HowItWorks/index.tsx b/website/src/sections/HowItWorks/index.tsx index da2ed44..5d4f851 100644 --- a/website/src/sections/HowItWorks/index.tsx +++ b/website/src/sections/HowItWorks/index.tsx @@ -7,6 +7,9 @@ import { CodeBlock } from '~/components/CodeBlock' import Image from 'next/image' import dashboard from '../../images/dashboard.png' +import { CodeBlockWithLanguageSelector } from '~/components/CodeBlockWithLanguageSelector' +import { useContext } from 'react' +import { CodingLanguage } from '~/pages' const code1 = `import RushDB from '@rushdb/javascript-sdk' const db = new RushDB('rushdb-api-key') @@ -32,6 +35,36 @@ await db.records.createMany("COMPANY", { email: 'jeff@google.com', salary: 3000000` +const code1Py = `from rushdb import RushDB + +db = RushDB("rushdb-api-key") + +db.records.create_many( + "COMPANY", + { + "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.", + "tags": ["AI", "Cloud Computing", "Research"], + "profitable": true, + "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", + "salary": 3000000"` + const code2 = `await db.records.find({ labels: ['COMPANY'], where: { @@ -59,6 +92,33 @@ const code2 = `await db.records.find({ } })` +const code2Py = `db.records.find( + { + "labels": ["COMPANY"], + "where": { + "stage": "seed", + "address": {"$contains": "USA"}, + "foundedAt": {"$year": {"$lte": 2000}}, + "rating": { + "$or": [{"$lt": 2.5}, {"$gte": 4.5}] + }, + "EMPLOYEE": { + "$alias": "$employee", + "salary": { + "$gte": 500_000 + } + }, + }, + "aggregate": { + "employees": { + "fn": "collect", + "alias": "$employee", + "limit": 10 + } + }, + } +)` + const code3 = `// Property \`name\` [string] await db.properties.values( '0192397b-8579-7ce2-a899-01c59bad63f8' @@ -87,6 +147,34 @@ await db.properties.values( } ` +const code3Py = `# Property \`name\` [string] +db.properties.values( + "0192397b-8579-7ce2-a899-01c59bad63f8" +) +# Response +{ + "values": [ + "Eleanor Whitaker", + "Marcus Donovan", + "Priya Kapoor", + "Julian Alvarez" + ], + "type": "string" +} + +# Property \`size\` [number] +db.properties.values( + "019412c0-2051-71fe-bc9d-26117b52c119" +) +# Response +{ + "min": 5.5, + "max": 12.5, + "values": [5.5, 6, 6.5, 7, 7.5, 8, 8.5, ...], + "type": "number" +} +` + const codeDocker = `docker run -p 3000:3000 --name rushdb \\ -e NEO4J_URL='neo4j+s://1234567.databases.neo4j.io' \\ -e NEO4J_USERNAME='neo4j' \\ @@ -152,7 +240,32 @@ async function generateAndStoreData() { ) }` +const codeAiIntegrationPy = `import openai +from rushdb import RushDB + +db = RushDB("rushdb-api-key") +openai.api_key = "openai-api-key" + + +def generate_and_store_data(): + # Step 1: Call OpenAI API to generate some output + prompt = "..." + completion = await openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": prompt}], + response_format={"type": "json_object"}, + ) + + # Step 2: Extract the generated content + generated_content = completion["choices"][0]["message"]["content"] + parsed_content = json.loads(generated_content) + + # Step 3: Store the output in RushDB + record = await db.records.create_many("AI_RESPONSE", parsed_content)` + export const HowItWorks = () => { + const { language } = useContext(CodingLanguage) + return ( <>
@@ -170,7 +283,7 @@ export const HowItWorks = () => {
and labels any input data, so you don’t have to.

{ complex, deeply interconnected data without the acrobatics.

- + >
@@ -328,12 +441,12 @@ export const HowItWorks = () => { backend, no fuss. All from a single API.

- + > @@ -370,8 +483,8 @@ export const HowItWorks = () => {
-