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
35 changes: 35 additions & 0 deletions terminusdb_client/scripts/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,41 @@ def importcsv(
# "not schema" make it always False if adding the schema option
has_schema = not schema and class_name in client.get_existing_classes()

def _df_to_schema(class_name, df):
class_dict = {"@type": "Class", "@id": class_name}
np_to_buildin = {
v: getattr(builtins, k)
for k, v in np.sctypeDict.items()
if k in vars(builtins)
}
np_to_buildin[np.datetime64] = dt.datetime
np_to_buildin[str] = str
for col, dtype in dict(df.dtypes).items():
if embedded and col in embedded:
converted_type = class_name
else:
converted_type = np_to_buildin[dtype.type]
if converted_type is object:
converted_type = str # pandas treats all string as objects
converted_type = wt.to_woql_type(converted_type)

if id_ and col == id_:
class_dict[col] = converted_type
elif na == "optional" and col not in keys:
class_dict[col] = {"@type": "Optional", "@class": converted_type}
else:
class_dict[col] = converted_type
# if id_ is not None:
# pass # don't need key if id is specified
# elif keys:
# class_dict["@key"] = {"@type": "Random"}
# elif na == "optional":
# # have to use random key cause keys will be optional
# class_dict["@key"] = {"@type": "Random"}
# else:
# class_dict["@key"] = {"@type": "Random"}
return class_dict

with pd.read_csv(csv_file, sep=sep, chunksize=chunksize, dtype=dtype) as reader:
for df in tqdm(reader):
if any(df.isna().any()) and na == "error":
Expand Down
244 changes: 239 additions & 5 deletions terminusdb_client/woqlquery/woql_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,240 @@ def triple(self, sub, pred, obj, opt=False):
self._cursor["object"] = self._clean_object(obj)
return self

def triple_slice(self, sub, pred, obj, low, high):
"""Creates a triple pattern matching rule for [S, P, O] with a half-open
value range [low, high) on the object. Returns triples whose typed object
value falls within the specified range.

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
Object, can be variable or node or value
low : object
The inclusive lower bound as a typed value
high : object
The exclusive upper bound as a typed value

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "TripleSlice"
self._cursor["subject"] = self._clean_subject(sub)
self._cursor["predicate"] = self._clean_predicate(pred)
self._cursor["object"] = self._clean_object(obj)
self._cursor["low"] = self._clean_object(low)
self._cursor["high"] = self._clean_object(high)
return self

def quad_slice(self, sub, pred, obj, low, high, graph):
"""Creates a triple pattern matching rule for [S, P, O, G] with a half-open
value range [low, high) on the object and an explicit graph selector.

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
Object, can be variable or node or value
low : object
The inclusive lower bound as a typed value
high : object
The exclusive upper bound as a typed value
graph : str
Graph resource identifier (e.g. 'instance' or 'schema')

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
self.triple_slice(sub, pred, obj, low, high)
self._cursor["graph"] = self._clean_graph(graph)
return self

def triple_slice_rev(self, sub, pred, obj, low, high):
"""Creates a triple pattern matching rule for [S, P, O] with a half-open
value range [low, high) on the object, returning results in reverse
(descending) object order. Same semantics as triple_slice but iterates
from highest to lowest value.

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
Object, can be variable or node or value
low : object
The inclusive lower bound as a typed value
high : object
The exclusive upper bound as a typed value

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "TripleSliceRev"
self._cursor["subject"] = self._clean_subject(sub)
self._cursor["predicate"] = self._clean_predicate(pred)
self._cursor["object"] = self._clean_object(obj)
self._cursor["low"] = self._clean_object(low)
self._cursor["high"] = self._clean_object(high)
return self

def quad_slice_rev(self, sub, pred, obj, low, high, graph):
"""Creates a triple pattern matching rule for [S, P, O, G] with a half-open
value range [low, high) on the object in reverse order, with an explicit
graph selector.

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
Object, can be variable or node or value
low : object
The inclusive lower bound as a typed value
high : object
The exclusive upper bound as a typed value
graph : str
Graph resource identifier (e.g. 'instance' or 'schema')

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
self.triple_slice_rev(sub, pred, obj, low, high)
self._cursor["graph"] = self._clean_graph(graph)
return self

def triple_next(self, sub, pred, obj, next_val):
"""Finds the next object value after a reference for a given subject-predicate pair.
When object is bound and next is free, finds the smallest next > object.
When next is bound and object is free, finds the largest object < next.

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
Object value or variable
next_val : object
Next object value or variable

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "TripleNext"
self._cursor["subject"] = self._clean_subject(sub)
self._cursor["predicate"] = self._clean_predicate(pred)
self._cursor["object"] = self._clean_object(obj)
self._cursor["next"] = self._clean_object(next_val)
return self

def quad_next(self, sub, pred, obj, next_val, graph):
"""Finds the next object value with an explicit graph selector.

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
Object value or variable
next_val : object
Next object value or variable
graph : str
Graph resource identifier (e.g. 'instance' or 'schema')

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
self.triple_next(sub, pred, obj, next_val)
self._cursor["graph"] = self._clean_graph(graph)
return self

def triple_previous(self, sub, pred, obj, prev_val):
"""Finds the previous object value before a reference for a given subject-predicate pair.
When object is bound and previous is free, finds the largest previous < object.
When previous is bound and object is free, finds the smallest object > previous.

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
Object value or variable
prev_val : object
Previous object value or variable

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
if self._cursor.get("@type"):
self._wrap_cursor_with_and()
self._cursor["@type"] = "TriplePrevious"
self._cursor["subject"] = self._clean_subject(sub)
self._cursor["predicate"] = self._clean_predicate(pred)
self._cursor["object"] = self._clean_object(obj)
self._cursor["previous"] = self._clean_object(prev_val)
return self

def quad_previous(self, sub, pred, obj, prev_val, graph):
"""Finds the previous object value with an explicit graph selector.

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
Object value or variable
prev_val : object
Previous object value or variable
graph : str
Graph resource identifier (e.g. 'instance' or 'schema')

Returns
-------
WOQLQuery object
query object that can be chained and/or execute
"""
self.triple_previous(sub, pred, obj, prev_val)
self._cursor["graph"] = self._clean_graph(graph)
return self

def added_triple(self, sub, pred, obj, opt=False):
"""Creates a triple pattern matching rule for the triple [S, P, O] (Subject, Predicate, Object) added to the current commit.

Expand Down Expand Up @@ -3434,7 +3668,7 @@ def localize(self, param_spec):
"""Build a localized scope for variables to prevent leaking local variables to outer scope.

Returns a tuple (localized_fn, v) where:
- localized_fn: function that wraps queries with select("") and eq() bindings
- localized_fn: function that wraps queries with select() (empty variable list) and eq() bindings
- v: VarsUnique object with unique variable names for use in the inner query

Parameters with non-None values are bound from outer scope via eq().
Expand Down Expand Up @@ -3464,7 +3698,7 @@ def localize(self, param_spec):
v = VarsUnique(*param_names)

def localized_fn(query=None):
# Create eq bindings for outer parameters OUTSIDE select("")
# Create eq bindings for outer parameters OUTSIDE select()
# This ensures outer parameters are visible in query results
outer_eq_bindings = []
for param_name in param_names:
Expand All @@ -3478,17 +3712,17 @@ def localized_fn(query=None):
outer_eq_bindings.append(
WOQLQuery().eq(outer_value, outer_value)
)
# Bind the unique variable to the outer parameter OUTSIDE the select("")
# Bind the unique variable to the outer parameter OUTSIDE the select()
outer_eq_bindings.append(
WOQLQuery().eq(getattr(v, param_name), outer_value)
)

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

if outer_eq_bindings:
# Wrap: eq(outer) AND select("") { query }
# Wrap: eq(outer) AND select() { query }
return WOQLQuery().woql_and(*outer_eq_bindings, localized_query)
return localized_query

Expand Down
Loading