Skip to content

Commit 1d4153b

Browse files
luis100claude
andcommitted
feat(search): Phase 1 — Advanced AIP Search nested filter groups
Adds `nestedType` and `nestedParentType` properties to SearchField so config-declared fields can carry a Solr block-join context. `SearchPanel.buildSearchFilter()` groups all nested-type fields by their (nestedType, nestedParentType) pair and wraps each group in a `ParentWhichFilterParameter` — following the RepresentationInformation pattern — instead of emitting flat filter parameters. Config registers three EmailArchive child search fields (emailSubject, emailSender, emailSentDate) as the reference example; any future nested metadata type benefits automatically through `roda-wui.properties` alone — no Java changes required. Note: the implementation uses a `nestedType` property on SearchField rather than the `nested_group` field type originally proposed in #3661. This is simpler (no new GWT widget), equally expressive, and avoids adding a UI rendering path that would be unused for all current types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5e73bb4 commit 1d4153b

6 files changed

Lines changed: 89 additions & 1 deletion

File tree

roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,6 +1980,8 @@ public String toString() {
19801980
public static final String SEARCH_FIELD_TYPE_SUGGEST_FIELD = "suggestField";
19811981
public static final String SEARCH_FIELD_TYPE_SUGGEST_PARTIAL = "suggestPartial";
19821982
public static final String SEARCH_FIELD_TYPE_CONTROLLED = "controlled";
1983+
public static final String SEARCH_FIELD_NESTED_TYPE = "nestedType";
1984+
public static final String SEARCH_FIELD_NESTED_PARENT_TYPE = "nestedParentType";
19831985
public static final String SEARCH_WITH_PREFILTER_HANDLER = "$prefilter";
19841986
public static final String SEARCH_WITH_SAVED_HANDLER = "$savedSearch";
19851987

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/AdvancedSearchFieldsPanel.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ private static List<SearchField> getSearchFieldsFromConfig(String classSimpleNam
9191
}
9292

9393
searchField.setSuggestPartial(suggestPartial);
94+
95+
String nestedType = ConfigurationManager.getString(fieldPrefix, RodaConstants.SEARCH_FIELD_NESTED_TYPE);
96+
String nestedParentType = ConfigurationManager.getString(fieldPrefix,
97+
RodaConstants.SEARCH_FIELD_NESTED_PARENT_TYPE);
98+
if (nestedType != null) {
99+
searchField.setNestedType(nestedType);
100+
}
101+
if (nestedParentType != null) {
102+
searchField.setNestedParentType(nestedParentType);
103+
}
104+
94105
searchFields.add(searchField);
95106
}
96107
}
@@ -204,6 +215,10 @@ private void setSuggestions(SearchFieldPanel searchFieldPanel) {
204215
}
205216
}
206217

218+
public SearchField getSearchField(String id) {
219+
return searchFields.get(id);
220+
}
221+
207222
@Override
208223
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
209224
return addHandler(handler, ValueChangeEvent.getType());

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchField.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class SearchField implements Serializable {
2222
private Tree<String> terms;
2323
private String suggestField;
2424
private boolean suggestPartial;
25+
private String nestedType;
26+
private String nestedParentType;
2527

2628
public SearchField() {
2729
super();
@@ -115,10 +117,26 @@ public void setSuggestPartial(boolean suggestPartial) {
115117
this.suggestPartial = suggestPartial;
116118
}
117119

120+
public String getNestedType() {
121+
return nestedType;
122+
}
123+
124+
public void setNestedType(String nestedType) {
125+
this.nestedType = nestedType;
126+
}
127+
128+
public String getNestedParentType() {
129+
return nestedParentType;
130+
}
131+
132+
public void setNestedParentType(String nestedParentType) {
133+
this.nestedParentType = nestedParentType;
134+
}
135+
118136
@Override
119137
public String toString() {
120138
return "SearchField [id=" + id + ", searchFields=" + searchFields + ", label=" + label + ", type=" + type
121139
+ ", fixed=" + fixed + ", terms=" + terms + ", suggestField=" + suggestField + ", suggestPartial="
122-
+ suggestPartial + "]";
140+
+ suggestPartial + ", nestedType=" + nestedType + ", nestedParentType=" + nestedParentType + "]";
123141
}
124142
}

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchPanel.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@
99

1010
import java.util.ArrayList;
1111
import java.util.HashMap;
12+
import java.util.Iterator;
13+
import java.util.LinkedHashMap;
1214
import java.util.List;
1315
import java.util.Map;
1416

1517
import org.roda.core.data.common.RodaConstants;
1618
import org.roda.core.data.v2.index.IsIndexed;
1719
import org.roda.core.data.v2.index.filter.AllFilterParameter;
20+
import org.roda.core.data.v2.index.filter.AndFiltersParameters;
1821
import org.roda.core.data.v2.index.filter.BasicSearchFilterParameter;
1922
import org.roda.core.data.v2.index.filter.Filter;
2023
import org.roda.core.data.v2.index.filter.FilterParameter;
2124
import org.roda.core.data.v2.index.filter.OrFiltersParameters;
25+
import org.roda.core.data.v2.index.filter.ParentWhichFilterParameter;
2226
import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
2327
import org.roda.core.data.v2.ip.IndexedAIP;
2428
import org.roda.wui.client.common.NoAsyncCallback;
@@ -400,6 +404,31 @@ private Filter buildSearchFilter() {
400404
}
401405
}
402406

407+
// Group nested-type fields into ParentWhichFilterParameter block-join queries
408+
Map<String, List<FilterParameter>> nestedGroups = new LinkedHashMap<>();
409+
Iterator<Map.Entry<String, FilterParameter>> it = advancedSearchFilters.entrySet().iterator();
410+
while (it.hasNext()) {
411+
Map.Entry<String, FilterParameter> entry = it.next();
412+
SearchField sf = advancedSearchFieldsPanel.getSearchField(entry.getKey());
413+
if (sf != null && sf.getNestedType() != null && sf.getNestedParentType() != null) {
414+
String groupKey = sf.getNestedType() + "\0" + sf.getNestedParentType();
415+
nestedGroups.computeIfAbsent(groupKey, k -> new ArrayList<>()).add(entry.getValue());
416+
it.remove();
417+
}
418+
}
419+
420+
for (Map.Entry<String, List<FilterParameter>> entry : nestedGroups.entrySet()) {
421+
String[] parts = entry.getKey().split("\0", 2);
422+
String nestedType = parts[0];
423+
String nestedParentType = parts[1];
424+
List<FilterParameter> childFilters = new ArrayList<>(entry.getValue());
425+
childFilters.add(new SimpleFilterParameter("content_type", nestedType));
426+
FilterParameter childrenFilter = childFilters.size() == 1 ? childFilters.get(0)
427+
: new AndFiltersParameters(childFilters);
428+
parameters.add(new ParentWhichFilterParameter(new SimpleFilterParameter("content_type", nestedParentType),
429+
childrenFilter));
430+
}
431+
403432
parameters.addAll(advancedSearchFilters.values());
404433
}
405434

roda-ui/roda-wui/src/main/resources/config/i18n/ServerMessages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ ui.search.fields.IndexedAIP.level=Level
1313
ui.search.fields.IndexedAIP.ingestSIPIds=Ingest SIP identifier
1414
ui.search.fields.IndexedAIP.type=Type
1515
ui.search.fields.IndexedAIP.hasRepresentations=With files
16+
ui.search.fields.IndexedAIP.emailSubject=Email subject
17+
ui.search.fields.IndexedAIP.emailSender=Email sender
18+
ui.search.fields.IndexedAIP.emailSentDate=Email sent date
1619
ui.search.fields.IndexedAIP.retentionPeriodStartDate=Retention start date
1720
ui.search.fields.IndexedAIP.overdueDate=Overdue on
1821
ui.search.fields.IndexedAIP.destroyedOn=Destroyed on

roda-ui/roda-wui/src/main/resources/config/roda-wui.properties

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ ui.search.fields.IndexedAIP = origination
373373
ui.search.fields.IndexedAIP = ingestSIPIds
374374
ui.search.fields.IndexedAIP = dates
375375
ui.search.fields.IndexedAIP = level
376+
ui.search.fields.IndexedAIP = emailSubject
377+
ui.search.fields.IndexedAIP = emailSender
378+
ui.search.fields.IndexedAIP = emailSentDate
376379

377380
ui.search.fields.IndexedAIP.identifier.fields = id
378381
ui.search.fields.IndexedAIP.identifier.i18n = ui.search.fields.IndexedAIP.identifier
@@ -442,6 +445,24 @@ ui.search.fields.IndexedAIP.type.i18n = ui.search.fields.IndexedAIP.type
442445
ui.search.fields.IndexedAIP.type.type = text
443446
ui.search.fields.IndexedAIP.type.fixed = true
444447

448+
ui.search.fields.IndexedAIP.emailSubject.fields = subject_txt
449+
ui.search.fields.IndexedAIP.emailSubject.i18n = ui.search.fields.IndexedAIP.emailSubject
450+
ui.search.fields.IndexedAIP.emailSubject.type = text
451+
ui.search.fields.IndexedAIP.emailSubject.nestedType = email
452+
ui.search.fields.IndexedAIP.emailSubject.nestedParentType = emailarchive
453+
454+
ui.search.fields.IndexedAIP.emailSender.fields = sender_s
455+
ui.search.fields.IndexedAIP.emailSender.i18n = ui.search.fields.IndexedAIP.emailSender
456+
ui.search.fields.IndexedAIP.emailSender.type = text
457+
ui.search.fields.IndexedAIP.emailSender.nestedType = email
458+
ui.search.fields.IndexedAIP.emailSender.nestedParentType = emailarchive
459+
460+
ui.search.fields.IndexedAIP.emailSentDate.fields = sentDate_dt
461+
ui.search.fields.IndexedAIP.emailSentDate.i18n = ui.search.fields.IndexedAIP.emailSentDate
462+
ui.search.fields.IndexedAIP.emailSentDate.type = date
463+
ui.search.fields.IndexedAIP.emailSentDate.nestedType = email
464+
ui.search.fields.IndexedAIP.emailSentDate.nestedParentType = emailarchive
465+
445466
ui.search.fields.IndexedRepresentation = identifier
446467
ui.search.fields.IndexedRepresentation = type
447468
ui.search.fields.IndexedRepresentation = original

0 commit comments

Comments
 (0)