Skip to content

Commit e23e38b

Browse files
authored
Merge pull request #451 from hoijnet/fix/add-random-idgen-alias
Fix/add random idgen alias
2 parents 68c5092 + 586aefa commit e23e38b

File tree

4 files changed

+167
-24
lines changed

4 files changed

+167
-24
lines changed

terminusdb_client/tests/test_woqlQuery.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from .woqljson.woqlConcatJson import WOQL_CONCAT_JSON
1616
from .woqljson.woqlIdgenJson import (
1717
WOQL_IDGEN_JSON,
18-
WOQL_RANDOM_IDGEN_JSON,
18+
WOQL_RANDOM_KEY_JSON,
1919
WOQL_UNIQUE_JSON,
2020
)
2121
from .woqljson.woqlJoinSplitJson import WOQL_JOIN_SPLIT_JSON
@@ -214,11 +214,9 @@ def test_idgen_method(self):
214214
woql_object = WOQLQuery().idgen("Station", "v:Start_ID", "v:Start_Station_URL")
215215
assert woql_object.to_dict() == WOQL_IDGEN_JSON
216216

217-
def test_random_idgen_method(self):
218-
woql_object = WOQLQuery().random_idgen(
219-
"Station", "v:Start_ID", "v:Start_Station_URL"
220-
)
221-
assert woql_object.to_dict() == WOQL_RANDOM_IDGEN_JSON
217+
def test_idgen_random_method(self):
218+
woql_object = WOQLQuery().idgen_random("Person/", "v:Person_ID")
219+
assert woql_object.to_dict() == WOQL_RANDOM_KEY_JSON
222220

223221
def test_typecast_method(self):
224222
woql_object = WOQLQuery().typecast(
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""Unit tests for idgen_random WOQL method"""
2+
from terminusdb_client.woqlquery.woql_query import WOQLQuery
3+
4+
5+
class TestIdgenRandom:
6+
"""Test idgen_random WOQL method"""
7+
8+
def test_idgen_random_basic(self):
9+
"""Test basic idgen_random functionality"""
10+
woql = WOQLQuery().idgen_random("Person/", "v:PersonID")
11+
result = woql.to_dict()
12+
13+
assert result["@type"] == "RandomKey"
14+
assert result["base"]["@type"] == "DataValue"
15+
assert result["base"]["data"]["@type"] == "xsd:string"
16+
assert result["base"]["data"]["@value"] == "Person/"
17+
assert result["uri"]["@type"] == "NodeValue"
18+
assert result["uri"]["variable"] == "PersonID"
19+
20+
def test_idgen_random_with_prefix(self):
21+
"""Test idgen_random with different prefix formats"""
22+
woql = WOQLQuery().idgen_random("http://example.org/Person/", "v:ID")
23+
result = woql.to_dict()
24+
25+
assert result["base"]["data"]["@value"] == "http://example.org/Person/"
26+
assert result["uri"]["variable"] == "ID"
27+
28+
def test_idgen_random_chaining(self):
29+
"""Test idgen_random can be chained with other operations"""
30+
woql = (WOQLQuery()
31+
.triple("v:Person", "rdf:type", "@schema:Person")
32+
.idgen_random("Person/", "v:PersonID"))
33+
34+
result = woql.to_dict()
35+
assert result["@type"] == "And"
36+
assert len(result["and"]) == 2
37+
assert result["and"][0]["@type"] == "Triple"
38+
assert result["and"][1]["@type"] == "RandomKey"
39+
40+
def test_idgen_random_multiple_calls(self):
41+
"""Test multiple idgen_random calls in same query"""
42+
woql = (WOQLQuery()
43+
.idgen_random("Person/", "v:PersonID")
44+
.idgen_random("Order/", "v:OrderID"))
45+
46+
result = woql.to_dict()
47+
assert result["@type"] == "And"
48+
assert result["and"][0]["@type"] == "RandomKey"
49+
assert result["and"][0]["base"]["data"]["@value"] == "Person/"
50+
assert result["and"][1]["@type"] == "RandomKey"
51+
assert result["and"][1]["base"]["data"]["@value"] == "Order/"
52+
53+
def test_idgen_random_args_parameter(self):
54+
"""Test idgen_random args parameter returns parameter list"""
55+
result = WOQLQuery().idgen_random("args", "v:ID")
56+
57+
assert result == ["base", "uri"]
58+
59+
def test_idgen_random_empty_prefix(self):
60+
"""Test idgen_random with empty prefix"""
61+
woql = WOQLQuery().idgen_random("", "v:ID")
62+
result = woql.to_dict()
63+
64+
assert result["@type"] == "RandomKey"
65+
assert result["base"]["data"]["@value"] == ""
66+
67+
def test_idgen_random_variable_output(self):
68+
"""Test idgen_random output variable format"""
69+
woql = WOQLQuery().idgen_random("Test/", "v:MyVar")
70+
result = woql.to_dict()
71+
72+
assert result["uri"]["variable"] == "MyVar"
73+
74+
def test_idgen_random_in_query_chain(self):
75+
"""Test idgen_random in complex query chain"""
76+
woql = (WOQLQuery()
77+
.triple("v:Person", "rdf:type", "@schema:Person")
78+
.idgen_random("Person/", "v:PersonID")
79+
.triple("v:PersonID", "@schema:name", "v:Name"))
80+
81+
result = woql.to_dict()
82+
assert result["@type"] == "And"
83+
# WOQLQuery chains create nested And structures
84+
# Verify RandomKey is present in the chain
85+
has_random_key = False
86+
87+
def check_for_random_key(obj):
88+
nonlocal has_random_key
89+
if isinstance(obj, dict):
90+
if obj.get("@type") == "RandomKey":
91+
has_random_key = True
92+
for value in obj.values():
93+
check_for_random_key(value)
94+
elif isinstance(obj, list):
95+
for item in obj:
96+
check_for_random_key(item)
97+
98+
check_for_random_key(result)
99+
assert has_random_key, "RandomKey should be present in query chain"
100+
101+
def test_idgen_random_matches_expected_json(self):
102+
"""Test idgen_random produces expected WOQL JSON structure"""
103+
from terminusdb_client.tests.woqljson.woqlIdgenJson import WOQL_RANDOM_KEY_JSON
104+
105+
woql = WOQLQuery().idgen_random("Person/", "v:Person_ID")
106+
result = woql.to_dict()
107+
108+
assert result == WOQL_RANDOM_KEY_JSON
109+
110+
def test_idgen_random_with_special_characters_in_prefix(self):
111+
"""Test idgen_random handles special characters in prefix"""
112+
woql = WOQLQuery().idgen_random("Test/2024-11/", "v:ID")
113+
result = woql.to_dict()
114+
115+
assert result["base"]["data"]["@value"] == "Test/2024-11/"
116+
117+
def test_idgen_random_preserves_cursor_state(self):
118+
"""Test idgen_random returns self for method chaining"""
119+
woql = WOQLQuery()
120+
result = woql.idgen_random("Test/", "v:ID")
121+
122+
assert result is woql # Should return same object for chaining

terminusdb_client/tests/woqljson/woqlIdgenJson.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,14 @@
2727
},
2828
}
2929

30-
WOQL_RANDOM_IDGEN_JSON = {
30+
WOQL_RANDOM_KEY_JSON = {
3131
"@type": "RandomKey",
3232
"base": {
3333
"@type": "DataValue",
34-
"data": {"@type": "xsd:string", "@value": "Station"},
35-
},
36-
"key_list": {
37-
"@type": "DataValue",
38-
"variable": "Start_ID",
34+
"data": {"@type": "xsd:string", "@value": "Person/"},
3935
},
4036
"uri": {
4137
"@type": "NodeValue",
42-
"variable": "Start_Station_URL",
38+
"variable": "Person_ID",
4339
},
4440
}

terminusdb_client/woqlquery/woql_query.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,38 +2207,65 @@ def idgen(self, prefix, input_var_list, output_var):
22072207
self._cursor["uri"] = self._clean_node_value(output_var)
22082208
return self
22092209

2210-
def random_idgen(self, prefix, key_list, uri):
2211-
"""Randomly generates an ID and appends to the end of the key_list.
2210+
def idgen_random(self, prefix, uri):
2211+
"""Generates a unique ID with cryptographically secure random suffix.
2212+
2213+
Uses base64 encoding to generate 16-character random IDs that are
2214+
guaranteed to be unique across executions. Matches the server's
2215+
idgen_random/2 predicate and maintains consistency with the JavaScript client.
22122216
22132217
Parameters
22142218
----------
22152219
prefix : str
2216-
prefix for the id
2217-
key_list : str
2218-
variable to generate id for
2220+
A prefix for the IDs to be generated (e.g. "Person/")
22192221
uri : str
2220-
the variable to hold the id
2222+
Variable name or output target for the generated ID
22212223
22222224
Returns
22232225
-------
22242226
WOQLQuery object
2225-
query object that can be chained and/or execute
2227+
query object that can be chained and/or executed
22262228
22272229
Examples
22282230
-------
2229-
>>> WOQLQuery().random_idgen("https://base.url",["page","1"],"v:obj_id").execute(client)
2230-
{'@type': 'api:WoqlResponse', 'api:status': 'api:success', 'api:variable_names': ['obj_id'], 'bindings': [{'obj_id': 'http://base.url_page_1_rv1mfa59ekisdutnxx6zdt2fkockgah'}], 'deletes': 0, 'inserts': 0, 'transaction_retry_count': 0}
2231+
>>> WOQLQuery().idgen_random("Person/", "v:person_id").execute(client)
2232+
{'@type': 'api:WoqlResponse', 'api:status': 'api:success', 'api:variable_names': ['person_id'], 'bindings': [{'person_id': 'Person/aB3dEf9GhI2jK4lM'}], 'deletes': 0, 'inserts': 0, 'transaction_retry_count': 0}
22312233
"""
22322234
if prefix and prefix == "args":
2233-
return ["base", "key_list", "uri"]
2235+
return ["base", "uri"]
22342236
if self._cursor.get("@type"):
22352237
self._wrap_cursor_with_and()
22362238
self._cursor["@type"] = "RandomKey"
22372239
self._cursor["base"] = self._clean_data_value(prefix)
2238-
self._cursor["key_list"] = self._data_list(key_list)
22392240
self._cursor["uri"] = self._clean_node_value(uri)
22402241
return self
22412242

2243+
def random_idgen(self, prefix, uri):
2244+
"""Generates a unique ID with cryptographically secure random suffix (alias for idgen_random).
2245+
2246+
This is an alias for idgen_random() for compatibility with older code.
2247+
Uses base64 encoding to generate 16-character random IDs that are
2248+
guaranteed to be unique across executions.
2249+
2250+
Parameters
2251+
----------
2252+
prefix : str
2253+
A prefix for the IDs to be generated (e.g. "Person/")
2254+
uri : str
2255+
Variable name or output target for the generated ID
2256+
2257+
Returns
2258+
-------
2259+
WOQLQuery object
2260+
query object that can be chained and/or executed
2261+
2262+
Examples
2263+
-------
2264+
>>> WOQLQuery().random_idgen("Person/", "v:person_id").execute(client)
2265+
{'@type': 'api:WoqlResponse', 'api:status': 'api:success', 'api:variable_names': ['person_id'], 'bindings': [{'person_id': 'Person/aB3dEf9GhI2jK4lM'}], 'deletes': 0, 'inserts': 0, 'transaction_retry_count': 0}
2266+
"""
2267+
return self.idgen_random(prefix, uri)
2268+
22422269
def upper(self, left, right):
22432270
"""Changes a string to upper-case - input is in left, output in right
22442271

0 commit comments

Comments
 (0)