Skip to content

Commit aacb20a

Browse files
arne-aignxclaude
andauthored
perf(application): replace like_regex with == for tag filtering (#516)
* perf(application): replace like_regex with == for tag filtering in application_runs Use JSONPath equality checks instead of like_regex for exact tag matching. PostgreSQL GIN indexes (jsonb_path_ops) can accelerate == but not like_regex, reducing database load for tag-filtered queries. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: update .python-version to 3.14.2 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 24737e0 commit aacb20a

File tree

3 files changed

+75
-9
lines changed

3 files changed

+75
-9
lines changed

.python-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.14.1
1+
3.14.2

src/aignostics/application/_service.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -711,14 +711,18 @@ def application_runs( # noqa: C901, PLR0912, PLR0913, PLR0914, PLR0915, PLR0917
711711

712712
# Handle tags filter
713713
if tags:
714-
# JSONPath filter to match all of the provided tags in the sdk.tags array
715-
# PostgreSQL limitation: Cannot use && between separate path expressions as backend crashes with 500
716-
# Workaround: Filter on backend for ANY tag match, then filter client-side for ALL
717-
# Use regex alternation to match any of the tags
718-
escaped_tags = [tag.replace('"', '\\"').replace("\\", "\\\\") for tag in tags]
719-
# Create regex pattern: ^(tag1|tag2|tag3)$
720-
regex_pattern = "^(" + "|".join(escaped_tags) + ")$"
721-
custom_metadata = f'$.sdk.tags ? (@ like_regex "{regex_pattern}")'
714+
# Use equality checks instead of like_regex for exact tag matching.
715+
# PostgreSQL GIN indexes (jsonb_path_ops) can accelerate == but not like_regex.
716+
# PostgreSQL JSONPath does not support combining separate path expressions
717+
# (e.g. $.sdk.tags ? (...) && $.sdk.note ? (...) is invalid JSONPath syntax),
718+
# so we can only filter on one path per API call.
719+
# Strategy: filter on backend for ANY tag match, then filter client-side for ALL.
720+
escaped_tags = [tag.replace("\\", "\\\\").replace('"', '\\"') for tag in tags]
721+
if len(escaped_tags) == 1:
722+
custom_metadata = f'$.sdk.tags[*] ? (@ == "{escaped_tags[0]}")'
723+
else:
724+
conditions = " || ".join(f'@ == "{t}"' for t in escaped_tags)
725+
custom_metadata = f"$.sdk.tags[*] ? ({conditions})"
722726

723727
run_iterator = self._get_platform_client().runs.list_data(
724728
application_id=application_id,

tests/aignostics/application/service_test.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,68 @@ def test_application_runs_query_escapes_special_characters(mock_get_client: Magi
431431
assert 'test\\"value\\\\path' in tag_call_kwargs["custom_metadata"]
432432

433433

434+
@pytest.mark.unit
435+
@patch("aignostics.application._service.Service._get_platform_client")
436+
def test_application_runs_tags_single_generates_equality_jsonpath(mock_get_client: MagicMock) -> None:
437+
"""Test that a single tag generates a JSONPath equality expression instead of like_regex."""
438+
mock_client = MagicMock()
439+
mock_runs = MagicMock()
440+
mock_runs.list_data.return_value = iter([])
441+
mock_client.runs = mock_runs
442+
mock_get_client.return_value = mock_client
443+
444+
service = ApplicationService()
445+
service.application_runs(tags={"experiment-1"})
446+
447+
call_kwargs = mock_runs.list_data.call_args[1]
448+
assert call_kwargs["custom_metadata"] == '$.sdk.tags[*] ? (@ == "experiment-1")'
449+
450+
451+
@pytest.mark.unit
452+
@patch("aignostics.application._service.Service._get_platform_client")
453+
def test_application_runs_tags_multiple_generates_or_equality_jsonpath(mock_get_client: MagicMock) -> None:
454+
"""Test that multiple tags generate a JSONPath OR equality expression."""
455+
mock_client = MagicMock()
456+
mock_runs = MagicMock()
457+
mock_runs.list_data.return_value = iter([])
458+
mock_client.runs = mock_runs
459+
mock_get_client.return_value = mock_client
460+
461+
service = ApplicationService()
462+
service.application_runs(tags={"alpha", "beta"})
463+
464+
call_kwargs = mock_runs.list_data.call_args[1]
465+
custom_metadata = call_kwargs["custom_metadata"]
466+
467+
# Tags are from a set so order is not guaranteed
468+
assert custom_metadata.startswith("$.sdk.tags[*] ? (")
469+
assert custom_metadata.endswith(")")
470+
assert '@ == "alpha"' in custom_metadata
471+
assert '@ == "beta"' in custom_metadata
472+
assert " || " in custom_metadata
473+
474+
475+
@pytest.mark.unit
476+
@patch("aignostics.application._service.Service._get_platform_client")
477+
def test_application_runs_tags_escapes_quotes_and_backslashes(mock_get_client: MagicMock) -> None:
478+
"""Test that tag values with quotes and backslashes are properly escaped in JSONPath."""
479+
mock_client = MagicMock()
480+
mock_runs = MagicMock()
481+
mock_runs.list_data.return_value = iter([])
482+
mock_client.runs = mock_runs
483+
mock_get_client.return_value = mock_client
484+
485+
service = ApplicationService()
486+
service.application_runs(tags={'tag"with"quotes', "path\\to\\dir"})
487+
488+
call_kwargs = mock_runs.list_data.call_args[1]
489+
custom_metadata = call_kwargs["custom_metadata"]
490+
491+
# Backslashes escaped first, then quotes
492+
assert '@ == "tag\\"with\\"quotes"' in custom_metadata
493+
assert '@ == "path\\\\to\\\\dir"' in custom_metadata
494+
495+
434496
@pytest.mark.unit
435497
@patch("aignostics.application._service.Service._get_platform_client")
436498
def test_application_run_update_custom_metadata_success(mock_get_client: MagicMock) -> None:

0 commit comments

Comments
 (0)