sop4py 2.0.0__py3-none-any.whl
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.
- sop/__init__.py +3 -0
- sop/btree.py +612 -0
- sop/call_go.py +304 -0
- sop/context.py +33 -0
- sop/libjsondb_amd64darwin.dylib +0 -0
- sop/libjsondb_amd64darwin.h +120 -0
- sop/libjsondb_amd64linux.h +120 -0
- sop/libjsondb_amd64linux.so +0 -0
- sop/libjsondb_amd64windows.dll +0 -0
- sop/libjsondb_amd64windows.h +120 -0
- sop/libjsondb_arm64darwin.dylib +0 -0
- sop/libjsondb_arm64darwin.h +120 -0
- sop/libjsondb_arm64linux.h +120 -0
- sop/libjsondb_arm64linux.so +0 -0
- sop/redis.py +40 -0
- sop/test_btree.py +479 -0
- sop/test_btree_idx.py +86 -0
- sop/transaction.py +167 -0
- sop4py-2.0.0.dist-info/METADATA +124 -0
- sop4py-2.0.0.dist-info/RECORD +22 -0
- sop4py-2.0.0.dist-info/WHEEL +5 -0
- sop4py-2.0.0.dist-info/top_level.txt +1 -0
sop/__init__.py
ADDED
sop/btree.py
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import uuid
|
|
3
|
+
import logging
|
|
4
|
+
import call_go
|
|
5
|
+
import context
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
from typing import TypeVar, Generic, Type
|
|
10
|
+
from dataclasses import dataclass, asdict
|
|
11
|
+
|
|
12
|
+
from transaction import Transaction, TransactionError
|
|
13
|
+
|
|
14
|
+
# Define TypeVars 'TK' & 'TV' to represent Key & Value generic types.
|
|
15
|
+
TK = TypeVar("TK")
|
|
16
|
+
TV = TypeVar("TV")
|
|
17
|
+
|
|
18
|
+
from datetime import timedelta
|
|
19
|
+
|
|
20
|
+
from enum import Enum, auto
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ValueDataSize(Enum):
|
|
24
|
+
Small = 0
|
|
25
|
+
Medium = 1
|
|
26
|
+
Big = 2
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PagingDirection(Enum):
|
|
30
|
+
Forward = 0
|
|
31
|
+
Backward = 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class PagingInfo:
|
|
36
|
+
"""
|
|
37
|
+
Paging Info is used to package paging details to SOP.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Offset defaults to 0, meaning, fetch from current cursor position in SOP backend.
|
|
41
|
+
page_offset: int = 0
|
|
42
|
+
# Fetch size of 20 is default.
|
|
43
|
+
page_size: int = 20
|
|
44
|
+
# Count of elements to fetch starting with the page offset. If left 0, 'will fetch PageSize number of elements
|
|
45
|
+
# after traversing the B-tree, bringing the cursor to the requested page offset.
|
|
46
|
+
# Otherwise, will fetch FetchCount number of data elements starting from the page offset.
|
|
47
|
+
fetch_count: int = 0
|
|
48
|
+
# Fetch direction of forward(0) is default.
|
|
49
|
+
direction: int = 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class CacheConfig:
|
|
54
|
+
"""
|
|
55
|
+
Cache config specify the options available for caching in B-tree.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
# Registry cache duration in minutes.
|
|
59
|
+
registry_cache_duration: int = 10
|
|
60
|
+
is_registry_cache_ttl: bool = False
|
|
61
|
+
node_cache_duration: int = 5
|
|
62
|
+
is_node_cache_ttl: bool = False
|
|
63
|
+
store_info_cache_duration: int = 5
|
|
64
|
+
is_store_info_cache_ttl: bool = False
|
|
65
|
+
value_data_cache_duration: int = 0
|
|
66
|
+
is_value_data_cache_ttl: bool = False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class IndexFieldSpecification:
|
|
71
|
+
"""
|
|
72
|
+
Make sure to specify correct field name so Key data comparer will be able to make a good comparison
|
|
73
|
+
between two Items when organizing Key/Value item pairs in the b-tree.
|
|
74
|
+
|
|
75
|
+
Otherwise it will treat all of item's to have the same key field value and thus, will affect both
|
|
76
|
+
performance and give incorrect ordering of the items.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
field_name: str
|
|
80
|
+
ascending_sort_order: bool = True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class IndexSpecification:
|
|
85
|
+
"""
|
|
86
|
+
Index Specification lists the fields comprising the index on the Key class & their sort order.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
index_fields: IndexFieldSpecification = None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class BtreeOptions:
|
|
94
|
+
"""
|
|
95
|
+
Btree options specify the options available for making a B-tree.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
name: str
|
|
99
|
+
is_unique: bool = False
|
|
100
|
+
slot_length: int = 500
|
|
101
|
+
description: str = ""
|
|
102
|
+
is_value_data_in_node_segment: bool = True
|
|
103
|
+
is_value_data_actively_persisted: bool = False
|
|
104
|
+
is_value_data_globally_cached: bool = False
|
|
105
|
+
index_specification: str = ""
|
|
106
|
+
transaction_id: str = str(uuid.UUID(int=0))
|
|
107
|
+
cache_config: CacheConfig = None
|
|
108
|
+
is_primitive_key: bool = True
|
|
109
|
+
leaf_load_balancing: bool = False
|
|
110
|
+
|
|
111
|
+
def set_value_data_size(self, s: ValueDataSize):
|
|
112
|
+
if s == ValueDataSize.Medium:
|
|
113
|
+
self.is_value_data_actively_persisted = False
|
|
114
|
+
self.is_value_data_globally_cached = True
|
|
115
|
+
self.is_value_data_in_node_segment = False
|
|
116
|
+
if s == ValueDataSize.Big:
|
|
117
|
+
self.is_value_data_actively_persisted = True
|
|
118
|
+
self.is_value_data_globally_cached = False
|
|
119
|
+
self.is_value_data_in_node_segment = False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class Item(Generic[TK, TV]):
|
|
124
|
+
key: TK
|
|
125
|
+
value: TV = None
|
|
126
|
+
id: str = str(uuid.UUID(int=0))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class BtreeError(TransactionError):
|
|
130
|
+
"""Exception for Btree-related errors, 'is derived from TransactionError."""
|
|
131
|
+
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass
|
|
136
|
+
class ManageBtreeMetaData(Generic[TK, TV]):
|
|
137
|
+
is_primitive_key: bool
|
|
138
|
+
transaction_id: str
|
|
139
|
+
btree_id: str
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class ManageBtreePayload(Generic[TK, TV]):
|
|
144
|
+
items: Item[TK, TV]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class BtreeAction(Enum):
|
|
148
|
+
NewBtree = auto()
|
|
149
|
+
OpenBtree = auto()
|
|
150
|
+
Add = auto()
|
|
151
|
+
AddIfNotExist = auto()
|
|
152
|
+
Update = auto()
|
|
153
|
+
Upsert = auto()
|
|
154
|
+
Remove = auto()
|
|
155
|
+
Find = auto()
|
|
156
|
+
FindWithID = auto()
|
|
157
|
+
GetItems = auto()
|
|
158
|
+
GetValues = auto()
|
|
159
|
+
GetKeys = auto()
|
|
160
|
+
First = auto()
|
|
161
|
+
Last = auto()
|
|
162
|
+
IsUnique = auto()
|
|
163
|
+
Count = auto()
|
|
164
|
+
GetStoreInfo = auto()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Btree(Generic[TK, TV]):
|
|
168
|
+
"""
|
|
169
|
+
B-tree manager. See "new" & "open" class methods below for details how to use.
|
|
170
|
+
Delegates API calls to the SOP library that does Direct IO to disk drives w/ built-in L1/L2 caching.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
Generic (TK, TV): TK - type of the Key part. TV - type of the Value part.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
transaction_id: uuid.uuid4
|
|
177
|
+
id: uuid.uuid4
|
|
178
|
+
|
|
179
|
+
def __init__(self, id: uuid.uuid4, is_primitive_key: bool, tid: uuid.uuid4):
|
|
180
|
+
self.id = id
|
|
181
|
+
self.is_primitive_key = is_primitive_key
|
|
182
|
+
self.transaction_id = tid
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def new(
|
|
186
|
+
cls: Type["Btree[TK,TV]"],
|
|
187
|
+
ctx: context.Context,
|
|
188
|
+
options: BtreeOptions,
|
|
189
|
+
trans: Transaction,
|
|
190
|
+
index_spec: IndexSpecification = None,
|
|
191
|
+
) -> "Btree[TK,TV]":
|
|
192
|
+
"""Create a new B-tree store in the backend storage with the options specified then returns an instance
|
|
193
|
+
of Python Btree (facade) that can let caller code to manage or search/fetch the items of the store.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
cls (Type["Btree[TK,TV]"]): Supports generics for Key (TK) & Value (TV) pair.
|
|
197
|
+
ctx (context.Context): context.Context object, useful for telling SOP in the backend the ID of the context for use in calls.
|
|
198
|
+
options (BtreeOptions): _description_
|
|
199
|
+
trans (Transaction): instance of a Transaction that the B-tree store to be opened belongs.
|
|
200
|
+
index_spec (IndexSpecification, optional): Defaults to None.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
BtreeError: error message pertaining to creation of a b-tree store related in the backend.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Btree[TK,TV]: B-tree instance
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
options.transaction_id = str(trans.transaction_id)
|
|
210
|
+
if index_spec is not None:
|
|
211
|
+
options.index_specification = json.dumps(asdict(index_spec))
|
|
212
|
+
|
|
213
|
+
res = call_go.manage_btree(
|
|
214
|
+
ctx.id, BtreeAction.NewBtree.value, json.dumps(asdict(options)), None
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if res == None:
|
|
218
|
+
raise BtreeError("unable to create a Btree in SOP")
|
|
219
|
+
try:
|
|
220
|
+
b3id = uuid.UUID(res)
|
|
221
|
+
except:
|
|
222
|
+
# if res can't be converted to UUID, it is expected to be an error msg from SOP.
|
|
223
|
+
raise BtreeError(res)
|
|
224
|
+
|
|
225
|
+
return cls(b3id, options.is_primitive_key, trans.transaction_id)
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def open(
|
|
229
|
+
cls: Type["Btree[TK,TV]"], ctx: context.Context, name: str, trans: Transaction
|
|
230
|
+
) -> "Btree[TK,TV]":
|
|
231
|
+
"""Open an existing B-tree store on the backend and returns a B-tree manager that can allow code to do operations on it.
|
|
232
|
+
Args:
|
|
233
|
+
cls (Type["Btree[TK,TV]"]): Supports generics for Key (TK) & Value (TV) pair.
|
|
234
|
+
ctx (context.Context): context.Context object, useful for telling SOP in the backend the ID of the context for use in calls.
|
|
235
|
+
name (str): Name of the B-tree store to open.
|
|
236
|
+
trans (Transaction): instance of a Transaction that the B-tree store to be opened belongs.
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
BtreeError: error message generated when calling different methods of the B-tree.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Btree[TK,TV]: Btree instance
|
|
243
|
+
"""
|
|
244
|
+
options: BtreeOptions = BtreeOptions(name=name)
|
|
245
|
+
options.transaction_id = str(trans.transaction_id)
|
|
246
|
+
res = call_go.manage_btree(
|
|
247
|
+
ctx.id, BtreeAction.OpenBtree.value, json.dumps(asdict(options)), ""
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
if res == None:
|
|
251
|
+
raise BtreeError(f"unable to open a Btree:{name} in SOP")
|
|
252
|
+
try:
|
|
253
|
+
b3id = uuid.UUID(res)
|
|
254
|
+
except:
|
|
255
|
+
# if res can't be converted to UUID, it is expected to be an error msg from SOP.
|
|
256
|
+
raise BtreeError(res)
|
|
257
|
+
|
|
258
|
+
return cls(b3id, False, trans.transaction_id)
|
|
259
|
+
|
|
260
|
+
def add(self, ctx: context.Context, items: Item[TK, TV]) -> bool:
|
|
261
|
+
return self._manage(ctx, BtreeAction.Add.value, items)
|
|
262
|
+
|
|
263
|
+
def add_if_not_exists(self, ctx: context.Context, items: Item[TK, TV]) -> bool:
|
|
264
|
+
return self._manage(ctx, BtreeAction.AddIfNotExist.value, items)
|
|
265
|
+
|
|
266
|
+
def update(self, ctx: context.Context, items: Item[TK, TV]) -> bool:
|
|
267
|
+
return self._manage(ctx, BtreeAction.Update.value, items)
|
|
268
|
+
|
|
269
|
+
def upsert(self, ctx: context.Context, items: Item[TK, TV]) -> bool:
|
|
270
|
+
return self._manage(ctx, BtreeAction.Upsert.value, items)
|
|
271
|
+
|
|
272
|
+
def remove(self, ctx: context.Context, keys: TK) -> bool:
|
|
273
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
274
|
+
is_primitive_key=self.is_primitive_key,
|
|
275
|
+
btree_id=str(self.id),
|
|
276
|
+
transaction_id=str(self.transaction_id),
|
|
277
|
+
)
|
|
278
|
+
res = call_go.manage_btree(
|
|
279
|
+
ctx.id,
|
|
280
|
+
BtreeAction.Remove.value,
|
|
281
|
+
json.dumps(asdict(metadata)),
|
|
282
|
+
json.dumps(asdict(keys)),
|
|
283
|
+
)
|
|
284
|
+
if res == None:
|
|
285
|
+
raise BtreeError(
|
|
286
|
+
f"unable to remove item w/ key:{keys} from a Btree:{self.id} in SOP"
|
|
287
|
+
)
|
|
288
|
+
return self._to_bool(res)
|
|
289
|
+
|
|
290
|
+
def get_items(
|
|
291
|
+
self,
|
|
292
|
+
ctx: context.Context,
|
|
293
|
+
pagingInfo: PagingInfo,
|
|
294
|
+
) -> Item[TK, TV]:
|
|
295
|
+
"""Fetch items from the B-tree store.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
ctx (context.Context): context.Context object
|
|
299
|
+
pagingInfo (PagingInfo): Paging details that describe how to navigate(walk the b-tree) & fetch a batch of items.
|
|
300
|
+
|
|
301
|
+
Returns: Item[TK, TV]: the fetched batch of items.
|
|
302
|
+
"""
|
|
303
|
+
return self._get(ctx, BtreeAction.GetItems.value, pagingInfo)
|
|
304
|
+
|
|
305
|
+
# Keys array contains the keys & their IDs to fetch value data of. Both input & output
|
|
306
|
+
# are Item type though input only has Key & ID field populated to find the data & output has Value of found data.
|
|
307
|
+
def get_values(self, ctx: context.Context, keys: Item[TK, TV]) -> Item[TK, TV]:
|
|
308
|
+
"""Fetch items' Value parts from the B-tree store.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
ctx (context.Context): context.Context object
|
|
312
|
+
keys (Item[TK, TV]): Keys that which, their Value parts will be fetched from the store.
|
|
313
|
+
|
|
314
|
+
Raises:
|
|
315
|
+
BtreeError: error message containing details what failed during (Values) fetch.
|
|
316
|
+
|
|
317
|
+
Returns: Item[TK, TV]: items containing the fetched (Values) data.
|
|
318
|
+
"""
|
|
319
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
320
|
+
is_primitive_key=self.is_primitive_key,
|
|
321
|
+
btree_id=str(self.id),
|
|
322
|
+
transaction_id=str(self.transaction_id),
|
|
323
|
+
)
|
|
324
|
+
payload: ManageBtreePayload = ManageBtreePayload(items=keys)
|
|
325
|
+
result, error = call_go.get_from_btree(
|
|
326
|
+
ctx.id,
|
|
327
|
+
BtreeAction.GetValues.value,
|
|
328
|
+
json.dumps(asdict(metadata)),
|
|
329
|
+
json.dumps(asdict(payload)),
|
|
330
|
+
)
|
|
331
|
+
if error is not None:
|
|
332
|
+
if result is None:
|
|
333
|
+
raise BtreeError(error)
|
|
334
|
+
else:
|
|
335
|
+
# just log the error since there are partial results we can return.
|
|
336
|
+
logger.error(error)
|
|
337
|
+
|
|
338
|
+
if result is None:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
data_dicts = json.loads(result)
|
|
342
|
+
return [Item[TK, TV](**data_dict) for data_dict in data_dicts]
|
|
343
|
+
|
|
344
|
+
def get_keys(
|
|
345
|
+
self,
|
|
346
|
+
ctx: context.Context,
|
|
347
|
+
pagingInfo: PagingInfo,
|
|
348
|
+
) -> Item[TK, TV]:
|
|
349
|
+
"""Fetch a set of Keys from the store.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
ctx (context.Context): context.Context object used when calling the SOP b-tree methods.
|
|
353
|
+
pagingInfo (PagingInfo): Paging details specifying how to navigate/walk the b-tree and fetch keys.
|
|
354
|
+
|
|
355
|
+
Returns: Item[TK, TV]: items with the Keys populated with the fetched data.
|
|
356
|
+
"""
|
|
357
|
+
return self._get(ctx, BtreeAction.GetKeys.value, pagingInfo)
|
|
358
|
+
|
|
359
|
+
def find(self, ctx: context.Context, key: TK) -> bool:
|
|
360
|
+
"""Find will navigate the b-tree and stop when the matching item, its Key matches with the key param, is found.
|
|
361
|
+
Or a nearby item if there is no match found.
|
|
362
|
+
This positions the cursor so on succeeding call to one or the fetch methods, get_keys, get_values, get_items, will
|
|
363
|
+
fetch the data (items) starting from this cursor (item) position.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
ctx (context.Context): _description_
|
|
367
|
+
key (TK): key of the item to search for.
|
|
368
|
+
|
|
369
|
+
Raises:
|
|
370
|
+
BtreeError: error message pertaining to the error encountered while searching for the item.
|
|
371
|
+
|
|
372
|
+
Returns: bool: true means the item with such key is found, false otherwise. If true is returned, the cursor is positioned
|
|
373
|
+
to the item with matching key, otherwise to an item with key similar to the requested key.
|
|
374
|
+
"""
|
|
375
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
376
|
+
is_primitive_key=self.is_primitive_key,
|
|
377
|
+
btree_id=str(self.id),
|
|
378
|
+
transaction_id=str(self.transaction_id),
|
|
379
|
+
)
|
|
380
|
+
payload: ManageBtreePayload = ManageBtreePayload(items=(Item(key=key),))
|
|
381
|
+
res = call_go.navigate_btree(
|
|
382
|
+
ctx.id,
|
|
383
|
+
BtreeAction.Find.value,
|
|
384
|
+
json.dumps(asdict(metadata)),
|
|
385
|
+
json.dumps(asdict(payload)),
|
|
386
|
+
)
|
|
387
|
+
if res == None:
|
|
388
|
+
raise BtreeError(
|
|
389
|
+
f"unable to Find using key:{key} the item of a Btree:{self.id} in SOP"
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
return self._to_bool(res)
|
|
393
|
+
|
|
394
|
+
def find_with_id(self, ctx: context.Context, key: TK, id: uuid.uuid4) -> bool:
|
|
395
|
+
"""Similar to Find but which, includes the ID of the item to search for. This is useful when there are duplicated (on key) items.
|
|
396
|
+
Code can then search for the right one despite having many items of same key, based on the item ID.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
ctx (context.Context): _description_
|
|
400
|
+
key (TK): _description_
|
|
401
|
+
id (uuid.uuid4): _description_
|
|
402
|
+
|
|
403
|
+
Raises:
|
|
404
|
+
BtreeError: _description_
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
bool: _description_
|
|
408
|
+
"""
|
|
409
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
410
|
+
is_primitive_key=self.is_primitive_key,
|
|
411
|
+
btree_id=str(self.id),
|
|
412
|
+
transaction_id=str(self.transaction_id),
|
|
413
|
+
)
|
|
414
|
+
payload: ManageBtreePayload = ManageBtreePayload(items=(Item(key=key, id=id),))
|
|
415
|
+
res = call_go.navigate_btree(
|
|
416
|
+
ctx.id,
|
|
417
|
+
BtreeAction.FindWithID.value,
|
|
418
|
+
json.dumps(asdict(metadata)),
|
|
419
|
+
json.dumps(asdict(payload)),
|
|
420
|
+
)
|
|
421
|
+
if res == None:
|
|
422
|
+
raise BtreeError(
|
|
423
|
+
f"unable to Find using key:{key} & ID:{id} the item of a Btree:{self.id} in SOP"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
return self._to_bool(res)
|
|
427
|
+
|
|
428
|
+
def first(
|
|
429
|
+
self,
|
|
430
|
+
ctx: context.Context,
|
|
431
|
+
) -> bool:
|
|
432
|
+
"""Navigate or positions the cursor to the first or top of the b-tree store.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
ctx (context.Context): _description_
|
|
436
|
+
|
|
437
|
+
Raises:
|
|
438
|
+
BtreeError: _description_
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
bool: _description_
|
|
442
|
+
"""
|
|
443
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
444
|
+
is_primitive_key=self.is_primitive_key,
|
|
445
|
+
btree_id=str(self.id),
|
|
446
|
+
transaction_id=str(self.transaction_id),
|
|
447
|
+
)
|
|
448
|
+
res = call_go.navigate_btree(
|
|
449
|
+
ctx.id, BtreeAction.First.value, json.dumps(asdict(metadata)), None
|
|
450
|
+
)
|
|
451
|
+
if res == None:
|
|
452
|
+
raise BtreeError(f"First call failed for Btree:{self.id} in SOP")
|
|
453
|
+
|
|
454
|
+
return self._to_bool(res)
|
|
455
|
+
|
|
456
|
+
def last(
|
|
457
|
+
self,
|
|
458
|
+
ctx: context.Context,
|
|
459
|
+
) -> bool:
|
|
460
|
+
"""Navigate or positions the cursor to the last or bottom of the b-tree store.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
ctx (context.Context): _description_
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
BtreeError: _description_
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
bool: _description_
|
|
470
|
+
"""
|
|
471
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
472
|
+
is_primitive_key=self.is_primitive_key,
|
|
473
|
+
btree_id=str(self.id),
|
|
474
|
+
transaction_id=str(self.transaction_id),
|
|
475
|
+
)
|
|
476
|
+
res = call_go.navigate_btree(
|
|
477
|
+
ctx.id, BtreeAction.Last.value, json.dumps(asdict(metadata)), None
|
|
478
|
+
)
|
|
479
|
+
if res == None:
|
|
480
|
+
raise BtreeError(f"Last call failed for Btree:{self.id} in SOP")
|
|
481
|
+
|
|
482
|
+
return self._to_bool(res)
|
|
483
|
+
|
|
484
|
+
def is_unique(self) -> bool:
|
|
485
|
+
"""true specifies that the b-tree store has no duplicated keyed items. false otherwise.
|
|
486
|
+
|
|
487
|
+
Raises:
|
|
488
|
+
BtreeError: _description_
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
bool: _description_
|
|
492
|
+
"""
|
|
493
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
494
|
+
is_primitive_key=self.is_primitive_key,
|
|
495
|
+
btree_id=str(self.id),
|
|
496
|
+
transaction_id=str(self.transaction_id),
|
|
497
|
+
)
|
|
498
|
+
res = call_go.is_unique_btree(
|
|
499
|
+
json.dumps(asdict(metadata)),
|
|
500
|
+
)
|
|
501
|
+
if res == None:
|
|
502
|
+
raise BtreeError(f"IsUnique call failed for Btree:{self.id} in SOP")
|
|
503
|
+
|
|
504
|
+
return self._to_bool(res)
|
|
505
|
+
|
|
506
|
+
def count(self) -> int:
|
|
507
|
+
"""Returns the count of items in the b-tree store.
|
|
508
|
+
|
|
509
|
+
Raises:
|
|
510
|
+
BtreeError: _description_
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
int: _description_
|
|
514
|
+
"""
|
|
515
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
516
|
+
is_primitive_key=self.is_primitive_key,
|
|
517
|
+
btree_id=str(self.id),
|
|
518
|
+
transaction_id=str(self.transaction_id),
|
|
519
|
+
)
|
|
520
|
+
count, error = call_go.get_btree_item_count(
|
|
521
|
+
json.dumps(asdict(metadata)),
|
|
522
|
+
)
|
|
523
|
+
if error is not None:
|
|
524
|
+
raise BtreeError(error)
|
|
525
|
+
return count
|
|
526
|
+
|
|
527
|
+
def get_store_info(self) -> BtreeOptions:
|
|
528
|
+
"""Returns the b-tree information as specified during creation (Btree.new method call).
|
|
529
|
+
|
|
530
|
+
Raises:
|
|
531
|
+
BtreeError: _description_
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
BtreeOptions: _description_
|
|
535
|
+
"""
|
|
536
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
537
|
+
is_primitive_key=self.is_primitive_key,
|
|
538
|
+
btree_id=str(self.id),
|
|
539
|
+
transaction_id=str(self.transaction_id),
|
|
540
|
+
)
|
|
541
|
+
payload, error = call_go.get_from_btree(
|
|
542
|
+
0, BtreeAction.GetStoreInfo.value, json.dumps(asdict(metadata)), None
|
|
543
|
+
)
|
|
544
|
+
if error is not None:
|
|
545
|
+
raise BtreeError(error)
|
|
546
|
+
|
|
547
|
+
data_dict = json.loads(payload)
|
|
548
|
+
return BtreeOptions(**data_dict)
|
|
549
|
+
|
|
550
|
+
def _manage(self, ctx: context.Context, action: int, items: Item[TK, TV]) -> bool:
|
|
551
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
552
|
+
is_primitive_key=self.is_primitive_key,
|
|
553
|
+
btree_id=str(self.id),
|
|
554
|
+
transaction_id=str(self.transaction_id),
|
|
555
|
+
)
|
|
556
|
+
payload: ManageBtreePayload = ManageBtreePayload(items=items)
|
|
557
|
+
res = call_go.manage_btree(
|
|
558
|
+
ctx.id,
|
|
559
|
+
action,
|
|
560
|
+
json.dumps(asdict(metadata)),
|
|
561
|
+
json.dumps(asdict(payload)),
|
|
562
|
+
)
|
|
563
|
+
if res == None:
|
|
564
|
+
raise BtreeError(f"unable to manage item to a Btree:{self.id} in SOP")
|
|
565
|
+
|
|
566
|
+
return self._to_bool(res)
|
|
567
|
+
|
|
568
|
+
def _get(
|
|
569
|
+
self,
|
|
570
|
+
ctx: context.Context,
|
|
571
|
+
getAction: int,
|
|
572
|
+
pagingInfo: PagingInfo,
|
|
573
|
+
) -> Item[TK, TV]:
|
|
574
|
+
if pagingInfo.direction > PagingDirection.Backward.value:
|
|
575
|
+
pagingInfo.direction = PagingDirection.Backward.value
|
|
576
|
+
if pagingInfo.direction < PagingDirection.Forward.value:
|
|
577
|
+
pagingInfo.direction = PagingDirection.Forward.value
|
|
578
|
+
|
|
579
|
+
metadata: ManageBtreeMetaData = ManageBtreeMetaData(
|
|
580
|
+
is_primitive_key=self.is_primitive_key,
|
|
581
|
+
btree_id=str(self.id),
|
|
582
|
+
transaction_id=str(self.transaction_id),
|
|
583
|
+
)
|
|
584
|
+
result, error = call_go.get_from_btree(
|
|
585
|
+
ctx.id,
|
|
586
|
+
getAction,
|
|
587
|
+
json.dumps(asdict(metadata)),
|
|
588
|
+
json.dumps(asdict(pagingInfo)),
|
|
589
|
+
)
|
|
590
|
+
if error is not None:
|
|
591
|
+
if result is None:
|
|
592
|
+
raise BtreeError(error)
|
|
593
|
+
else:
|
|
594
|
+
# just log the error since there are partial results we can return.
|
|
595
|
+
logger.error(error)
|
|
596
|
+
|
|
597
|
+
if result is None:
|
|
598
|
+
return None
|
|
599
|
+
|
|
600
|
+
data_dicts = json.loads(result)
|
|
601
|
+
return [Item[TK, TV](**data_dict) for data_dict in data_dicts]
|
|
602
|
+
|
|
603
|
+
def _to_bool(self, res: str) -> bool:
|
|
604
|
+
"""
|
|
605
|
+
Converts result string "true" or "false" to bool or an errmsg to raise an error.
|
|
606
|
+
"""
|
|
607
|
+
if res.lower() == "true":
|
|
608
|
+
return True
|
|
609
|
+
if res.lower() == "false":
|
|
610
|
+
return False
|
|
611
|
+
|
|
612
|
+
raise BtreeError(res)
|