diff --git a/README.md b/README.md index 006f537..da11420 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,8 @@ csv_data = "name,email,age\nJohn,john@example.com,30\nJane,jane@example.com,25" db.records.import_csv( label='USER', data=csv_data, - options={'returnResult': True, 'suggestTypes': True}, + # skipEmptyValues: treat empty cells ("" / []) as unset instead of storing them (0/False are kept) + options={'returnResult': True, 'suggestTypes': True, 'skipEmptyValues': True}, parse_config={'header': True, 'skipEmptyLines': True, 'dynamicTyping': True}, ) ``` diff --git a/pyproject.toml b/pyproject.toml index c7e7019..dd6e15b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rushdb" -version = "2.3.0" +version = "2.6.0" description = "RushDB Python SDK — memory layer for AI agents and modern apps" authors = [ {name = "RushDB Team", email = "hi@rushdb.com"} diff --git a/src/rushdb/api/ai.py b/src/rushdb/api/ai.py index 5e3b527..d524f0c 100644 --- a/src/rushdb/api/ai.py +++ b/src/rushdb/api/ai.py @@ -1,6 +1,6 @@ """AI API for RushDB Python SDK. -Provides methods for graph ontology exploration, semantic vector search, +Provides methods for graph schema exploration, semantic vector search, and embedding index management. """ @@ -123,7 +123,7 @@ class AIAPI(BaseAPI): Example:: >>> db = RushDB(api_key="...") - >>> ontology = db.ai.get_ontology() + >>> schema = db.ai.get_schema() >>> results = db.ai.search({"query": "fast cars", "propertyName": "description"}) >>> db.ai.indexes.create({"propertyName": "description", "label": "Book"}) """ @@ -132,12 +132,12 @@ def __init__(self, client: "RushDB"): super().__init__(client) self.indexes = _AIIndexesNamespace(client) - def get_ontology( + def get_schema( self, params: Optional[Dict[str, Any]] = None, transaction: Optional[Union[Transaction, str]] = None, ) -> ApiResponse: - """Return the full graph ontology as structured JSON. + """Return the full graph schema as structured JSON. Each item contains the label name, record count, properties with value ranges/samples, and cross-label relationships with direction. @@ -149,13 +149,13 @@ def get_ontology( Args: params: Optional filter. Pass ``{"labels": ["Label1"]}`` to scope - the ontology to specific labels only. Pass ``{"force": True}`` - to bypass the 1-hour ontology cache and trigger a full + the schema to specific labels only. Pass ``{"force": True}`` + to bypass the 1-hour schema cache and trigger a full recalculation. transaction: Optional transaction context. Returns: - ApiResponse: Response whose ``data`` is a list of ontology items. + ApiResponse: Response whose ``data`` is a list of schema items. """ headers: Dict[str, str] = {} if transaction is not None: @@ -165,7 +165,7 @@ def get_ontology( headers["x-transaction-id"] = tx_id response = self.client._make_request( - "POST", "/ai/ontology", params or {}, headers=headers or None + "POST", "/ai/schema", params or {}, headers=headers or None ) return ApiResponse( data=response.get("data"), @@ -173,12 +173,12 @@ def get_ontology( total=response.get("total"), ) - def get_ontology_markdown( + def get_schema_markdown( self, params: Optional[Dict[str, Any]] = None, transaction: Optional[Union[Transaction, str]] = None, ) -> ApiResponse: - """Return the full graph ontology as compact Markdown tables. + """Return the full graph schema as compact Markdown tables. Token-efficient representation intended for direct LLM consumption. Includes labels with counts, properties with types and value @@ -191,7 +191,7 @@ def get_ontology_markdown( Args: params: Optional filter. Pass ``{"labels": ["Label1"]}`` to scope the output to specific labels only. Pass ``{"force": True}`` - to bypass the 1-hour ontology cache and trigger a full + to bypass the 1-hour schema cache and trigger a full recalculation. transaction: Optional transaction context. @@ -206,7 +206,7 @@ def get_ontology_markdown( headers["x-transaction-id"] = tx_id response = self.client._make_request( - "POST", "/ai/ontology/md", params or {}, headers=headers or None + "POST", "/ai/schema/md", params or {}, headers=headers or None ) return ApiResponse( data=response.get("data"), diff --git a/src/rushdb/api/records.py b/src/rushdb/api/records.py index 0923531..4710b20 100644 --- a/src/rushdb/api/records.py +++ b/src/rushdb/api/records.py @@ -172,6 +172,8 @@ def create( Available options: - returnResult (bool): Whether to return the created record data. Defaults to True. - suggestTypes (bool): Whether to automatically suggest data types. Defaults to True. + - skipEmptyValues (bool): Treat empty strings ("") and empty arrays ([]) as unset — + no property is created. 0 and False are kept. Defaults to False. vectors (Optional[List[Dict[str, Any]]]): Optional pre-computed embedding vectors to write alongside the record. Each entry must contain at least ``propertyName`` and ``vector``; ``similarityFunction`` is required when @@ -231,7 +233,9 @@ def create_many( record. Nested objects/arrays are not supported here — raises ``ValueError`` if any item contains a nested object or list. options: Optional write options forwarded as-is to the server - (e.g. ``suggestTypes``, ``mergeBy``, ``mergeStrategy``, etc.). + (e.g. ``suggestTypes``, ``skipEmptyValues``, ``mergeBy``, + ``mergeStrategy``, etc.). ``skipEmptyValues`` treats empty strings + and empty arrays as unset (``0``/``False`` are kept); defaults to False. vectors: Optional per-row inline vectors for external embedding indexes. ``vectors[i]`` is applied to ``data[i]``. Each element is a list of vector entry dicts: ``[{"propertyName": str, "vector": List[float], diff --git a/src/rushdb/api/relationship_patterns.py b/src/rushdb/api/relationship_patterns.py index 2bf6eac..0f22467 100644 --- a/src/rushdb/api/relationship_patterns.py +++ b/src/rushdb/api/relationship_patterns.py @@ -10,7 +10,7 @@ class RelationshipPatternsAPI(BaseAPI): - """Review and manage relationship patterns inferred from project ontology. + """Review and manage relationship patterns inferred from project schema. Accessed via ``db.relationships.patterns``. """ @@ -27,12 +27,12 @@ def _wrap(response: Dict[str, Any]) -> ApiResponse: ) def list(self) -> ApiResponse: - """List inferred patterns, ontology relationships, and analysis status.""" + """List inferred patterns, schema relationships, and analysis status.""" response = self.client._make_request("GET", "/relationships/patterns") return self._wrap(response) def analyze(self) -> ApiResponse: - """Queue ontology analysis to generate relationship pattern suggestions.""" + """Queue schema analysis to generate relationship pattern suggestions.""" response = self.client._make_request( "POST", "/relationships/patterns/analyze", {} ) diff --git a/src/rushdb/models/api_response.py b/src/rushdb/models/api_response.py index 7dbc26f..fae9b1d 100644 --- a/src/rushdb/models/api_response.py +++ b/src/rushdb/models/api_response.py @@ -19,7 +19,7 @@ class ApiResponse(Generic[T]): total: Optional total count (e.g. for list endpoints). Example: - >>> response = db.ai.get_ontology_markdown() + >>> response = db.ai.get_schema_markdown() >>> schema_md = response.data # str >>> response = db.ai.indexes.find() >>> indexes = response.data # list[dict] diff --git a/src/rushdb/models/property.py b/src/rushdb/models/property.py index cd47ae0..543eb5c 100644 --- a/src/rushdb/models/property.py +++ b/src/rushdb/models/property.py @@ -21,8 +21,9 @@ class DatetimeObject(TypedDict, total=False): NumberValue = Union[float, int] StringValue = str -# Property types -PropertyType = Literal["boolean", "datetime", "null", "number", "string"] +# Property types. A None/null value means the field is unset; `None` is still accepted as an +# input value in `PropertyWithValue.value` below. +PropertyType = Literal["boolean", "datetime", "number", "string"] class Property(TypedDict):