Skip to content

Commit

Permalink
[Fix #2158] Adding more filter conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
fjtirado committed Dec 12, 2024
1 parent 1da630c commit 8010ab9
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ private static ObjectNode createObjectNode(String variableName, Object variableV
}
return result;
}

public static <T> AttributeFilter<T> jsonFilter(AttributeFilter<T> filter) {
filter.setJson(true);
if (filter != null) {
filter.setJson(true);
}
return filter;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ private AttributeFilter<?> mapJsonArgument(String attribute, String key, Object
return jsonFilter(lessThanEqual(sb.toString(), value));
case BETWEEN:
return jsonFilter(filterValueMap(value, val -> between(sb.toString(), val.get("from"), val.get("to"))));
case IN:
return jsonFilter(filterValueList(value, val -> in(sb.toString(), val)));
case IS_NULL:
return jsonFilter(Boolean.TRUE.equals(value) ? isNull(sb.toString()) : notNull(sb.toString()));
case CONTAINS:
return jsonFilter(contains(sb.toString(), value));
case LIKE:
return jsonFilter(like(sb.toString(), value.toString()));
case CONTAINS_ALL:
return filterValueList(value, val -> containsAll(sb.toString(), val));
case CONTAINS_ANY:
return filterValueList(value, val -> containsAny(sb.toString(), val));
case EQUAL:
default:
return jsonFilter(equalTo(sb.toString(), value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.kie.kogito.index.graphql.query;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -71,4 +73,34 @@ void testJsonMapperBetween() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("between", Map.of("from", 1, "to", 3)))))).containsExactly(
jsonFilter(between("variables.workflowdata.number", 1, 3)));
}

@Test
void testJsonMapperIn() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("in", List.of(1, 3)))))).containsExactly(
jsonFilter(in("variables.workflowdata.number", Arrays.asList(1, 3))));
}

@Test
void testJsonMapperContains() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("contains", 1))))).containsExactly(
jsonFilter(contains("variables.workflowdata.number", 1)));
}

@Test
void testJsonMapperLike() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("like", "kk"))))).containsExactly(
jsonFilter(like("variables.workflowdata.number", "kk")));
}

@Test
void testJsonMapperNull() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("isNull", true))))).containsExactly(
jsonFilter(isNull("variables.workflowdata.number")));
}

@Test
void testJsonMapperNotNull() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata", Map.of("number", Map.of("isNull", false))))).containsExactly(
jsonFilter(notNull("variables.workflowdata.number")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@ public static BiConsumer<List<ObjectNode>, String[]> assertWithObjectNodeInOrder
public static BiConsumer<List<ObjectNode>, String[]> assertWithObjectNode() {
return (instances, ids) -> assertThat(instances).hasSize(ids == null ? 0 : ids.length).extracting(n -> n.get("id").asText()).containsExactlyInAnyOrder(ids);
}

public static <V> BiConsumer<List<V>, String[]> assertNotId() {
return (instances, ids) -> assertThat(instances).extracting("id").doesNotContainAnyElementsOf(List.of(ids));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
import org.kie.kogito.jackson.utils.ObjectMapperFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;

import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
Expand Down Expand Up @@ -83,6 +85,20 @@ public static ProcessInstanceVariableDataEvent createProcessInstanceVariableEven
return event;
}

public static ProcessInstanceVariableDataEvent createProcessInstanceVariableEvent(String processInstance,
String processId, String name, int age, boolean isMartian, List<String> aliases) {
ProcessInstanceVariableDataEvent event = new ProcessInstanceVariableDataEvent();
event.setKogitoProcessId(processId);
event.setKogitoProcessInstanceId(processInstance);
ArrayNode node = ObjectMapperFactory.get().createArrayNode();
aliases.forEach(s -> node.add(new TextNode(s)));
event.setData(ProcessInstanceVariableEventBody.create().processId(processId).processInstanceId(processInstance)
.variableName("traveller").variableValue(ObjectMapperFactory.get().createObjectNode().put("name", name).put("age", age).put("isMartian", isMartian)
.set("aliases", node))
.build());
return event;
}

public static ProcessInstanceNodeDataEvent createProcessInstanceNodeDataEvent(String processInstance, String processId, String nodeDefinitionId, String nodeInstanceId, String nodeName,
String nodeType, int eventType) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,73 @@
*/
package org.kie.kogito.index.postgresql;

import java.util.List;
import java.util.stream.Collectors;

import org.kie.kogito.persistence.api.query.AttributeFilter;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;

import static java.util.stream.Collectors.toList;

public class PostgresqlJsonNavigator {

private PostgresqlJsonNavigator() {
}

public static Predicate buildPredicate(AttributeFilter<?> filter, Root<?> root,
CriteriaBuilder builder) {
boolean isString;
switch (filter.getCondition()) {
case EQUAL:
boolean isString = filter.getValue() instanceof String;
isString = filter.getValue() instanceof String;
return builder.equal(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, filter.getValue(), isString));
case BETWEEN:
isString = filter.getValue() instanceof String;
List<Object> value = (List<Object>) filter.getValue();
return builder.between(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, value.get(0), isString),
buildObjectExpression(builder, value.get(1), isString));
case GT:
isString = filter.getValue() instanceof String;
return builder.greaterThan(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, filter.getValue(), isString));
case GTE:
isString = filter.getValue() instanceof String;
return builder.greaterThanOrEqualTo(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, filter.getValue(), isString));
case LT:
isString = filter.getValue() instanceof String;
return builder.lessThan(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, filter.getValue(), isString));
case LTE:
isString = filter.getValue() instanceof String;
return builder
.lessThanOrEqualTo(buildPathExpression(builder, root, filter.getAttribute(), isString), buildObjectExpression(builder, filter.getValue(), isString));
case IN:
List<?> values = ((List<?>) filter.getValue());
isString = values.get(0) instanceof String;
return buildPathExpression(builder, root, filter.getAttribute(), isString).in(values.stream().map(o -> buildObjectExpression(builder, o, isString)).collect(Collectors.toList()));
case LIKE:
isString = filter.getValue() instanceof String;
return builder.like(buildPathExpression(builder, root, filter.getAttribute(), isString),
filter.getValue().toString().replaceAll("\\*", "%"));
case IS_NULL:
return builder.isNull(buildPathExpression(builder, root, filter.getAttribute(), false));
case NOT_NULL:
return builder.isNotNull(buildPathExpression(builder, root, filter.getAttribute(), false));
}
throw new UnsupportedOperationException();
}

private static Expression<?> buildObjectExpression(CriteriaBuilder builder, Object value, boolean isString) {
private static Expression buildObjectExpression(CriteriaBuilder builder, Object value, boolean isString) {
return isString ? builder.literal(value) : builder.function("to_jsonb", Object.class, builder.literal(value));
}

private static Expression<?> buildPathExpression(CriteriaBuilder builder, Root<?> root, String attributeName, boolean isStr) {
private static Expression buildObjectExpression(CriteriaBuilder builder, Object value) {
return buildObjectExpression(builder, value, value instanceof String);
}

private static Expression buildPathExpression(CriteriaBuilder builder, Root<?> root, String attributeName, boolean isStr) {
String[] attributes = attributeName.split("\\.");
Expression<?>[] arguments = new Expression[attributes.length];
arguments[0] = root.get(attributes[0]);
Expand All @@ -53,5 +93,4 @@ private static Expression<?> buildPathExpression(CriteriaBuilder builder, Root<?
}
return isStr ? builder.function("jsonb_extract_path_text", String.class, arguments) : builder.function("jsonb_extract_path", Object.class, arguments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.kie.kogito.index.postgresql.query;

import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.Test;
Expand All @@ -31,10 +32,10 @@
import io.quarkus.test.junit.QuarkusTest;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.kie.kogito.index.json.JsonUtils.jsonFilter;
import static org.kie.kogito.index.test.QueryTestUtils.assertNotId;
import static org.kie.kogito.index.test.QueryTestUtils.assertWithId;
import static org.kie.kogito.persistence.api.query.QueryFilterFactory.equalTo;
import static org.kie.kogito.persistence.api.query.QueryFilterFactory.*;

@QuarkusTest
@QuarkusTestResource(PostgreSqlQuarkusTestResource.class)
Expand All @@ -45,11 +46,75 @@ void testProcessInstanceVariables() {
String processId = "travels";
String processInstanceId = UUID.randomUUID().toString();
ProcessInstanceStorage storage = getStorage();
ProcessInstanceVariableDataEvent variableEvent = TestUtils.createProcessInstanceVariableEvent(processInstanceId, processId, "John", "Smith");
ProcessInstanceVariableDataEvent variableEvent = TestUtils.createProcessInstanceVariableEvent(processInstanceId, processId, "John", 28, false,
List.of("Super", "Astonishing", "TheRealThing"));
storage.indexVariable(variableEvent);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.firstName", "John"))), null, null, null,
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.name", "John"))), null, null, null,
processInstanceId);
queryAndAssert((instances, ids) -> assertThat(instances).isEmpty(), storage, singletonList(jsonFilter(equalTo("variables.traveller.firstName", "Smith"))), null, null, null,
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.name", "Smith"))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.isMartian", false))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.isMartian", true))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(equalTo("variables.traveller.age", 29))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(between("variables.traveller.age", 26, 30))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(between("variables.traveller.age", 1, 3))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(between("variables.traveller.age", 26, 30))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(between("variables.traveller.age", 1, 3))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(greaterThan("variables.traveller.age", 26))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(greaterThan("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(greaterThanEqual("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(greaterThanEqual("variables.traveller.age", 29))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(lessThan("variables.traveller.age", 29))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(lessThan("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(lessThanEqual("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(lessThanEqual("variables.traveller.age", 27))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(lessThanEqual("variables.traveller.age", 28))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(lessThanEqual("variables.traveller.age", 27))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(in("variables.traveller.name", List.of("John", "Smith")))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(in("variables.traveller.age", List.of("Jack", "Smith")))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(in("variables.traveller.age", List.of(28, 29)))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(in("variables.traveller.age", List.of(27, 29)))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(like("variables.traveller.name", "Joh*"))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(like("variables.traveller.name", "Joha*"))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(notNull("variables.traveller.aliases"))), null, null, null,
processInstanceId);
queryAndAssert(assertNotId(), storage, singletonList(jsonFilter(isNull("variables.traveller.aliases"))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(not(jsonFilter(isNull("variables.traveller.aliases")))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(and(List.of(jsonFilter(notNull("variables.traveller.aliases")), jsonFilter(lessThan("variables.traveller.age", 45))))), null, null, null,
processInstanceId);
queryAndAssert(assertWithId(), storage, singletonList(or(List.of(jsonFilter(notNull("variables.traveller.aliases")), jsonFilter(lessThan("variables.traveller.age", 22))))), null, null, null,
processInstanceId);
// TODO add support for json contains (requires writing dialect extension on hibernate)
//queryAndAssert(assertWithId(), storage, singletonList(jsonFilter(contains("variables.traveller.aliases", "TheRealThing"))), null, null, null,
// processInstanceId);
//queryAndAssert(assertEmpty(), storage, singletonList(jsonFilter(contains("variables.traveller.aliases", "TheDummyThing"))), null, null, null,
// processInstanceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static AttributeFilter<String> like(String attribute, String value) {
return new AttributeFilter<>(attribute, FilterCondition.LIKE, value);
}

public static AttributeFilter<String> contains(String attribute, String value) {
public static <T> AttributeFilter<T> contains(String attribute, T value) {
return new AttributeFilter<>(attribute, FilterCondition.CONTAINS, value);
}

Expand Down

0 comments on commit 8010ab9

Please sign in to comment.