Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions terminusdb_client/tests/integration_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,53 @@ def test_woql_substr(docker_url):
assert result["bindings"][0]["Substring"]["@value"] == "Soc"


def test_woql_triple_relation(docker_url):
client = Client(docker_url, user_agent=test_user_agent)
client.connect()
db_name = "relations" + str(random())
client.create_database(db_name)
client.connect(db=db_name)
schema = {
"@type": "Class",
"@id": "Event",
"interval": "xdd:dateTimeInterval",
}
client.insert_document(schema, graph_type="schema")
client.insert_document(
[
{
"@type": "Event",
"@id": "Event/ev-2025-Q1",
"interval": {
"@type": "xdd:dateTimeInterval",
"@value": "2025-01-01T00:00:00Z/2025-04-01T00:00:00Z",
},
},
{
"@type": "Event",
"@id": "Event/ev-2025-Q2",
"interval": {
"@type": "xdd:dateTimeInterval",
"@value": "2025-04-01T00:00:00Z/2025-07-01T00:00:00Z",
},
},
]
)
result = client.query(
WOQLQuery().triple_relation(
"v:doc",
"@schema:interval",
"v:iv",
"meets",
{
"@type": "xdd:dateTimeInterval",
"@value": "2025-04-01T00:00:00Z/2025-07-01T00:00:00Z",
},
)
)
assert [b["doc"] for b in result["bindings"]] == ["Event/ev-2025-Q1"]


def test_diff_apply_version(docker_url):
client = Client(docker_url, user_agent=test_user_agent)
client.connect()
Expand Down
57 changes: 57 additions & 0 deletions terminusdb_client/tests/test_woqlQuery.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,33 @@ def test_woql_not_method(self):
assert woql_object.to_dict() == json_obj
assert woql_object_chain.to_dict() == json_obj

def test_triple_relation_method(self):
woql_object = WOQLQuery().triple_relation(
"v:doc",
"scm:interval",
"v:iv",
"meets",
{"@type": "xdd:dateTimeInterval", "@value": "2025-01-01/2025-04-01"},
)
json_obj = {
"@type": "TripleRelation",
"subject": {"@type": "NodeValue", "variable": "doc"},
"predicate": {"@type": "NodeValue", "node": "scm:interval"},
"object": {"@type": "Value", "variable": "iv"},
"relation": {
"@type": "DataValue",
"data": {"@type": "xsd:string", "@value": "meets"},
},
"value": {
"@type": "DataValue",
"data": {
"@type": "xdd:dateTimeInterval",
"@value": "2025-01-01/2025-04-01",
},
},
}
assert woql_object.to_dict() == json_obj

def test_woql_and_method(self):
woql_object = WOQLQuery().woql_and(
WOQLQuery().triple("a", "b", "c"), WOQLQuery().triple("1", "2", "3")
Expand Down Expand Up @@ -237,6 +264,36 @@ def test_cast_method(self):
woql_object = WOQLQuery().cast("v:Duration", "xsd:integer", "v:Duration_Cast")
assert woql_object.to_dict() == WOQL_CAST_JSON

def test_cast_method_prefixes_dict(self):
woql_object = WOQLQuery().cast(
"v:QName_str",
"xsd:QName",
"v:QName_val",
prefixes={"eg": "http://example.org/ns#"},
)
json_obj = woql_object.to_dict()
assert json_obj["prefixes"] == {
"@type": "Value",
"data": {"eg": "http://example.org/ns#"},
}

def test_cast_method_prefixes_var(self):
woql_object = WOQLQuery().cast(
"v:QName_str",
"xsd:QName",
"v:QName_val",
prefixes="v:Namespaces",
)
json_obj = woql_object.to_dict()
assert json_obj["prefixes"] == {
"@type": "Value",
"variable": "Namespaces",
}

def test_cast_method_no_prefixes(self):
woql_object = WOQLQuery().cast("v:Duration", "xsd:integer", "v:Duration_Cast")
assert "prefixes" not in woql_object.to_dict()

def test_cast_method_literal(self):
woql_object1 = WOQLQuery().cast(
"1", "xsd:integer", "v:Duration_Cast", literal_type="integer"
Expand Down
59 changes: 56 additions & 3 deletions terminusdb_client/woqlquery/woql_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -2715,6 +2715,45 @@ def interval_relation_typed(self, relation, x, y):
self._cursor["y"] = self._clean_object(y)
return self

def triple_relation(self, sub, pred, obj, relation, value):
"""Finds triples whose object stands in a typed relation to a
reference value.

Supports the thirteen Allen interval relations on
xdd:dateTimeInterval objects; instant objects (xsd:dateTime)
compare as zero-duration points. An instant at a meeting boundary
belongs to the interval it starts.

Parameters
----------
sub : str
Subject, has to be a node (URI) or variable
pred : str
Predicate, can be variable (prefix with "v:") or node
obj : str or dict
The interval or instant object or a variable
relation : str or dict
relation name (e.g. "during", "meets")
value : str or dict
the reference interval or instant literal

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if relation is None or value is None:
raise ValueError("TripleRelation takes five parameters")
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "TripleRelation"
self._cursor["subject"] = self._clean_subject(sub)
self._cursor["predicate"] = self._clean_predicate(pred)
self._cursor["object"] = self._clean_object(obj)
self._cursor["relation"] = self._clean_data_value(relation)
self._cursor["value"] = self._clean_data_value(value)
return self

def range_min(self, input_list, result):
"""Find the minimum value in a list using the standard ordering.

Expand Down Expand Up @@ -3766,7 +3805,7 @@ def count(self, countvar, query=None):
self._cursor["count"] = self._clean_data_value(countvar)
return self._add_sub_query(query)

def cast(self, val, user_type, result, literal_type=None):
def cast(self, val, user_type, result, literal_type=None, prefixes=None):
"""Changes the type of va to type and saves the return in vb

Parameters
Expand All @@ -3780,6 +3819,11 @@ def cast(self, val, user_type, result, literal_type=None):
literal_type: str, optional
literal type of`val`, can be used to treat `val` as a literal rather than an object or variable in the WOQL query.
If literal type is "owl:Thing" or "node", `val` will be treated as object in the graph
prefixes : dict or str or Var, optional
prefix map used to expand or contract expanded names
(xsd:QName, xbrl:SQName) during the cast. A plain dict is sent
as a literal map; a "v:" string or Var is a variable bound
elsewhere (e.g. via dot on a stored namespaces dict).

Returns
-------
Expand All @@ -3790,9 +3834,13 @@ def cast(self, val, user_type, result, literal_type=None):
if (literal_type == "owl:Thing" or literal_type == "node") and isinstance(
val, str
):
return self.cast({"@type": "Value", "node": val}, user_type, result)
return self.cast(
{"@type": "Value", "node": val}, user_type, result, prefixes=prefixes
)
else:
return self.cast(self.literal(val, literal_type), user_type, result)
return self.cast(
self.literal(val, literal_type), user_type, result, prefixes=prefixes
)
if val and val == "args":
return ["value", "type", "result"]
if self._cursor.get("@type"):
Expand All @@ -3801,6 +3849,11 @@ def cast(self, val, user_type, result, literal_type=None):
self._cursor["value"] = self._clean_object(val)
self._cursor["type"] = self._clean_node_value(user_type)
self._cursor["result"] = self._clean_object(result)
if prefixes is not None:
if type(prefixes) is dict and "@type" not in prefixes:
self._cursor["prefixes"] = {"@type": "Value", "data": prefixes}
else:
self._cursor["prefixes"] = self._clean_object(prefixes)
return self

def type_of(self, value, vtype):
Expand Down
Loading