Skip to content

Commit

Permalink
Labels multiselect:
Browse files Browse the repository at this point in the history
combine the labels filter by operation to handle musliselect when building the query

Labels multiselect:
Add one test case in execution repository

code clean up

code clean up
  • Loading branch information
aeSouid committed Feb 25, 2025
1 parent daca567 commit eeefdca
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,21 @@ protected void find() {
executions = executionRepository.find(Pageable.from(1, 10), null, filters);
assertThat(executions.getTotal(), is(0L));

filters = List.of(QueryFilter.builder()
.field(QueryFilter.Field.LABELS)
.operation(QueryFilter.Op.EQUALS)
.value(Map.of("key", "value"))
.build(),
QueryFilter.builder()
.field(QueryFilter.Field.LABELS)
.operation(QueryFilter.Op.EQUALS)
.value(Map.of("keyTest", "valueTest"))
.build()

);
executions = executionRepository.find(Pageable.from(1, 10), null, filters);
assertThat(executions.getTotal(), is(1L));

Check failure on line 188 in core/src/test/java/io/kestra/core/repositories/AbstractExecutionRepositoryTest.java

View workflow job for this annotation

GitHub Actions / Java Tests Report

io.kestra.repository.h2.H2ExecutionRepositoryTest ► find()

Failed test found in: jdbc-h2/build/test-results/test/TEST-io.kestra.repository.h2.H2ExecutionRepositoryTest.xml jdbc-mysql/build/test-results/test/TEST-io.kestra.repository.mysql.MysqlExecutionRepositoryTest.xml jdbc-postgres/build/test-results/test/TEST-io.kestra.repository.postgres.PostgresExecutionRepositoryTest.xml Error: java.lang.AssertionError:
Raw output
java.lang.AssertionError: 
Expected: is <1L>
     but: was <0L>
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:6)
	at io.kestra.core.repositories.AbstractExecutionRepositoryTest.find(AbstractExecutionRepositoryTest.java:188)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:142)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:162)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:119)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:129)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

filters = List.of(QueryFilter.builder()
.field(QueryFilter.Field.FLOW_ID)
.operation(QueryFilter.Op.EQUALS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ public static Condition findCondition(Object labels, QueryFilter.Op operation) {
});

}
return conditions.isEmpty() ? DSL.trueCondition() : DSL.and(conditions);
return conditions.isEmpty() ? DSL.trueCondition() : DSL.or(conditions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public static Condition findCondition(Object labels, QueryFilter.Op operation) {

});
}
return conditions.isEmpty() ? DSL.trueCondition() : DSL.and(conditions);
return conditions.isEmpty() ? DSL.trueCondition() : DSL.or(conditions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ public static Condition findCondition(Object labels, QueryFilter.Op operation) {
if (labels instanceof Map<?, ?> labelValues) {
labelValues.forEach((key, value) -> {
String sql = "value -> 'labels' @> '[{\"key\":\"" + key + "\", \"value\":\"" + value + "\"}]'";
if (operation.equals(EQUALS))
if (operation.equals(EQUALS)) {
conditions.add(DSL.condition(sql));
else
} else {
conditions.add(DSL.not(DSL.condition(sql)));

}
});
}
return conditions.isEmpty() ? DSL.trueCondition() : DSL.and(conditions);
return conditions.isEmpty() ? DSL.trueCondition() : DSL.or(conditions);
}


Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/filter/KestraFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@
};
// Check if parent filter already exists
const existingFilterIndex = currentFilters.value.findIndex(
const existingFilterIndex = currentFilters.value.filter((itm) => itm.label !== "labels").findIndex(
(item) => item.label === option.value.label,
);
if (existingFilterIndex !== -1) {
Expand Down Expand Up @@ -393,7 +393,7 @@
const isOptionDisabled = () => {
if (!activeParentFilter.value) return false;
const parentIndex = currentFilters.value.findIndex(
const parentIndex = currentFilters.value.filter((itm) => itm.label !== "labels").findIndex(
(item) => item.label === activeParentFilter.value,
);
if (parentIndex === -1) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -25,14 +26,25 @@ public class QueryFilterFormatBinder implements AnnotatedRequestArgumentBinder<Q
@VisibleForTesting
static List<QueryFilter> getQueryFilters(Map<String, List<String>> queryParams) {
List<QueryFilter> filters = new ArrayList<>();
Map<QueryFilter.Op, Map<String, String>> labelsByOperation = new HashMap<>(); // Group labels by operation

queryParams.forEach((key, values) -> {
if (!key.startsWith("filters[")) return;

Matcher matcher = FILTER_PATTERN.matcher(key);

if (matcher.matches()) {
parseFilters(values, matcher, filters);
parseFilters(values, matcher, filters, labelsByOperation);
}
});
// Add a QueryFilter for each operation's labels
labelsByOperation.forEach((operation, labels) -> {
if (!labels.isEmpty()) {
filters.add(QueryFilter.builder()
.field(QueryFilter.Field.LABELS)
.operation(operation)
.value(labels)
.build());
}
});

Expand All @@ -53,21 +65,25 @@ public BindingResult<List<QueryFilter>> bind(ArgumentConversionContext<List<Quer
return () -> Optional.of(filters);
}

private static void parseFilters(List<String> values, Matcher matcher, List<QueryFilter> filters) {
private static void parseFilters(List<String> values, Matcher matcher, List<QueryFilter> filters, Map<QueryFilter.Op, Map<String, String>> labelsByOperation) {
String fieldStr = matcher.group(1);
String operationStr = matcher.group(2);
String nestedKey = matcher.group(3); // Extract nested key if present
String nestedKey = matcher.group(3); // Extract nested key if present

QueryFilter.Field field = QueryFilter.Field.fromString(fieldStr);
QueryFilter.Op operation = QueryFilter.Op.fromString(operationStr);

Object value = nestedKey != null ? Map.of(nestedKey, values.getFirst()) : getFlatValue(values, field, operation);

filters.add(QueryFilter.builder()
.field(field)
.operation(operation)
.value(value)
.build());
// For labels: Add the key-value to the appropriate operation's map
if (field == QueryFilter.Field.LABELS && nestedKey != null) {
labelsByOperation.computeIfAbsent(operation, k -> new HashMap<>()).put(nestedKey, values.getFirst());
} else {
Object value = nestedKey != null ? Map.of(nestedKey, values.getFirst()) : getFlatValue(values, field, operation);
filters.add(QueryFilter.builder()
.field(field)
.operation(operation)
.value(value)
.build());
}
}

private static Object getFlatValue(List<String> values, QueryFilter.Field field, QueryFilter.Op operation) {
Expand Down

0 comments on commit eeefdca

Please sign in to comment.