Null search datetimes (#818)

* support null operations

* update

* only provide variables for non-null params

* test update
This commit is contained in:
Preston Rasmussen 2025-08-12 12:24:37 -04:00 committed by GitHub
parent baa6825708
commit bfe51a0fcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 55 additions and 12 deletions

View file

@ -158,7 +158,7 @@ class Graphiti:
If not set, the Graphiti default is used. If not set, the Graphiti default is used.
ensure_ascii : bool, optional ensure_ascii : bool, optional
Whether to escape non-ASCII characters in JSON serialization for prompts. Defaults to False. Whether to escape non-ASCII characters in JSON serialization for prompts. Defaults to False.
Set to False to preserve non-ASCII characters (e.g., Korean, Japanese, Chinese) in their Set as False to preserve non-ASCII characters (e.g., Korean, Japanese, Chinese) in their
original form, making them readable in LLM logs and improving model understanding. original form, making them readable in LLM logs and improving model understanding.
Returns Returns

View file

@ -28,6 +28,8 @@ class ComparisonOperator(Enum):
less_than = '<' less_than = '<'
greater_than_equal = '>=' greater_than_equal = '>='
less_than_equal = '<=' less_than_equal = '<='
is_null = 'IS NULL'
is_not_null = 'IS NOT NULL'
class DateFilter(BaseModel): class DateFilter(BaseModel):
@ -64,6 +66,19 @@ def node_search_filter_query_constructor(
return filter_query, filter_params return filter_query, filter_params
def date_filter_query_constructor(
value_name: str, param_name: str, operator: ComparisonOperator
) -> str:
query = '(' + value_name + ' '
if operator == ComparisonOperator.is_null or operator == ComparisonOperator.is_not_null:
query += operator.value + ')'
else:
query += operator.value + ' ' + param_name + ')'
return query
def edge_search_filter_query_constructor( def edge_search_filter_query_constructor(
filters: SearchFilters, filters: SearchFilters,
) -> tuple[str, dict[str, Any]]: ) -> tuple[str, dict[str, Any]]:
@ -85,10 +100,16 @@ def edge_search_filter_query_constructor(
valid_at_filter = '\nAND (' valid_at_filter = '\nAND ('
for i, or_list in enumerate(filters.valid_at): for i, or_list in enumerate(filters.valid_at):
for j, date_filter in enumerate(or_list): for j, date_filter in enumerate(or_list):
filter_params['valid_at_' + str(j)] = date_filter.date if date_filter.comparison_operator not in [
ComparisonOperator.is_null,
ComparisonOperator.is_not_null,
]:
filter_params['valid_at_' + str(j)] = date_filter.date
and_filters = [ and_filters = [
'(e.valid_at ' + date_filter.comparison_operator.value + f' $valid_at_{j})' date_filter_query_constructor(
'e.valid_at', f'$valid_at_{j}', date_filter.comparison_operator
)
for j, date_filter in enumerate(or_list) for j, date_filter in enumerate(or_list)
] ]
and_filter_query = '' and_filter_query = ''
@ -110,10 +131,16 @@ def edge_search_filter_query_constructor(
invalid_at_filter = ' AND (' invalid_at_filter = ' AND ('
for i, or_list in enumerate(filters.invalid_at): for i, or_list in enumerate(filters.invalid_at):
for j, date_filter in enumerate(or_list): for j, date_filter in enumerate(or_list):
filter_params['invalid_at_' + str(j)] = date_filter.date if date_filter.comparison_operator not in [
ComparisonOperator.is_null,
ComparisonOperator.is_not_null,
]:
filter_params['invalid_at_' + str(j)] = date_filter.date
and_filters = [ and_filters = [
'(e.invalid_at ' + date_filter.comparison_operator.value + f' $invalid_at_{j})' date_filter_query_constructor(
'e.invalid_at', f'$invalid_at_{j}', date_filter.comparison_operator
)
for j, date_filter in enumerate(or_list) for j, date_filter in enumerate(or_list)
] ]
and_filter_query = '' and_filter_query = ''
@ -135,10 +162,16 @@ def edge_search_filter_query_constructor(
created_at_filter = ' AND (' created_at_filter = ' AND ('
for i, or_list in enumerate(filters.created_at): for i, or_list in enumerate(filters.created_at):
for j, date_filter in enumerate(or_list): for j, date_filter in enumerate(or_list):
filter_params['created_at_' + str(j)] = date_filter.date if date_filter.comparison_operator not in [
ComparisonOperator.is_null,
ComparisonOperator.is_not_null,
]:
filter_params['created_at_' + str(j)] = date_filter.date
and_filters = [ and_filters = [
'(e.created_at ' + date_filter.comparison_operator.value + f' $created_at_{j})' date_filter_query_constructor(
'e.created_at', f'$created_at_{j}', date_filter.comparison_operator
)
for j, date_filter in enumerate(or_list) for j, date_filter in enumerate(or_list)
] ]
and_filter_query = '' and_filter_query = ''
@ -160,10 +193,16 @@ def edge_search_filter_query_constructor(
expired_at_filter = ' AND (' expired_at_filter = ' AND ('
for i, or_list in enumerate(filters.expired_at): for i, or_list in enumerate(filters.expired_at):
for j, date_filter in enumerate(or_list): for j, date_filter in enumerate(or_list):
filter_params['expired_at_' + str(j)] = date_filter.date if date_filter.comparison_operator not in [
ComparisonOperator.is_null,
ComparisonOperator.is_not_null,
]:
filter_params['expired_at_' + str(j)] = date_filter.date
and_filters = [ and_filters = [
'(e.expired_at ' + date_filter.comparison_operator.value + f' $expired_at_{j})' date_filter_query_constructor(
'e.expired_at', f'$expired_at_{j}', date_filter.comparison_operator
)
for j, date_filter in enumerate(or_list) for j, date_filter in enumerate(or_list)
] ]
and_filter_query = '' and_filter_query = ''

View file

@ -1,7 +1,7 @@
[project] [project]
name = "graphiti-core" name = "graphiti-core"
description = "A temporal graph building library" description = "A temporal graph building library"
version = "0.18.5" version = "0.18.6"
authors = [ authors = [
{ name = "Paul Paliychuk", email = "paul@getzep.com" }, { name = "Paul Paliychuk", email = "paul@getzep.com" },
{ name = "Preston Rasmussen", email = "preston@getzep.com" }, { name = "Preston Rasmussen", email = "preston@getzep.com" },

View file

@ -65,7 +65,11 @@ async def test_graphiti_init(driver):
search_filter = SearchFilters( search_filter = SearchFilters(
node_labels=['Person', 'City'], node_labels=['Person', 'City'],
created_at=[[DateFilter(date=utc_now(), comparison_operator=ComparisonOperator.less_than)]], created_at=[
[DateFilter(date=None, comparison_operator=ComparisonOperator.is_null)],
[DateFilter(date=utc_now(), comparison_operator=ComparisonOperator.less_than)],
[DateFilter(date=None, comparison_operator=ComparisonOperator.is_not_null)],
],
) )
results = await graphiti.search_( results = await graphiti.search_(

2
uv.lock generated
View file

@ -746,7 +746,7 @@ wheels = [
[[package]] [[package]]
name = "graphiti-core" name = "graphiti-core"
version = "0.18.4" version = "0.18.6"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "diskcache" }, { name = "diskcache" },