rushdb 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rushdb-2.0.0 → rushdb-2.2.0}/PKG-INFO +1 -1
- {rushdb-2.0.0 → rushdb-2.2.0}/pyproject.toml +1 -1
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/__init__.py +7 -1
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/__init__.py +2 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/records.py +82 -81
- rushdb-2.2.0/src/rushdb/api/relationship_patterns.py +61 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/relationships.py +9 -1
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/client.py +2 -2
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/record.py +13 -16
- rushdb-2.2.0/tests/test_relationship_patterns.py +54 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/uv.lock +2 -2
- {rushdb-2.0.0 → rushdb-2.2.0}/.env.example +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/.github/workflows/ci.yml +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/.gitignore +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/CODE_OF_CONDUCT.md +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/CONTRIBUTING.md +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/LICENSE +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/README.md +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/rushdb-logo.svg +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/__init__.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/ai.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/base.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/labels.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/properties.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/query.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/api/transactions.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/common.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/__init__.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/api_response.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/property.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/relationship.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/result.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/search_query.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/models/transaction.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/src/rushdb/utils/token_prefix.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/tests/__init__.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/tests/test_base_setup.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/tests/test_create_import.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/tests/test_search_query.py +0 -0
- {rushdb-2.0.0 → rushdb-2.2.0}/tests/test_search_result.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rushdb
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: RushDB Python SDK — memory layer for AI agents and modern apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/rush-db/rushdb-python
|
|
6
6
|
Project-URL: Repository, https://github.com/rush-db/rushdb-python
|
|
@@ -5,12 +5,14 @@ Exposes the RushDB class.
|
|
|
5
5
|
|
|
6
6
|
from .api.ai import AIAPI
|
|
7
7
|
from .api.query import QueryAPI
|
|
8
|
+
from .api.records import pick_record_id
|
|
9
|
+
from .api.relationship_patterns import RelationshipPatternsAPI
|
|
8
10
|
from .api.relationships import RelationsAPI
|
|
9
11
|
from .client import RushDB
|
|
10
12
|
from .common import NonUniqueResultError, RushDBError
|
|
11
13
|
from .models.api_response import ApiResponse
|
|
12
14
|
from .models.property import Property
|
|
13
|
-
from .models.record import Record
|
|
15
|
+
from .models.record import Record, RecordTarget, RelationTarget
|
|
14
16
|
from .models.relationship import RelationshipDetachOptions, RelationshipOptions
|
|
15
17
|
from .models.result import RecordSearchResult, SearchResult
|
|
16
18
|
from .models.transaction import Transaction
|
|
@@ -20,6 +22,9 @@ __all__ = [
|
|
|
20
22
|
"RushDBError",
|
|
21
23
|
"NonUniqueResultError",
|
|
22
24
|
"Record",
|
|
25
|
+
"RecordTarget",
|
|
26
|
+
"RelationTarget",
|
|
27
|
+
"pick_record_id",
|
|
23
28
|
"RecordSearchResult",
|
|
24
29
|
"SearchResult",
|
|
25
30
|
"Transaction",
|
|
@@ -28,6 +33,7 @@ __all__ = [
|
|
|
28
33
|
"RelationshipDetachOptions",
|
|
29
34
|
"QueryAPI",
|
|
30
35
|
"RelationsAPI",
|
|
36
|
+
"RelationshipPatternsAPI",
|
|
31
37
|
"ApiResponse",
|
|
32
38
|
"AIAPI",
|
|
33
39
|
]
|
|
@@ -2,6 +2,7 @@ from .labels import LabelsAPI
|
|
|
2
2
|
from .properties import PropertiesAPI
|
|
3
3
|
from .query import QueryAPI
|
|
4
4
|
from .records import RecordsAPI
|
|
5
|
+
from .relationship_patterns import RelationshipPatternsAPI
|
|
5
6
|
from .relationships import RelationsAPI
|
|
6
7
|
from .transactions import TransactionsAPI
|
|
7
8
|
|
|
@@ -12,4 +13,5 @@ __all__ = [
|
|
|
12
13
|
"TransactionsAPI",
|
|
13
14
|
"QueryAPI",
|
|
14
15
|
"RelationsAPI",
|
|
16
|
+
"RelationshipPatternsAPI",
|
|
15
17
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from typing import Any, Dict, List, Optional, Union
|
|
3
3
|
|
|
4
|
-
from ..models.record import Record
|
|
4
|
+
from ..models.record import Record, RecordTarget, RelationTarget
|
|
5
5
|
from ..models.relationship import RelationshipDetachOptions, RelationshipOptions
|
|
6
6
|
from ..models.result import RecordSearchResult
|
|
7
7
|
from ..models.search_query import SearchQuery
|
|
@@ -9,6 +9,29 @@ from ..models.transaction import Transaction
|
|
|
9
9
|
from .base import BaseAPI
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def pick_record_id(target: RecordTarget) -> Optional[str]:
|
|
13
|
+
"""Extract a record ID string from any RecordTarget representation.
|
|
14
|
+
|
|
15
|
+
Mirrors the JavaScript SDK's ``pickRecordId`` utility in ``src/api/utils.ts``.
|
|
16
|
+
Accepts a UUID string, a ``Record`` instance, or a plain dict containing
|
|
17
|
+
an ``__id`` field — in all cases the resolved UUID string is returned.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
target: A record reference as a string ID, ``Record`` object, or dict
|
|
21
|
+
with an ``"__id"`` key.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
The UUID string, or ``None`` if it cannot be resolved.
|
|
25
|
+
"""
|
|
26
|
+
if isinstance(target, str):
|
|
27
|
+
return target
|
|
28
|
+
elif isinstance(target, Record):
|
|
29
|
+
return target.data.get("__id")
|
|
30
|
+
elif isinstance(target, dict):
|
|
31
|
+
return target.get("__id")
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
12
35
|
def _is_flat(obj: Any) -> bool:
|
|
13
36
|
"""Return True if obj is a dict with no nested dict/list values."""
|
|
14
37
|
return isinstance(obj, dict) and not any(
|
|
@@ -55,7 +78,7 @@ class RecordsAPI(BaseAPI):
|
|
|
55
78
|
|
|
56
79
|
def set(
|
|
57
80
|
self,
|
|
58
|
-
|
|
81
|
+
target: RecordTarget,
|
|
59
82
|
data: Dict[str, Any],
|
|
60
83
|
label: Optional[str] = None,
|
|
61
84
|
vectors: Optional[List[Dict[str, Any]]] = None,
|
|
@@ -67,7 +90,8 @@ class RecordsAPI(BaseAPI):
|
|
|
67
90
|
removing any existing fields that are not present in the new data.
|
|
68
91
|
|
|
69
92
|
Args:
|
|
70
|
-
|
|
93
|
+
target (RecordTarget): The record to update. Accepts a UUID string,
|
|
94
|
+
a ``Record`` instance, or a dict containing an ``"__id"`` field.
|
|
71
95
|
data (Dict[str, Any]): The new data to replace the existing record data.
|
|
72
96
|
label (Optional[str]): Optional label to re-assign to the record.
|
|
73
97
|
vectors (Optional[List[Dict[str, Any]]]): Optional pre-computed embedding vectors
|
|
@@ -81,9 +105,10 @@ class RecordsAPI(BaseAPI):
|
|
|
81
105
|
Dict[str, str]: Response from the server containing operation status.
|
|
82
106
|
|
|
83
107
|
Raises:
|
|
84
|
-
ValueError: If the
|
|
108
|
+
ValueError: If the target cannot be resolved to a valid record ID.
|
|
85
109
|
RequestError: If the server request fails.
|
|
86
110
|
"""
|
|
111
|
+
record_id = pick_record_id(target)
|
|
87
112
|
headers = Transaction._build_transaction_header(transaction)
|
|
88
113
|
payload: Dict[str, Any] = {"data": data}
|
|
89
114
|
if label is not None:
|
|
@@ -96,7 +121,7 @@ class RecordsAPI(BaseAPI):
|
|
|
96
121
|
|
|
97
122
|
def update(
|
|
98
123
|
self,
|
|
99
|
-
|
|
124
|
+
target: RecordTarget,
|
|
100
125
|
data: Dict[str, Any],
|
|
101
126
|
transaction: Optional[Transaction] = None,
|
|
102
127
|
) -> Dict[str, str]:
|
|
@@ -106,7 +131,8 @@ class RecordsAPI(BaseAPI):
|
|
|
106
131
|
with existing record data without removing existing fields.
|
|
107
132
|
|
|
108
133
|
Args:
|
|
109
|
-
|
|
134
|
+
target (RecordTarget): The record to update. Accepts a UUID string,
|
|
135
|
+
a ``Record`` instance, or a dict containing an ``"__id"`` field.
|
|
110
136
|
data (Dict[str, Any]): The data to merge with the existing record data.
|
|
111
137
|
transaction (Optional[Transaction], optional): Transaction context for the operation.
|
|
112
138
|
If provided, the operation will be part of the transaction. Defaults to None.
|
|
@@ -115,9 +141,10 @@ class RecordsAPI(BaseAPI):
|
|
|
115
141
|
Dict[str, str]: Response from the server containing operation status.
|
|
116
142
|
|
|
117
143
|
Raises:
|
|
118
|
-
ValueError: If the
|
|
144
|
+
ValueError: If the target cannot be resolved to a valid record ID.
|
|
119
145
|
RequestError: If the server request fails.
|
|
120
146
|
"""
|
|
147
|
+
record_id = pick_record_id(target)
|
|
121
148
|
headers = Transaction._build_transaction_header(transaction)
|
|
122
149
|
|
|
123
150
|
return self.client._make_request(
|
|
@@ -383,15 +410,8 @@ class RecordsAPI(BaseAPI):
|
|
|
383
410
|
|
|
384
411
|
def attach(
|
|
385
412
|
self,
|
|
386
|
-
source:
|
|
387
|
-
target:
|
|
388
|
-
str,
|
|
389
|
-
List[str],
|
|
390
|
-
Dict[str, Any],
|
|
391
|
-
List[Dict[str, Any]],
|
|
392
|
-
"Record",
|
|
393
|
-
List["Record"],
|
|
394
|
-
],
|
|
413
|
+
source: RecordTarget,
|
|
414
|
+
target: RelationTarget,
|
|
395
415
|
options: Optional[RelationshipOptions] = None,
|
|
396
416
|
transaction: Optional[Transaction] = None,
|
|
397
417
|
) -> Dict[str, str]:
|
|
@@ -402,16 +422,11 @@ class RecordsAPI(BaseAPI):
|
|
|
402
422
|
record dictionaries, or Record objects.
|
|
403
423
|
|
|
404
424
|
Args:
|
|
405
|
-
source (
|
|
406
|
-
|
|
407
|
-
target (
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
- List of record IDs (List[str])
|
|
411
|
-
- Record dictionary with '__id' field
|
|
412
|
-
- List of record dictionaries
|
|
413
|
-
- Record object
|
|
414
|
-
- List of Record objects
|
|
425
|
+
source (RecordTarget): The source record to attach targets to.
|
|
426
|
+
Accepts a UUID string, a ``Record`` instance, or a dict with ``"__id"``.
|
|
427
|
+
target (RelationTarget): The target record(s) to attach to the source.
|
|
428
|
+
Accepts a single or list of: UUID string, ``Record`` instance, or
|
|
429
|
+
dict with ``"__id"``.
|
|
415
430
|
options (Optional[RelationshipOptions], optional): Additional options for the relationship.
|
|
416
431
|
Defaults to None.
|
|
417
432
|
transaction (Optional[Transaction], optional): Transaction context for the operation.
|
|
@@ -445,15 +460,8 @@ class RecordsAPI(BaseAPI):
|
|
|
445
460
|
|
|
446
461
|
def detach(
|
|
447
462
|
self,
|
|
448
|
-
source:
|
|
449
|
-
target:
|
|
450
|
-
str,
|
|
451
|
-
List[str],
|
|
452
|
-
Dict[str, Any],
|
|
453
|
-
List[Dict[str, Any]],
|
|
454
|
-
"Record",
|
|
455
|
-
List["Record"],
|
|
456
|
-
],
|
|
463
|
+
source: RecordTarget,
|
|
464
|
+
target: RelationTarget,
|
|
457
465
|
options: Optional[RelationshipDetachOptions] = None,
|
|
458
466
|
transaction: Optional[Transaction] = None,
|
|
459
467
|
) -> Dict[str, str]:
|
|
@@ -464,16 +472,11 @@ class RecordsAPI(BaseAPI):
|
|
|
464
472
|
record dictionaries, or Record objects.
|
|
465
473
|
|
|
466
474
|
Args:
|
|
467
|
-
source (
|
|
468
|
-
|
|
469
|
-
target (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
- List of record IDs (List[str])
|
|
473
|
-
- Record dictionary with '__id' field
|
|
474
|
-
- List of record dictionaries
|
|
475
|
-
- Record object
|
|
476
|
-
- List of Record objects
|
|
475
|
+
source (RecordTarget): The source record to detach targets from.
|
|
476
|
+
Accepts a UUID string, a ``Record`` instance, or a dict with ``"__id"``.
|
|
477
|
+
target (RelationTarget): The target record(s) to detach from the source.
|
|
478
|
+
Accepts a single or list of: UUID string, ``Record`` instance, or
|
|
479
|
+
dict with ``"__id"``.
|
|
477
480
|
options (Optional[RelationshipDetachOptions], optional): Additional options for the detach operation.
|
|
478
481
|
Defaults to None.
|
|
479
482
|
transaction (Optional[Transaction], optional): Transaction context for the operation.
|
|
@@ -550,7 +553,7 @@ class RecordsAPI(BaseAPI):
|
|
|
550
553
|
|
|
551
554
|
def delete_by_id(
|
|
552
555
|
self,
|
|
553
|
-
|
|
556
|
+
target: Union[RecordTarget, List[RecordTarget]],
|
|
554
557
|
transaction: Optional[Transaction] = None,
|
|
555
558
|
) -> Dict[str, str]:
|
|
556
559
|
"""Delete one or more records by their unique identifiers.
|
|
@@ -559,8 +562,9 @@ class RecordsAPI(BaseAPI):
|
|
|
559
562
|
deletion and bulk deletion of multiple records.
|
|
560
563
|
|
|
561
564
|
Args:
|
|
562
|
-
|
|
563
|
-
|
|
565
|
+
target (Union[RecordTarget, List[RecordTarget]]): The record(s) to delete.
|
|
566
|
+
Accepts a UUID string, a ``Record`` instance, a dict containing
|
|
567
|
+
``"__id"``, or a list of any of those forms.
|
|
564
568
|
transaction (Optional[Transaction], optional): Transaction context for the operation.
|
|
565
569
|
If provided, the operation will be part of the transaction. Defaults to None.
|
|
566
570
|
|
|
@@ -569,39 +573,42 @@ class RecordsAPI(BaseAPI):
|
|
|
569
573
|
information about the deletion operation.
|
|
570
574
|
|
|
571
575
|
Raises:
|
|
572
|
-
ValueError: If any
|
|
576
|
+
ValueError: If any target cannot be resolved to a valid record ID.
|
|
573
577
|
RequestError: If the server request fails.
|
|
574
578
|
|
|
575
579
|
Note:
|
|
576
|
-
When deleting multiple records (list
|
|
580
|
+
When deleting multiple records (list), the operation uses a
|
|
577
581
|
batch delete with a limit of 1000 records. For single record deletion,
|
|
578
582
|
it uses a direct DELETE request.
|
|
579
583
|
|
|
580
584
|
Example:
|
|
581
585
|
>>> records_api = RecordsAPI(client)
|
|
582
|
-
>>> # Delete a single record
|
|
586
|
+
>>> # Delete a single record — string, Record object, or dict all work
|
|
583
587
|
>>> response = records_api.delete_by_id("record_123")
|
|
588
|
+
>>> response = records_api.delete_by_id(my_record)
|
|
584
589
|
>>>
|
|
585
590
|
>>> # Delete multiple records
|
|
586
|
-
>>>
|
|
587
|
-
>>> response = records_api.delete_by_id(
|
|
591
|
+
>>> response = records_api.delete_by_id(["record_123", "record_456"])
|
|
592
|
+
>>> response = records_api.delete_by_id([record_a, record_b])
|
|
588
593
|
"""
|
|
589
594
|
headers = Transaction._build_transaction_header(transaction)
|
|
590
595
|
|
|
591
|
-
if isinstance(
|
|
596
|
+
if isinstance(target, list):
|
|
597
|
+
ids = self._extract_target_ids(target)
|
|
592
598
|
return self.client._make_request(
|
|
593
599
|
"POST",
|
|
594
600
|
"/records/delete",
|
|
595
|
-
{"limit": 1000, "where": {"$id": {"$in":
|
|
601
|
+
{"limit": 1000, "where": {"$id": {"$in": ids}}},
|
|
596
602
|
headers,
|
|
597
603
|
)
|
|
604
|
+
record_id = pick_record_id(target)
|
|
598
605
|
return self.client._make_request(
|
|
599
|
-
"DELETE", f"/records/{
|
|
606
|
+
"DELETE", f"/records/{record_id}", None, headers
|
|
600
607
|
)
|
|
601
608
|
|
|
602
609
|
def find_by_id(
|
|
603
610
|
self,
|
|
604
|
-
|
|
611
|
+
target: Union[RecordTarget, List[RecordTarget]],
|
|
605
612
|
transaction: Optional[Transaction] = None,
|
|
606
613
|
) -> Union["Record", RecordSearchResult]:
|
|
607
614
|
"""Retrieve one or more records by their unique identifiers.
|
|
@@ -609,17 +616,19 @@ class RecordsAPI(BaseAPI):
|
|
|
609
616
|
Mirrors the TypeScript SDK ``records.findById`` method.
|
|
610
617
|
|
|
611
618
|
Args:
|
|
612
|
-
|
|
619
|
+
target: A single record reference (UUID string, ``Record`` instance, or
|
|
620
|
+
dict with ``"__id"``), or a list of any of those forms.
|
|
613
621
|
transaction: Optional transaction context for the operation.
|
|
614
622
|
|
|
615
623
|
Returns:
|
|
616
|
-
A single :class:`Record` when ``
|
|
617
|
-
:class:`RecordSearchResult` when ``
|
|
624
|
+
A single :class:`Record` when ``target`` is a single reference, or a
|
|
625
|
+
:class:`RecordSearchResult` when ``target`` is a list.
|
|
618
626
|
"""
|
|
619
627
|
headers = Transaction._build_transaction_header(transaction)
|
|
620
|
-
if isinstance(
|
|
628
|
+
if isinstance(target, list):
|
|
629
|
+
ids = self._extract_target_ids(target)
|
|
621
630
|
response = self.client._make_request(
|
|
622
|
-
"POST", "/records", {"ids":
|
|
631
|
+
"POST", "/records", {"ids": ids}, headers
|
|
623
632
|
)
|
|
624
633
|
records = [Record(self.client, r) for r in (response.get("data") or [])]
|
|
625
634
|
return RecordSearchResult(
|
|
@@ -627,21 +636,25 @@ class RecordsAPI(BaseAPI):
|
|
|
627
636
|
total=response.get("total", len(records)),
|
|
628
637
|
client=self.client,
|
|
629
638
|
)
|
|
639
|
+
record_id = pick_record_id(target)
|
|
630
640
|
response = self.client._make_request(
|
|
631
|
-
"GET", f"/records/{
|
|
641
|
+
"GET", f"/records/{record_id}", None, headers
|
|
632
642
|
)
|
|
633
643
|
return Record(self.client, response.get("data", response))
|
|
634
644
|
|
|
635
645
|
def find(
|
|
636
646
|
self,
|
|
637
647
|
search_query: Optional[SearchQuery] = None,
|
|
638
|
-
record_id: Optional[
|
|
648
|
+
record_id: Optional[RecordTarget] = None,
|
|
639
649
|
transaction: Optional[Transaction] = None,
|
|
640
650
|
) -> RecordSearchResult:
|
|
641
651
|
try:
|
|
642
652
|
headers = Transaction._build_transaction_header(transaction)
|
|
643
653
|
|
|
644
|
-
|
|
654
|
+
resolved_id = pick_record_id(record_id) if record_id is not None else None
|
|
655
|
+
path = (
|
|
656
|
+
f"/records/{resolved_id}/search" if resolved_id else "/records/search"
|
|
657
|
+
)
|
|
645
658
|
response = self.client._make_request(
|
|
646
659
|
"POST",
|
|
647
660
|
path,
|
|
@@ -825,14 +838,7 @@ class RecordsAPI(BaseAPI):
|
|
|
825
838
|
|
|
826
839
|
@staticmethod
|
|
827
840
|
def _extract_target_ids(
|
|
828
|
-
target: Union[
|
|
829
|
-
str,
|
|
830
|
-
List[str],
|
|
831
|
-
Dict[str, Any],
|
|
832
|
-
List[Dict[str, Any]],
|
|
833
|
-
"Record",
|
|
834
|
-
List["Record"],
|
|
835
|
-
],
|
|
841
|
+
target: Union[RecordTarget, List[RecordTarget]],
|
|
836
842
|
) -> List[str]:
|
|
837
843
|
"""Extract record IDs from various input types and formats.
|
|
838
844
|
|
|
@@ -841,14 +847,9 @@ class RecordsAPI(BaseAPI):
|
|
|
841
847
|
types commonly used throughout the API for specifying target records.
|
|
842
848
|
|
|
843
849
|
Args:
|
|
844
|
-
target (Union[
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
- List[str]: List of record IDs
|
|
848
|
-
- Dict[str, Any]: Record dictionary containing '__id' field
|
|
849
|
-
- List[Dict[str, Any]]: List of record dictionaries with '__id' fields
|
|
850
|
-
- Record: Record object with data containing '__id'
|
|
851
|
-
- List[Record]: List of Record objects
|
|
850
|
+
target (Union[RecordTarget, List[RecordTarget]]): The target input to
|
|
851
|
+
extract IDs from. Accepts a UUID string, a ``Record`` instance, a
|
|
852
|
+
dict with ``"__id"``, or a list of any of those forms.
|
|
852
853
|
|
|
853
854
|
Returns:
|
|
854
855
|
List[str]: List of extracted record ID strings.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Relationship pattern suggestions API for the RushDB Python SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
+
|
|
5
|
+
from ..models.api_response import ApiResponse
|
|
6
|
+
from .base import BaseAPI
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..client import RushDB
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RelationshipPatternsAPI(BaseAPI):
|
|
13
|
+
"""Review and manage relationship patterns inferred from project ontology.
|
|
14
|
+
|
|
15
|
+
Accessed via ``db.relationships.patterns``.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: "RushDB"):
|
|
19
|
+
super().__init__(client)
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _wrap(response: Dict[str, Any]) -> ApiResponse:
|
|
23
|
+
return ApiResponse(
|
|
24
|
+
data=response.get("data"),
|
|
25
|
+
success=response.get("success", True),
|
|
26
|
+
total=response.get("total"),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def list(self) -> ApiResponse:
|
|
30
|
+
"""List inferred patterns, ontology relationships, and analysis status."""
|
|
31
|
+
response = self.client._make_request("GET", "/relationships/patterns")
|
|
32
|
+
return self._wrap(response)
|
|
33
|
+
|
|
34
|
+
def analyze(self) -> ApiResponse:
|
|
35
|
+
"""Queue ontology analysis to generate relationship pattern suggestions."""
|
|
36
|
+
response = self.client._make_request(
|
|
37
|
+
"POST", "/relationships/patterns/analyze", {}
|
|
38
|
+
)
|
|
39
|
+
return self._wrap(response)
|
|
40
|
+
|
|
41
|
+
def approve(self, pattern_id: str) -> ApiResponse:
|
|
42
|
+
"""Approve and apply a suggested relationship pattern."""
|
|
43
|
+
response = self.client._make_request(
|
|
44
|
+
"POST", f"/relationships/patterns/{pattern_id}/approve", {}
|
|
45
|
+
)
|
|
46
|
+
return self._wrap(response)
|
|
47
|
+
|
|
48
|
+
def ignore(self, pattern_id: str) -> ApiResponse:
|
|
49
|
+
"""Ignore a suggested relationship pattern without applying it."""
|
|
50
|
+
response = self.client._make_request(
|
|
51
|
+
"POST", f"/relationships/patterns/{pattern_id}/ignore", {}
|
|
52
|
+
)
|
|
53
|
+
return self._wrap(response)
|
|
54
|
+
|
|
55
|
+
def delete(self, pattern_id: str, *, delete_existing: bool = False) -> ApiResponse:
|
|
56
|
+
"""Delete a saved pattern and optionally its materialized relationships."""
|
|
57
|
+
params = {"deleteExisting": "true"} if delete_existing else None
|
|
58
|
+
response = self.client._make_request(
|
|
59
|
+
"DELETE", f"/relationships/patterns/{pattern_id}", params=params
|
|
60
|
+
)
|
|
61
|
+
return self._wrap(response)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import typing
|
|
2
|
-
from typing import List, Optional, TypedDict, Union
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional, TypedDict, Union
|
|
3
3
|
from urllib.parse import urlencode
|
|
4
4
|
|
|
5
5
|
from ..models.relationship import Relationship
|
|
6
6
|
from ..models.search_query import SearchQuery
|
|
7
7
|
from ..models.transaction import Transaction
|
|
8
8
|
from .base import BaseAPI
|
|
9
|
+
from .relationship_patterns import RelationshipPatternsAPI
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ..client import RushDB
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class PaginationParams(TypedDict, total=False):
|
|
@@ -60,6 +64,10 @@ class RelationsAPI(BaseAPI):
|
|
|
60
64
|
>>> page_1 = await relations_api.find(pagination=pagination)
|
|
61
65
|
"""
|
|
62
66
|
|
|
67
|
+
def __init__(self, client: "RushDB"):
|
|
68
|
+
super().__init__(client)
|
|
69
|
+
self.patterns = RelationshipPatternsAPI(client)
|
|
70
|
+
|
|
63
71
|
def find(
|
|
64
72
|
self,
|
|
65
73
|
search_query: Optional[SearchQuery] = None,
|
|
@@ -34,7 +34,7 @@ class _SettingsNamespace:
|
|
|
34
34
|
Returns:
|
|
35
35
|
Dict[str, Any]: Current project settings object.
|
|
36
36
|
"""
|
|
37
|
-
response = self._client._make_request("GET", "/settings")
|
|
37
|
+
response = self._client._make_request("GET", "/sdk/settings")
|
|
38
38
|
return response.get("data", response)
|
|
39
39
|
|
|
40
40
|
|
|
@@ -288,7 +288,7 @@ class RushDB:
|
|
|
288
288
|
... return client
|
|
289
289
|
"""
|
|
290
290
|
try:
|
|
291
|
-
self._make_request("GET", "/settings")
|
|
291
|
+
self._make_request("GET", "/sdk/settings")
|
|
292
292
|
return True
|
|
293
293
|
except RushDBError:
|
|
294
294
|
return False
|
|
@@ -7,6 +7,17 @@ from .transaction import Transaction
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from ..client import RushDB
|
|
9
9
|
|
|
10
|
+
# RecordTarget mirrors the JS SDK's DBRecordTarget type:
|
|
11
|
+
# a record can be identified by a UUID string, a dict containing "__id", or a Record instance.
|
|
12
|
+
RecordTarget = Union[str, Dict[str, Any], "Record"]
|
|
13
|
+
|
|
14
|
+
# RelationTarget mirrors the JS SDK's RelationTarget type:
|
|
15
|
+
# one or more records used as the target in attach/detach operations.
|
|
16
|
+
RelationTarget = Union[
|
|
17
|
+
"RecordTarget",
|
|
18
|
+
List["RecordTarget"],
|
|
19
|
+
]
|
|
20
|
+
|
|
10
21
|
|
|
11
22
|
class Record:
|
|
12
23
|
"""Represents a record in RushDB with methods for manipulation."""
|
|
@@ -74,14 +85,7 @@ class Record:
|
|
|
74
85
|
|
|
75
86
|
def attach(
|
|
76
87
|
self,
|
|
77
|
-
target:
|
|
78
|
-
str,
|
|
79
|
-
List[str],
|
|
80
|
-
Dict[str, Any],
|
|
81
|
-
List[Dict[str, Any]],
|
|
82
|
-
"Record",
|
|
83
|
-
List["Record"],
|
|
84
|
-
],
|
|
88
|
+
target: "RelationTarget",
|
|
85
89
|
options: Optional[RelationshipOptions] = None,
|
|
86
90
|
transaction: Optional[Transaction] = None,
|
|
87
91
|
) -> Dict[str, str]:
|
|
@@ -90,14 +94,7 @@ class Record:
|
|
|
90
94
|
|
|
91
95
|
def detach(
|
|
92
96
|
self,
|
|
93
|
-
target:
|
|
94
|
-
str,
|
|
95
|
-
List[str],
|
|
96
|
-
Dict[str, Any],
|
|
97
|
-
List[Dict[str, Any]],
|
|
98
|
-
"Record",
|
|
99
|
-
List["Record"],
|
|
100
|
-
],
|
|
97
|
+
target: "RelationTarget",
|
|
101
98
|
options: Optional[RelationshipDetachOptions] = None,
|
|
102
99
|
transaction: Optional[Transaction] = None,
|
|
103
100
|
) -> Dict[str, str]:
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
3
|
+
from src.rushdb.api.relationship_patterns import RelationshipPatternsAPI
|
|
4
|
+
from src.rushdb.api.relationships import RelationsAPI
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_relationships_exposes_patterns_namespace():
|
|
8
|
+
client = Mock()
|
|
9
|
+
|
|
10
|
+
relationships = RelationsAPI(client)
|
|
11
|
+
|
|
12
|
+
assert isinstance(relationships.patterns, RelationshipPatternsAPI)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_list_relationship_patterns():
|
|
16
|
+
client = Mock()
|
|
17
|
+
client._make_request.return_value = {
|
|
18
|
+
"data": {"patterns": [], "relationships": [], "analysis": {"status": "idle"}},
|
|
19
|
+
"success": True,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
response = RelationsAPI(client).patterns.list()
|
|
23
|
+
|
|
24
|
+
client._make_request.assert_called_once_with("GET", "/relationships/patterns")
|
|
25
|
+
assert response.data["patterns"] == []
|
|
26
|
+
assert response.data["analysis"]["status"] == "idle"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_mutate_relationship_patterns():
|
|
30
|
+
client = Mock()
|
|
31
|
+
client._make_request.return_value = {"data": {"deleted": True}, "success": True}
|
|
32
|
+
patterns = RelationsAPI(client).patterns
|
|
33
|
+
|
|
34
|
+
patterns.analyze()
|
|
35
|
+
client._make_request.assert_called_with(
|
|
36
|
+
"POST", "/relationships/patterns/analyze", {}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
patterns.approve("pattern-id")
|
|
40
|
+
client._make_request.assert_called_with(
|
|
41
|
+
"POST", "/relationships/patterns/pattern-id/approve", {}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
patterns.ignore("pattern-id")
|
|
45
|
+
client._make_request.assert_called_with(
|
|
46
|
+
"POST", "/relationships/patterns/pattern-id/ignore", {}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
patterns.delete("pattern-id", delete_existing=True)
|
|
50
|
+
client._make_request.assert_called_with(
|
|
51
|
+
"DELETE",
|
|
52
|
+
"/relationships/patterns/pattern-id",
|
|
53
|
+
params={"deleteExisting": "true"},
|
|
54
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 3
|
|
3
3
|
requires-python = ">=3.8"
|
|
4
4
|
resolution-markers = [
|
|
5
5
|
"python_full_version >= '3.10'",
|
|
@@ -785,7 +785,7 @@ wheels = [
|
|
|
785
785
|
|
|
786
786
|
[[package]]
|
|
787
787
|
name = "rushdb"
|
|
788
|
-
version = "2.
|
|
788
|
+
version = "2.2.0"
|
|
789
789
|
source = { editable = "." }
|
|
790
790
|
dependencies = [
|
|
791
791
|
{ name = "python-dotenv", version = "1.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|