Skip to content

Commit

Permalink
fix: properly compute search panes with related entities (bis)
Browse files Browse the repository at this point in the history
Related: #159
  • Loading branch information
darrachequesne committed Feb 17, 2025
1 parent cbcc57e commit cc4b439
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public Predicate toPredicate(@NonNull Root<S> root, @NonNull CriteriaQuery<?> qu
if (input.getSearchPanes() != null) {
input.getSearchPanes().forEach((attribute, values) -> {
if (!values.isEmpty()) {
predicates.columns.add(root.get(attribute).in(values));
Predicate predicate = SpecificationBuilder.getPathRecursively(root, attribute).in(values);
predicates.columns.add(predicate);
}
});
}
Expand Down Expand Up @@ -89,4 +90,13 @@ Predicate toPredicate(CriteriaBuilder criteriaBuilder) {
return columns.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(columns.toArray(new Predicate[0]));
}
}

public static Path<?> getPathRecursively(Root<?> root, String attribute) {
String[] parts = attribute.split("\\.");
Path<?> path = root;
for (String part : parts) {
path = path.get(part);
}
return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* The format of the payload sent by the client.
Expand All @@ -16,13 +14,6 @@
*/
@Data
public class DataTablesInput {
/**
* Format: <code>searchPanes.$attribute.0</code> (<code>searchPanes[$attribute][0]</code> without jquery.spring-friendly.js)
*
* @see <a href="https://github.com/DataTables/SearchPanes/blob/5e6d3229cd90594cc67d6d266321f1c922fc9231/src/searchPanes.ts#L119-L137">source</a>
*/
private static final Pattern SEARCH_PANES_REGEX = Pattern.compile("^searchPanes\\.(\\w+)\\.\\d+$");

/**
* Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side
* processing requests are drawn in sequence by DataTables (Ajax requests are asynchronous and
Expand Down Expand Up @@ -137,17 +128,20 @@ public void addOrder(String columnName, boolean ascending) {

public void parseSearchPanesFromQueryParams(Map<String, String> queryParams, Collection<String> attributes) {
Map<String, Set<String>> searchPanes = new HashMap<>();
attributes.forEach(attribute -> searchPanes.put(attribute, new HashSet<>()));

queryParams.forEach((key, value) -> {
Matcher matcher = SEARCH_PANES_REGEX.matcher(key);
if (matcher.matches()) {
String attribute = matcher.group(1);
if (attributes.contains(attribute)) {
searchPanes.get(attribute).add(value);

for (String attribute : attributes) {
Set<String> values = new HashSet<>();
for (int i = 0; ; i++) {
String paramName = "searchPanes." + attribute + "." + i;
String paramValue = queryParams.get(paramName);
if (paramValue != null) {
values.add(paramValue);
} else {
break;
}
}
});
searchPanes.put(attribute, values);
}

this.searchPanes = searchPanes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private SearchPanes computeSearchPanes(DataTablesInput input, Specification<T> s
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<T> root = query.from(getDomainClass());
Path<?> path = getPath(root, attribute);
Path<?> path = SpecificationBuilder.getPathRecursively(root, attribute);

query.multiselect(path, criteriaBuilder.count(root));
query.groupBy(path);
Expand All @@ -130,13 +130,4 @@ private SearchPanes computeSearchPanes(DataTablesInput input, Specification<T> s
return new SearchPanes(options);
}

private Path<?> getPath(Root<T> root, String attribute) {
String[] parts = attribute.split("\\.");
Path<?> path = root;
for (String part : parts) {
path = path.get(part);
}
return path;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ public void testParseSearchPanes() {
queryParams.put("searchPanes.attr3.test", "4");
queryParams.put("searchPanes.attr4.0", "5");
queryParams.put("ignored", "6");
queryParams.put("searchPanes.a.t.t.r.5.0", "7");

input.parseSearchPanesFromQueryParams(queryParams, asList("attr1", "attr2"));
input.parseSearchPanesFromQueryParams(queryParams, asList("attr1", "attr2", "a.t.t.r.5"));

assertThat(input.getSearchPanes()).containsOnly(
entry("attr1", new HashSet<>(asList("1", "2"))),
entry("attr2", new HashSet<>(asList("3")))
entry("attr1", Set.of("1", "2")),
entry("attr2", Set.of("3")),
entry("a.t.t.r.5", Set.of("7"))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,26 @@ void withSearchPanesAndAPreFilteringSpecification() {
);
}

@Test
void withSearchPanesAndFilterOnRelationship() {
DataTablesInput input = createInput();

Map<String, Set<String>> searchPanes = new HashMap<>();
searchPanes.put("position", emptySet());
searchPanes.put("age", emptySet());
searchPanes.put("office.city", Set.of("London", "New York"));

input.setSearchPanes(searchPanes);

DataTablesOutput<Employee> output = getOutput(input);
assertThat(output.getRecordsFiltered()).isEqualTo(3);

assertThat(output.getSearchPanes().getOptions().get("office.city")).containsOnly(
new SearchPanes.Item("London", "London", 2, 2),
new SearchPanes.Item("New York", "New York", 1, 1)
);
}

protected static DataTablesInput createInput() {
DataTablesInput input = new DataTablesInput();
input.addColumn("id", true, true, "");
Expand Down

0 comments on commit cc4b439

Please sign in to comment.