Skip to content

Commit 86fd6a2

Browse files
[NA] Add missing sort filter fields (#2750)
1 parent 392e9e2 commit 86fd6a2

File tree

10 files changed

+220
-24
lines changed

10 files changed

+220
-24
lines changed

apps/opik-backend/src/main/java/com/comet/opik/api/filter/DatasetField.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public enum DatasetField implements Field {
1111
DESCRIPTION(DESCRIPTION_QUERY_PARAM, FieldType.STRING_STATE_DB),
1212
TAGS(TAGS_QUERY_PARAM, FieldType.STRING_STATE_DB),
1313
CREATED_AT(CREATED_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB),
14+
CREATED_BY(CREATED_BY_QUERY_PARAM, FieldType.STRING_STATE_DB),
1415
LAST_UPDATED_AT(LAST_UPDATED_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB),
16+
LAST_UPDATED_BY(LAST_UPDATED_BY_QUERY_PARAM, FieldType.STRING_STATE_DB),
1517
LAST_CREATED_EXPERIMENT_AT(LAST_CREATED_EXPERIMENT_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB),
1618
LAST_CREATED_OPTIMIZATION_AT(LAST_CREATED_OPTIMIZATION_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB);
1719

apps/opik-backend/src/main/java/com/comet/opik/api/filter/Field.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public interface Field {
2828
String LAST_MESSAGE_QUERY_PARAM = "last_message";
2929
String CREATED_AT_QUERY_PARAM = "created_at";
3030
String LAST_UPDATED_AT_QUERY_PARAM = "last_updated_at";
31+
String CREATED_BY_QUERY_PARAM = "created_by";
32+
String LAST_UPDATED_BY_QUERY_PARAM = "last_updated_by";
3133
String LAST_CREATED_EXPERIMENT_AT_QUERY_PARAM = "last_created_experiment_at";
3234
String LAST_CREATED_OPTIMIZATION_AT_QUERY_PARAM = "last_created_optimization_at";
3335
String GUARDRAILS_QUERY_PARAM = "guardrails";
@@ -36,6 +38,7 @@ public interface Field {
3638
String STATUS_QUERY_PARAM = "status";
3739
String TYPE_QUERY_PARAM = "type";
3840
String LLM_SPAN_COUNT_QUERY_PARAM = "llm_span_count";
41+
String VERSION_COUNT_QUERY_PARAM = "version_count";
3942

4043
@JsonValue
4144
String getQueryParamField();

apps/opik-backend/src/main/java/com/comet/opik/api/filter/PromptField.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ public enum PromptField implements Field {
1111
DESCRIPTION(DESCRIPTION_QUERY_PARAM, FieldType.STRING_STATE_DB),
1212
CREATED_AT(CREATED_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB),
1313
LAST_UPDATED_AT(LAST_UPDATED_AT_QUERY_PARAM, FieldType.DATE_TIME_STATE_DB),
14+
CREATED_BY(CREATED_BY_QUERY_PARAM, FieldType.STRING_STATE_DB),
15+
LAST_UPDATED_BY(LAST_UPDATED_BY_QUERY_PARAM, FieldType.STRING_STATE_DB),
1416
TAGS(TAGS_QUERY_PARAM, FieldType.STRING_STATE_DB),
17+
VERSION_COUNT(VERSION_COUNT_QUERY_PARAM, FieldType.NUMBER),
1518
;
1619

1720
private final String queryParamField;

apps/opik-backend/src/main/java/com/comet/opik/api/sorting/SortableFields.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ public class SortableFields {
3939
public static final String FEEDBACK_SCORES = "feedback_scores.*";
4040
public static final String NUMBER_OF_MESSAGES = "number_of_messages";
4141
public static final String STATUS = "status";
42+
public static final String VERSION_COUNT = "version_count";
4243
}

apps/opik-backend/src/main/java/com/comet/opik/api/sorting/SortingFactoryDatasets.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
import java.util.List;
44

55
import static com.comet.opik.api.sorting.SortableFields.CREATED_AT;
6+
import static com.comet.opik.api.sorting.SortableFields.CREATED_BY;
67
import static com.comet.opik.api.sorting.SortableFields.DESCRIPTION;
78
import static com.comet.opik.api.sorting.SortableFields.ID;
89
import static com.comet.opik.api.sorting.SortableFields.LAST_CREATED_EXPERIMENT_AT;
910
import static com.comet.opik.api.sorting.SortableFields.LAST_CREATED_OPTIMIZATION_AT;
1011
import static com.comet.opik.api.sorting.SortableFields.LAST_UPDATED_AT;
12+
import static com.comet.opik.api.sorting.SortableFields.LAST_UPDATED_BY;
1113
import static com.comet.opik.api.sorting.SortableFields.NAME;
1214
import static com.comet.opik.api.sorting.SortableFields.TAGS;
1315

1416
public class SortingFactoryDatasets extends SortingFactory {
1517

1618
private static final List<String> SUPPORTED_FIELDS = List.of(ID, NAME, DESCRIPTION, TAGS, CREATED_AT,
17-
LAST_UPDATED_AT, LAST_CREATED_EXPERIMENT_AT, LAST_CREATED_OPTIMIZATION_AT);
19+
LAST_UPDATED_AT, CREATED_BY, LAST_UPDATED_BY, LAST_CREATED_EXPERIMENT_AT, LAST_CREATED_OPTIMIZATION_AT);
1820

1921
@Override
2022
public List<String> getSortableFields() {

apps/opik-backend/src/main/java/com/comet/opik/api/sorting/SortingFactoryPrompts.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static com.comet.opik.api.sorting.SortableFields.LAST_UPDATED_BY;
1111
import static com.comet.opik.api.sorting.SortableFields.NAME;
1212
import static com.comet.opik.api.sorting.SortableFields.TAGS;
13+
import static com.comet.opik.api.sorting.SortableFields.VERSION_COUNT;
1314

1415
public class SortingFactoryPrompts extends SortingFactory {
1516
@Override
@@ -22,6 +23,7 @@ public List<String> getSortableFields() {
2223
LAST_UPDATED_AT,
2324
CREATED_BY,
2425
LAST_UPDATED_BY,
26+
VERSION_COUNT,
2527
TAGS);
2628
}
2729
}

apps/opik-backend/src/main/java/com/comet/opik/domain/PromptDAO.java

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,21 @@ SELECT JSON_OBJECT(
6969
Prompt findById(@Bind("id") UUID id, @Bind("workspace_id") String workspaceId);
7070

7171
@SqlQuery("""
72-
WITH prompt_filtered AS (
73-
SELECT *
74-
FROM prompts
75-
WHERE workspace_id = :workspace_id
76-
<if(filters)> AND <filters> <endif>
77-
<if(name)> AND name like concat('%', :name, '%') <endif>
78-
)
79-
SELECT
72+
WITH prompt_full AS (
73+
SELECT
8074
p.*,
8175
count(pv.id) as version_count
82-
FROM prompt_filtered p
83-
LEFT JOIN prompt_versions pv ON pv.prompt_id = p.id
84-
GROUP BY p.id
85-
ORDER BY <if(sort_fields)> <sort_fields>, <endif> p.id DESC
76+
FROM prompts p
77+
LEFT JOIN prompt_versions pv ON pv.prompt_id = p.id
78+
WHERE p.workspace_id = :workspace_id
79+
GROUP BY p.id
80+
)
81+
SELECT *
82+
FROM prompt_full
83+
WHERE 1 = 1
84+
<if(filters)> AND <filters> <endif>
85+
<if(name)> AND name like concat('%', :name, '%') <endif>
86+
ORDER BY <if(sort_fields)> <sort_fields>, <endif> id DESC
8687
LIMIT :limit OFFSET :offset
8788
""")
8889
@UseStringTemplateEngine
@@ -93,10 +94,22 @@ List<Prompt> find(@Define("name") @Bind("name") String name, @Bind("workspace_id
9394
@Define("filters") String filters,
9495
@BindMap Map<String, Object> filterMapping);
9596

96-
@SqlQuery("SELECT COUNT(id) FROM prompts " +
97-
" WHERE workspace_id = :workspace_id " +
98-
"<if(filters)> AND <filters> <endif>" +
99-
" <if(name)> AND name like concat('%', :name, '%') <endif> ")
97+
@SqlQuery("""
98+
WITH prompt_full AS (
99+
SELECT
100+
p.*,
101+
count(pv.id) as version_count
102+
FROM prompts p
103+
LEFT JOIN prompt_versions pv ON pv.prompt_id = p.id
104+
WHERE p.workspace_id = :workspace_id
105+
GROUP BY p.id
106+
)
107+
SELECT COUNT(id)
108+
FROM prompt_full
109+
WHERE 1 = 1
110+
<if(filters)> AND <filters> <endif>
111+
<if(name)> AND name like concat('%', :name, '%') <endif>
112+
""")
100113
@UseStringTemplateEngine
101114
@AllowUnusedBindings
102115
long count(@Define("name") @Bind("name") String name, @Bind("workspace_id") String workspaceId,

apps/opik-backend/src/main/java/com/comet/opik/domain/filter/FilterQueryBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class FilterQueryBuilder {
5252
private static final String LLM_SPAN_COUNT_ANALYTICS_DB = "llm_span_count";
5353
private static final String TYPE_ANALYTICS_DB = "type";
5454
private static final String TAGS_DB = "tags";
55+
private static final String VERSION_COUNT_DB = "version_count";
5556
private static final String USAGE_COMPLETION_TOKENS_ANALYTICS_DB = "usage['completion_tokens']";
5657
private static final String USAGE_PROMPT_TOKENS_ANALYTICS_DB = "usage['prompt_tokens']";
5758
private static final String USAGE_TOTAL_TOKENS_ANALYTICS_DB = "usage['total_tokens']";
@@ -62,6 +63,8 @@ public class FilterQueryBuilder {
6263
private static final String LAST_MESSAGE_ANALYTICS_DB = "last_message";
6364
private static final String CREATED_AT_DB = "created_at";
6465
private static final String LAST_UPDATED_AT_DB = "last_updated_at";
66+
private static final String CREATED_BY_DB = "created_by";
67+
private static final String LAST_UPDATED_BY_DB = "last_updated_by";
6568
private static final String LAST_CREATED_EXPERIMENT_AT_DB = "last_created_experiment_at";
6669
private static final String LAST_CREATED_OPTIMIZATION_AT_DB = "last_created_optimization_at";
6770
private static final String NUMBER_OF_MESSAGES_ANALYTICS_DB = "number_of_messages";
@@ -222,7 +225,10 @@ public class FilterQueryBuilder {
222225
.put(PromptField.DESCRIPTION, DESCRIPTION_DB)
223226
.put(PromptField.CREATED_AT, CREATED_AT_DB)
224227
.put(PromptField.LAST_UPDATED_AT, LAST_UPDATED_AT_DB)
228+
.put(PromptField.CREATED_BY, CREATED_BY_DB)
229+
.put(PromptField.LAST_UPDATED_BY, LAST_UPDATED_BY_DB)
225230
.put(PromptField.TAGS, TAGS_DB)
231+
.put(PromptField.VERSION_COUNT, VERSION_COUNT_DB)
226232
.build());
227233

228234
private static final Map<DatasetField, String> DATASET_FIELDS_MAP = new EnumMap<>(
@@ -232,7 +238,9 @@ public class FilterQueryBuilder {
232238
.put(DatasetField.DESCRIPTION, DESCRIPTION_DB)
233239
.put(DatasetField.TAGS, TAGS_DB)
234240
.put(DatasetField.CREATED_AT, CREATED_AT_DB)
241+
.put(DatasetField.CREATED_BY, CREATED_BY_DB)
235242
.put(DatasetField.LAST_UPDATED_AT, LAST_UPDATED_AT_DB)
243+
.put(DatasetField.LAST_UPDATED_BY, LAST_UPDATED_BY_DB)
236244
.put(DatasetField.LAST_CREATED_EXPERIMENT_AT, LAST_CREATED_EXPERIMENT_AT_DB)
237245
.put(DatasetField.LAST_CREATED_OPTIMIZATION_AT, LAST_CREATED_OPTIMIZATION_AT_DB)
238246
.build());
@@ -303,15 +311,20 @@ public class FilterQueryBuilder {
303311
.add(PromptField.DESCRIPTION)
304312
.add(PromptField.CREATED_AT)
305313
.add(PromptField.LAST_UPDATED_AT)
314+
.add(PromptField.CREATED_BY)
315+
.add(PromptField.LAST_UPDATED_BY)
306316
.add(PromptField.TAGS)
317+
.add(PromptField.VERSION_COUNT)
307318
.build(),
308319
FilterStrategy.DATASET, EnumSet.copyOf(ImmutableSet.<DatasetField>builder()
309320
.add(DatasetField.ID)
310321
.add(DatasetField.NAME)
311322
.add(DatasetField.DESCRIPTION)
312323
.add(DatasetField.TAGS)
313324
.add(DatasetField.CREATED_AT)
325+
.add(DatasetField.CREATED_BY)
314326
.add(DatasetField.LAST_UPDATED_AT)
327+
.add(DatasetField.LAST_UPDATED_BY)
315328
.add(DatasetField.LAST_CREATED_EXPERIMENT_AT)
316329
.add(DatasetField.LAST_CREATED_OPTIMIZATION_AT)
317330
.build()),

apps/opik-backend/src/test/java/com/comet/opik/api/resources/v1/priv/DatasetsResourceTest.java

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,7 +1810,12 @@ void getDatasets__whenFetchingAllDatasets__thenReturnDatasetsSortedByByValidFiel
18101810

18111811
mockTargetWorkspace(apiKey, workspaceName, workspaceId);
18121812

1813-
List<Dataset> datasets = PodamFactoryUtils.manufacturePojoList(factory, Dataset.class);
1813+
List<Dataset> datasets = PodamFactoryUtils.manufacturePojoList(factory, Dataset.class).stream()
1814+
.map(d -> d.toBuilder()
1815+
.lastUpdatedBy(USER)
1816+
.createdBy(USER)
1817+
.build())
1818+
.toList();
18141819

18151820
datasets.forEach(dataset -> {
18161821
var id = createAndAssert(dataset, apiKey, workspaceName);
@@ -1838,13 +1843,18 @@ private Stream<Arguments> getDatasets__whenFetchingAllDatasets__thenReturnDatase
18381843
Comparator<Dataset> tagsComparator = Comparator.comparing(d -> d.tags().toString(),
18391844
String.CASE_INSENSITIVE_ORDER);
18401845
Comparator<Dataset> createdAtComparator = Comparator.comparing(Dataset::createdAt);
1846+
Comparator<Dataset> createdByComparator = Comparator.comparing(Dataset::createdBy,
1847+
String.CASE_INSENSITIVE_ORDER);
18411848
Comparator<Dataset> lastUpdatedAtComparator = Comparator.comparing(Dataset::lastUpdatedAt);
1849+
Comparator<Dataset> lastUpdatedByComparator = Comparator.comparing(Dataset::lastUpdatedBy,
1850+
String.CASE_INSENSITIVE_ORDER);
18421851
Comparator<Dataset> lastCreatedExperimentAtComparator = Comparator.comparing(
18431852
Dataset::lastCreatedExperimentAt,
18441853
Comparator.nullsLast(Comparator.naturalOrder()));
18451854
Comparator<Dataset> lastCreatedOptimizationAtComparator = Comparator.comparing(
18461855
Dataset::lastCreatedOptimizationAt,
18471856
Comparator.nullsLast(Comparator.naturalOrder()));
1857+
Comparator<Dataset> idComparatorReversed = Comparator.comparing(Dataset::id).reversed();
18481858

18491859
return Stream.of(
18501860
// ID field sorting
@@ -1857,10 +1867,10 @@ private Stream<Arguments> getDatasets__whenFetchingAllDatasets__thenReturnDatase
18571867

18581868
// NAME field sorting
18591869
Arguments.of(
1860-
nameComparator,
1870+
nameComparator.thenComparing(idComparatorReversed),
18611871
SortingField.builder().field(SortableFields.NAME).direction(Direction.ASC).build()),
18621872
Arguments.of(
1863-
nameComparator.reversed(),
1873+
nameComparator.reversed().thenComparing(idComparatorReversed),
18641874
SortingField.builder().field(SortableFields.NAME).direction(Direction.DESC).build()),
18651875

18661876
// DESCRIPTION field sorting
@@ -1887,6 +1897,14 @@ private Stream<Arguments> getDatasets__whenFetchingAllDatasets__thenReturnDatase
18871897
createdAtComparator.reversed(),
18881898
SortingField.builder().field(SortableFields.CREATED_AT).direction(Direction.DESC).build()),
18891899

1900+
// CREATED_BY field sorting
1901+
Arguments.of(
1902+
createdByComparator.thenComparing(idComparatorReversed),
1903+
SortingField.builder().field(SortableFields.CREATED_BY).direction(Direction.ASC).build()),
1904+
Arguments.of(
1905+
createdByComparator.reversed().thenComparing(idComparatorReversed),
1906+
SortingField.builder().field(SortableFields.CREATED_BY).direction(Direction.DESC).build()),
1907+
18901908
// LAST_UPDATED_AT field sorting
18911909
Arguments.of(
18921910
lastUpdatedAtComparator,
@@ -1897,6 +1915,16 @@ private Stream<Arguments> getDatasets__whenFetchingAllDatasets__thenReturnDatase
18971915
SortingField.builder().field(SortableFields.LAST_UPDATED_AT).direction(Direction.DESC)
18981916
.build()),
18991917

1918+
// LAST_UPDATED_BY field sorting
1919+
Arguments.of(
1920+
lastUpdatedByComparator.thenComparing(idComparatorReversed),
1921+
SortingField.builder().field(SortableFields.LAST_UPDATED_BY).direction(Direction.ASC)
1922+
.build()),
1923+
Arguments.of(
1924+
lastUpdatedByComparator.reversed().thenComparing(idComparatorReversed),
1925+
SortingField.builder().field(SortableFields.LAST_UPDATED_BY).direction(Direction.DESC)
1926+
.build()),
1927+
19001928
// LAST_CREATED_EXPERIMENT_AT field sorting
19011929
Arguments.of(
19021930
lastCreatedExperimentAtComparator,
@@ -2156,6 +2184,29 @@ private Stream<Arguments> getValidFilters() {
21562184
.build(),
21572185
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
21582186

2187+
// CREATED_BY field tests
2188+
Arguments.of(
2189+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2190+
.field(DatasetField.CREATED_BY)
2191+
.operator(Operator.EQUAL)
2192+
.value(USER)
2193+
.build(),
2194+
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
2195+
Arguments.of(
2196+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2197+
.field(DatasetField.CREATED_BY)
2198+
.operator(Operator.NOT_EQUAL)
2199+
.value(USER)
2200+
.build(),
2201+
(Function<List<Dataset>, List<Dataset>>) datasets -> List.of()),
2202+
Arguments.of(
2203+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2204+
.field(DatasetField.CREATED_BY)
2205+
.operator(Operator.CONTAINS)
2206+
.value(USER.substring(0, 3))
2207+
.build(),
2208+
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
2209+
21592210
// LAST_UPDATED_AT field tests (following prompt test pattern)
21602211
Arguments.of(
21612212
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
@@ -2172,6 +2223,29 @@ private Stream<Arguments> getValidFilters() {
21722223
.build(),
21732224
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
21742225

2226+
// LAST_UPDATED_BY field tests
2227+
Arguments.of(
2228+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2229+
.field(DatasetField.LAST_UPDATED_BY)
2230+
.operator(Operator.EQUAL)
2231+
.value(USER)
2232+
.build(),
2233+
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
2234+
Arguments.of(
2235+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2236+
.field(DatasetField.LAST_UPDATED_BY)
2237+
.operator(Operator.NOT_EQUAL)
2238+
.value(USER)
2239+
.build(),
2240+
(Function<List<Dataset>, List<Dataset>>) datasets -> List.of()),
2241+
Arguments.of(
2242+
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()
2243+
.field(DatasetField.LAST_UPDATED_BY)
2244+
.operator(Operator.CONTAINS)
2245+
.value(USER.substring(0, 3))
2246+
.build(),
2247+
(Function<List<Dataset>, List<Dataset>>) datasets -> datasets),
2248+
21752249
// LAST_CREATED_EXPERIMENT_AT field tests
21762250
Arguments.of(
21772251
(Function<List<Dataset>, DatasetFilter>) datasets -> DatasetFilter.builder()

0 commit comments

Comments
 (0)