Skip to content

Commit e57a809

Browse files
authored
Merge pull request #462 from hoijnet/range-queries
Enable range queries
2 parents 62abdb8 + bf82c43 commit e57a809

File tree

2 files changed

+250
-5
lines changed

2 files changed

+250
-5
lines changed

terminusdb_client/scripts/scripts.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ def importcsv(
486486
embedded = [x.lower().replace(" ", "_") for x in embedded]
487487
try:
488488
pd = import_module("pandas")
489+
np = import_module("numpy")
489490
except ImportError:
490491
raise ImportError(
491492
"Library 'pandas' is required to import csv, either install 'pandas' or install woqlDataframe requirements as follows: python -m pip install -U terminus-client-python[dataframe]"
@@ -501,10 +502,20 @@ def importcsv(
501502

502503
def _df_to_schema(class_name, df):
503504
class_dict = {"@type": "Class", "@id": class_name}
505+
np_to_buildin = {
506+
v: getattr(builtins, k)
507+
for k, v in np.sctypeDict.items()
508+
if k in vars(builtins)
509+
}
510+
np_to_buildin[np.datetime64] = dt.datetime
511+
np_to_buildin[str] = str
504512
for col, dtype in dict(df.dtypes).items():
505513
if embedded and col in embedded:
506514
converted_type = class_name
507515
else:
516+
converted_type = np_to_buildin[dtype.type]
517+
if converted_type is object:
518+
converted_type = str # pandas treats all string as objects
508519
# Map pandas/numpy dtype to Python type
509520
# Uses dtype.kind for compatibility with numpy 2.0+ and pandas 3.0+
510521
dtype_kind = getattr(dtype, "kind", "O")

terminusdb_client/woqlquery/woql_query.py

Lines changed: 239 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,240 @@ def triple(self, sub, pred, obj, opt=False):
10091009
self._cursor["object"] = self._clean_object(obj)
10101010
return self
10111011

1012+
def triple_slice(self, sub, pred, obj, low, high):
1013+
"""Creates a triple pattern matching rule for [S, P, O] with a half-open
1014+
value range [low, high) on the object. Returns triples whose typed object
1015+
value falls within the specified range.
1016+
1017+
Parameters
1018+
----------
1019+
sub : str
1020+
Subject, has to be a node (URI) or variable
1021+
pred : str
1022+
Predicate, can be variable (prefix with "v:") or node
1023+
obj : str
1024+
Object, can be variable or node or value
1025+
low : object
1026+
The inclusive lower bound as a typed value
1027+
high : object
1028+
The exclusive upper bound as a typed value
1029+
1030+
Returns
1031+
-------
1032+
WOQLQuery object
1033+
query object that can be chained and/or execute
1034+
"""
1035+
if self._cursor.get("@type"):
1036+
self._wrap_cursor_with_and()
1037+
self._cursor["@type"] = "TripleSlice"
1038+
self._cursor["subject"] = self._clean_subject(sub)
1039+
self._cursor["predicate"] = self._clean_predicate(pred)
1040+
self._cursor["object"] = self._clean_object(obj)
1041+
self._cursor["low"] = self._clean_object(low)
1042+
self._cursor["high"] = self._clean_object(high)
1043+
return self
1044+
1045+
def quad_slice(self, sub, pred, obj, low, high, graph):
1046+
"""Creates a triple pattern matching rule for [S, P, O, G] with a half-open
1047+
value range [low, high) on the object and an explicit graph selector.
1048+
1049+
Parameters
1050+
----------
1051+
sub : str
1052+
Subject, has to be a node (URI) or variable
1053+
pred : str
1054+
Predicate, can be variable (prefix with "v:") or node
1055+
obj : str
1056+
Object, can be variable or node or value
1057+
low : object
1058+
The inclusive lower bound as a typed value
1059+
high : object
1060+
The exclusive upper bound as a typed value
1061+
graph : str
1062+
Graph resource identifier (e.g. 'instance' or 'schema')
1063+
1064+
Returns
1065+
-------
1066+
WOQLQuery object
1067+
query object that can be chained and/or execute
1068+
"""
1069+
self.triple_slice(sub, pred, obj, low, high)
1070+
self._cursor["graph"] = self._clean_graph(graph)
1071+
return self
1072+
1073+
def triple_slice_rev(self, sub, pred, obj, low, high):
1074+
"""Creates a triple pattern matching rule for [S, P, O] with a half-open
1075+
value range [low, high) on the object, returning results in reverse
1076+
(descending) object order. Same semantics as triple_slice but iterates
1077+
from highest to lowest value.
1078+
1079+
Parameters
1080+
----------
1081+
sub : str
1082+
Subject, has to be a node (URI) or variable
1083+
pred : str
1084+
Predicate, can be variable (prefix with "v:") or node
1085+
obj : str
1086+
Object, can be variable or node or value
1087+
low : object
1088+
The inclusive lower bound as a typed value
1089+
high : object
1090+
The exclusive upper bound as a typed value
1091+
1092+
Returns
1093+
-------
1094+
WOQLQuery object
1095+
query object that can be chained and/or execute
1096+
"""
1097+
if self._cursor.get("@type"):
1098+
self._wrap_cursor_with_and()
1099+
self._cursor["@type"] = "TripleSliceRev"
1100+
self._cursor["subject"] = self._clean_subject(sub)
1101+
self._cursor["predicate"] = self._clean_predicate(pred)
1102+
self._cursor["object"] = self._clean_object(obj)
1103+
self._cursor["low"] = self._clean_object(low)
1104+
self._cursor["high"] = self._clean_object(high)
1105+
return self
1106+
1107+
def quad_slice_rev(self, sub, pred, obj, low, high, graph):
1108+
"""Creates a triple pattern matching rule for [S, P, O, G] with a half-open
1109+
value range [low, high) on the object in reverse order, with an explicit
1110+
graph selector.
1111+
1112+
Parameters
1113+
----------
1114+
sub : str
1115+
Subject, has to be a node (URI) or variable
1116+
pred : str
1117+
Predicate, can be variable (prefix with "v:") or node
1118+
obj : str
1119+
Object, can be variable or node or value
1120+
low : object
1121+
The inclusive lower bound as a typed value
1122+
high : object
1123+
The exclusive upper bound as a typed value
1124+
graph : str
1125+
Graph resource identifier (e.g. 'instance' or 'schema')
1126+
1127+
Returns
1128+
-------
1129+
WOQLQuery object
1130+
query object that can be chained and/or execute
1131+
"""
1132+
self.triple_slice_rev(sub, pred, obj, low, high)
1133+
self._cursor["graph"] = self._clean_graph(graph)
1134+
return self
1135+
1136+
def triple_next(self, sub, pred, obj, next_val):
1137+
"""Finds the next object value after a reference for a given subject-predicate pair.
1138+
When object is bound and next is free, finds the smallest next > object.
1139+
When next is bound and object is free, finds the largest object < next.
1140+
1141+
Parameters
1142+
----------
1143+
sub : str
1144+
Subject, has to be a node (URI) or variable
1145+
pred : str
1146+
Predicate, can be variable (prefix with "v:") or node
1147+
obj : str
1148+
Object value or variable
1149+
next_val : object
1150+
Next object value or variable
1151+
1152+
Returns
1153+
-------
1154+
WOQLQuery object
1155+
query object that can be chained and/or execute
1156+
"""
1157+
if self._cursor.get("@type"):
1158+
self._wrap_cursor_with_and()
1159+
self._cursor["@type"] = "TripleNext"
1160+
self._cursor["subject"] = self._clean_subject(sub)
1161+
self._cursor["predicate"] = self._clean_predicate(pred)
1162+
self._cursor["object"] = self._clean_object(obj)
1163+
self._cursor["next"] = self._clean_object(next_val)
1164+
return self
1165+
1166+
def quad_next(self, sub, pred, obj, next_val, graph):
1167+
"""Finds the next object value with an explicit graph selector.
1168+
1169+
Parameters
1170+
----------
1171+
sub : str
1172+
Subject, has to be a node (URI) or variable
1173+
pred : str
1174+
Predicate, can be variable (prefix with "v:") or node
1175+
obj : str
1176+
Object value or variable
1177+
next_val : object
1178+
Next object value or variable
1179+
graph : str
1180+
Graph resource identifier (e.g. 'instance' or 'schema')
1181+
1182+
Returns
1183+
-------
1184+
WOQLQuery object
1185+
query object that can be chained and/or execute
1186+
"""
1187+
self.triple_next(sub, pred, obj, next_val)
1188+
self._cursor["graph"] = self._clean_graph(graph)
1189+
return self
1190+
1191+
def triple_previous(self, sub, pred, obj, prev_val):
1192+
"""Finds the previous object value before a reference for a given subject-predicate pair.
1193+
When object is bound and previous is free, finds the largest previous < object.
1194+
When previous is bound and object is free, finds the smallest object > previous.
1195+
1196+
Parameters
1197+
----------
1198+
sub : str
1199+
Subject, has to be a node (URI) or variable
1200+
pred : str
1201+
Predicate, can be variable (prefix with "v:") or node
1202+
obj : str
1203+
Object value or variable
1204+
prev_val : object
1205+
Previous object value or variable
1206+
1207+
Returns
1208+
-------
1209+
WOQLQuery object
1210+
query object that can be chained and/or execute
1211+
"""
1212+
if self._cursor.get("@type"):
1213+
self._wrap_cursor_with_and()
1214+
self._cursor["@type"] = "TriplePrevious"
1215+
self._cursor["subject"] = self._clean_subject(sub)
1216+
self._cursor["predicate"] = self._clean_predicate(pred)
1217+
self._cursor["object"] = self._clean_object(obj)
1218+
self._cursor["previous"] = self._clean_object(prev_val)
1219+
return self
1220+
1221+
def quad_previous(self, sub, pred, obj, prev_val, graph):
1222+
"""Finds the previous object value with an explicit graph selector.
1223+
1224+
Parameters
1225+
----------
1226+
sub : str
1227+
Subject, has to be a node (URI) or variable
1228+
pred : str
1229+
Predicate, can be variable (prefix with "v:") or node
1230+
obj : str
1231+
Object value or variable
1232+
prev_val : object
1233+
Previous object value or variable
1234+
graph : str
1235+
Graph resource identifier (e.g. 'instance' or 'schema')
1236+
1237+
Returns
1238+
-------
1239+
WOQLQuery object
1240+
query object that can be chained and/or execute
1241+
"""
1242+
self.triple_previous(sub, pred, obj, prev_val)
1243+
self._cursor["graph"] = self._clean_graph(graph)
1244+
return self
1245+
10121246
def added_triple(self, sub, pred, obj, opt=False):
10131247
"""Creates a triple pattern matching rule for the triple [S, P, O] (Subject, Predicate, Object) added to the current commit.
10141248
@@ -3462,7 +3696,7 @@ def localize(self, param_spec):
34623696
"""Build a localized scope for variables to prevent leaking local variables to outer scope.
34633697
34643698
Returns a tuple (localized_fn, v) where:
3465-
- localized_fn: function that wraps queries with select("") and eq() bindings
3699+
- localized_fn: function that wraps queries with select() (empty variable list) and eq() bindings
34663700
- v: VarsUnique object with unique variable names for use in the inner query
34673701
34683702
Parameters with non-None values are bound from outer scope via eq().
@@ -3492,7 +3726,7 @@ def localize(self, param_spec):
34923726
v = VarsUnique(*param_names)
34933727

34943728
def localized_fn(query=None):
3495-
# Create eq bindings for outer parameters OUTSIDE select("")
3729+
# Create eq bindings for outer parameters OUTSIDE select()
34963730
# This ensures outer parameters are visible in query results
34973731
outer_eq_bindings = []
34983732
for param_name in param_names:
@@ -3506,17 +3740,17 @@ def localized_fn(query=None):
35063740
outer_eq_bindings.append(
35073741
WOQLQuery().eq(outer_value, outer_value)
35083742
)
3509-
# Bind the unique variable to the outer parameter OUTSIDE the select("")
3743+
# Bind the unique variable to the outer parameter OUTSIDE the select()
35103744
outer_eq_bindings.append(
35113745
WOQLQuery().eq(getattr(v, param_name), outer_value)
35123746
)
35133747

35143748
if query is not None:
3515-
# Functional mode: wrap query in select(""), then add outer eq bindings
3749+
# Functional mode: wrap query in select() with empty variable list
35163750
localized_query = WOQLQuery().select(query)
35173751

35183752
if outer_eq_bindings:
3519-
# Wrap: eq(outer) AND select("") { query }
3753+
# Wrap: eq(outer) AND select() { query }
35203754
return WOQLQuery().woql_and(*outer_eq_bindings, localized_query)
35213755
return localized_query
35223756

0 commit comments

Comments
 (0)