Skip to content

Commit cc4b439

Browse files
fix: properly compute search panes with related entities (bis)
Related: #159
1 parent cbcc57e commit cc4b439

File tree

5 files changed

+49
-32
lines changed

5 files changed

+49
-32
lines changed

src/main/java/org/springframework/data/jpa/datatables/SpecificationBuilder.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public Predicate toPredicate(@NonNull Root<S> root, @NonNull CriteriaQuery<?> qu
2828
if (input.getSearchPanes() != null) {
2929
input.getSearchPanes().forEach((attribute, values) -> {
3030
if (!values.isEmpty()) {
31-
predicates.columns.add(root.get(attribute).in(values));
31+
Predicate predicate = SpecificationBuilder.getPathRecursively(root, attribute).in(values);
32+
predicates.columns.add(predicate);
3233
}
3334
});
3435
}
@@ -89,4 +90,13 @@ Predicate toPredicate(CriteriaBuilder criteriaBuilder) {
8990
return columns.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(columns.toArray(new Predicate[0]));
9091
}
9192
}
93+
94+
public static Path<?> getPathRecursively(Root<?> root, String attribute) {
95+
String[] parts = attribute.split("\\.");
96+
Path<?> path = root;
97+
for (String part : parts) {
98+
path = path.get(part);
99+
}
100+
return path;
101+
}
92102
}

src/main/java/org/springframework/data/jpa/datatables/mapping/DataTablesInput.java

+12-18
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import jakarta.validation.constraints.NotEmpty;
77
import jakarta.validation.constraints.NotNull;
88
import java.util.*;
9-
import java.util.regex.Matcher;
10-
import java.util.regex.Pattern;
119

1210
/**
1311
* The format of the payload sent by the client.
@@ -16,13 +14,6 @@
1614
*/
1715
@Data
1816
public class DataTablesInput {
19-
/**
20-
* Format: <code>searchPanes.$attribute.0</code> (<code>searchPanes[$attribute][0]</code> without jquery.spring-friendly.js)
21-
*
22-
* @see <a href="https://github.com/DataTables/SearchPanes/blob/5e6d3229cd90594cc67d6d266321f1c922fc9231/src/searchPanes.ts#L119-L137">source</a>
23-
*/
24-
private static final Pattern SEARCH_PANES_REGEX = Pattern.compile("^searchPanes\\.(\\w+)\\.\\d+$");
25-
2617
/**
2718
* Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side
2819
* processing requests are drawn in sequence by DataTables (Ajax requests are asynchronous and
@@ -137,17 +128,20 @@ public void addOrder(String columnName, boolean ascending) {
137128

138129
public void parseSearchPanesFromQueryParams(Map<String, String> queryParams, Collection<String> attributes) {
139130
Map<String, Set<String>> searchPanes = new HashMap<>();
140-
attributes.forEach(attribute -> searchPanes.put(attribute, new HashSet<>()));
141-
142-
queryParams.forEach((key, value) -> {
143-
Matcher matcher = SEARCH_PANES_REGEX.matcher(key);
144-
if (matcher.matches()) {
145-
String attribute = matcher.group(1);
146-
if (attributes.contains(attribute)) {
147-
searchPanes.get(attribute).add(value);
131+
132+
for (String attribute : attributes) {
133+
Set<String> values = new HashSet<>();
134+
for (int i = 0; ; i++) {
135+
String paramName = "searchPanes." + attribute + "." + i;
136+
String paramValue = queryParams.get(paramName);
137+
if (paramValue != null) {
138+
values.add(paramValue);
139+
} else {
140+
break;
148141
}
149142
}
150-
});
143+
searchPanes.put(attribute, values);
144+
}
151145

152146
this.searchPanes = searchPanes;
153147
}

src/main/java/org/springframework/data/jpa/datatables/repository/DataTablesRepositoryImpl.java

+1-10
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private SearchPanes computeSearchPanes(DataTablesInput input, Specification<T> s
107107
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
108108
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
109109
Root<T> root = query.from(getDomainClass());
110-
Path<?> path = getPath(root, attribute);
110+
Path<?> path = SpecificationBuilder.getPathRecursively(root, attribute);
111111

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

133-
private Path<?> getPath(Root<T> root, String attribute) {
134-
String[] parts = attribute.split("\\.");
135-
Path<?> path = root;
136-
for (String part : parts) {
137-
path = path.get(part);
138-
}
139-
return path;
140-
}
141-
142133
}

src/test/java/org/springframework/data/jpa/datatables/mapping/DataTablesInputTest.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ public void testParseSearchPanes() {
2020
queryParams.put("searchPanes.attr3.test", "4");
2121
queryParams.put("searchPanes.attr4.0", "5");
2222
queryParams.put("ignored", "6");
23+
queryParams.put("searchPanes.a.t.t.r.5.0", "7");
2324

24-
input.parseSearchPanesFromQueryParams(queryParams, asList("attr1", "attr2"));
25+
input.parseSearchPanesFromQueryParams(queryParams, asList("attr1", "attr2", "a.t.t.r.5"));
2526

2627
assertThat(input.getSearchPanes()).containsOnly(
27-
entry("attr1", new HashSet<>(asList("1", "2"))),
28-
entry("attr2", new HashSet<>(asList("3")))
28+
entry("attr1", Set.of("1", "2")),
29+
entry("attr2", Set.of("3")),
30+
entry("a.t.t.r.5", Set.of("7"))
2931
);
3032
}
3133
}

src/test/java/org/springframework/data/jpa/datatables/repository/EmployeeRepositoryTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,26 @@ void withSearchPanesAndAPreFilteringSpecification() {
367367
);
368368
}
369369

370+
@Test
371+
void withSearchPanesAndFilterOnRelationship() {
372+
DataTablesInput input = createInput();
373+
374+
Map<String, Set<String>> searchPanes = new HashMap<>();
375+
searchPanes.put("position", emptySet());
376+
searchPanes.put("age", emptySet());
377+
searchPanes.put("office.city", Set.of("London", "New York"));
378+
379+
input.setSearchPanes(searchPanes);
380+
381+
DataTablesOutput<Employee> output = getOutput(input);
382+
assertThat(output.getRecordsFiltered()).isEqualTo(3);
383+
384+
assertThat(output.getSearchPanes().getOptions().get("office.city")).containsOnly(
385+
new SearchPanes.Item("London", "London", 2, 2),
386+
new SearchPanes.Item("New York", "New York", 1, 1)
387+
);
388+
}
389+
370390
protected static DataTablesInput createInput() {
371391
DataTablesInput input = new DataTablesInput();
372392
input.addColumn("id", true, true, "");

0 commit comments

Comments
 (0)