Skip to content

Commit 4e567e8

Browse files
committed
[DURACOM-448] Port inverse relations feature from DSpace-CRIS
Adds support for displaying related items in entity profile pages through relation box components. Key changes: - Add DiscoveryRelatedItemConfiguration for entity-to-entity searches - Extend ScopeResolver to support Items as search scope - Modify search logic to handle RELATION.* configurations - Enhance query builder for dynamic filter injection and sort functions - Add facet support for relation searches - Update facet result converter to include configuration in responses - Fix test assertions for configuration parameter in links This enables use cases like showing all publications for a Person, researchers for an OrgUnit, or publications for a Project. Refs: bf04523, 8b4e381, 188acd2, 96a33cf
1 parent 3b9b846 commit 4e567e8

9 files changed

Lines changed: 271 additions & 665 deletions

File tree

dspace-api/src/main/java/org/dspace/discovery/DiscoverQuery.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ public class DiscoverQuery {
4040

4141
private int start = 0;
4242
private int maxResults = -1;
43+
private IndexableObject scopeObject;
44+
45+
public IndexableObject getScopeObject() {
46+
return scopeObject;
47+
}
48+
49+
public void setScopeObject(final IndexableObject scopeObject) {
50+
this.scopeObject = scopeObject;
51+
}
4352

4453
/**
4554
* Attributes used for sorting of results

dspace-api/src/main/java/org/dspace/discovery/FacetYearRange.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.regex.Matcher;
1212
import java.util.regex.Pattern;
1313

14+
import org.apache.commons.lang3.StringUtils;
1415
import org.dspace.core.Context;
1516
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
1617

@@ -104,7 +105,15 @@ private void calculateNewRangeBasedOnSearchIndex(Context context, List<String> f
104105
yearRangeQuery.setSortField(dateFacet + "_sort", DiscoverQuery.SORT_ORDER.asc);
105106
yearRangeQuery.addFilterQueries(filterQueries.toArray(new String[filterQueries.size()]));
106107
yearRangeQuery.addSearchField(dateFacet);
107-
DiscoverResult lastYearResult = searchService.search(context, scope, yearRangeQuery);
108+
boolean isRelatedEntity =
109+
StringUtils.isNotBlank(parentQuery.getDiscoveryConfigurationName()) &&
110+
parentQuery.getDiscoveryConfigurationName().toUpperCase().startsWith("RELATION");
111+
DiscoverResult lastYearResult;
112+
if (isRelatedEntity) {
113+
lastYearResult = searchService.search(context, yearRangeQuery);
114+
} else {
115+
lastYearResult = searchService.search(context, scope, yearRangeQuery);
116+
}
108117

109118
if (0 < lastYearResult.getIndexableObjects().size()) {
110119
List<DiscoverResult.SearchDocument> searchDocuments = lastYearResult
@@ -115,7 +124,12 @@ private void calculateNewRangeBasedOnSearchIndex(Context context, List<String> f
115124
}
116125
//Now get the first year
117126
yearRangeQuery.setSortField(dateFacet + "_sort", DiscoverQuery.SORT_ORDER.desc);
118-
DiscoverResult firstYearResult = searchService.search(context, scope, yearRangeQuery);
127+
DiscoverResult firstYearResult;
128+
if (isRelatedEntity) {
129+
firstYearResult = searchService.search(context, yearRangeQuery);
130+
} else {
131+
firstYearResult = searchService.search(context, scope, yearRangeQuery);
132+
}
119133
if (0 < firstYearResult.getIndexableObjects().size()) {
120134
List<DiscoverResult.SearchDocument> searchDocuments = firstYearResult
121135
.getSearchDocument(firstYearResult.getIndexableObjects().get(0));

dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ public Map<String, DiscoveryConfiguration> getMap() {
5353

5454
public void setMap(Map<String, DiscoveryConfiguration> map) {
5555
this.map = map;
56+
if (map != null) {
57+
// improve the configuration assigning the map key as id to any configuration
58+
// that doesn't have one
59+
for (Map.Entry<String, DiscoveryConfiguration> entry : map.entrySet()) {
60+
DiscoveryConfiguration conf = entry.getValue();
61+
if (StringUtils.isBlank(conf.getId())) {
62+
conf.setId(entry.getKey());
63+
}
64+
}
65+
}
5666
}
5767

5868
public Map<Integer, List<String>> getToIgnoreMetadataFields() {

dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
1313

1414
import java.sql.SQLException;
15+
import java.text.MessageFormat;
1516
import java.util.ArrayList;
1617
import java.util.List;
1718
import java.util.Objects;
19+
import java.util.Optional;
1820

1921
import org.apache.commons.collections4.CollectionUtils;
2022
import org.apache.commons.lang3.StringUtils;
@@ -33,10 +35,12 @@
3335
import org.dspace.discovery.configuration.DiscoveryConfiguration;
3436
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
3537
import org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration;
38+
import org.dspace.discovery.configuration.DiscoveryRelatedItemConfiguration;
3639
import org.dspace.discovery.configuration.DiscoverySearchFilter;
3740
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
3841
import org.dspace.discovery.configuration.DiscoverySortConfiguration;
3942
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
43+
import org.dspace.discovery.configuration.DiscoverySortFunctionConfiguration;
4044
import org.dspace.discovery.indexobject.factory.IndexFactory;
4145
import org.dspace.discovery.utils.parameter.QueryBuilderSearchFilter;
4246
import org.dspace.services.ConfigurationService;
@@ -111,17 +115,24 @@ public DiscoverQuery buildQuery(Context context, IndexableObject scope,
111115
String sortDirection)
112116
throws IllegalArgumentException, SearchServiceException {
113117

114-
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
115-
dsoTypes);
118+
DiscoverQuery queryArgs =
119+
buildCommonDiscoverQuery(
120+
context, discoveryConfiguration, query, searchFilters,
121+
dsoTypes, scope
122+
);
116123

117124
//When all search criteria are set, configure facet results
118125
addFaceting(context, scope, queryArgs, discoveryConfiguration);
119126

120127
//Configure pagination and sorting
121128
configurePagination(pageSize, offset, queryArgs);
122-
configureSorting(sortProperty, sortDirection, queryArgs, discoveryConfiguration.getSearchSortConfiguration());
129+
configureSorting(
130+
sortProperty, sortDirection, queryArgs, discoveryConfiguration.getSearchSortConfiguration(),
131+
scope
132+
);
123133

124134
addDiscoveryHitHighlightFields(discoveryConfiguration, queryArgs);
135+
Optional.ofNullable(scope).ifPresent(queryArgs::setScopeObject);
125136
return queryArgs;
126137
}
127138

@@ -186,7 +197,7 @@ public DiscoverQuery buildFacetQuery(Context context, IndexableObject scope,
186197
throws IllegalArgumentException {
187198

188199
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
189-
dsoTypes);
200+
dsoTypes, scope);
190201

191202
//When all search criteria are set, configure facet results
192203
addFacetingForFacets(context, scope, prefix, queryArgs, discoveryConfiguration, facetName, pageSize);
@@ -201,8 +212,8 @@ public DiscoverQuery buildFacetQuery(Context context, IndexableObject scope,
201212
}
202213

203214
private void configurePaginationForFacets(Long offset, DiscoverQuery queryArgs) {
204-
if (offset != null) {
205-
queryArgs.setFacetOffset(Math.toIntExact(offset));
215+
if (offset != null && queryArgs.getFacetFields().size() == 1) {
216+
queryArgs.getFacetFields().get(0).setOffset(offset.intValue());
206217
}
207218
}
208219

@@ -255,9 +266,11 @@ private void fillFacetIntoQueryArgs(Context context, IndexableObject scope, Stri
255266

256267
private DiscoverQuery buildCommonDiscoverQuery(Context context, DiscoveryConfiguration discoveryConfiguration,
257268
String query,
258-
List<QueryBuilderSearchFilter> searchFilters, List<String> dsoTypes)
269+
List<QueryBuilderSearchFilter> searchFilters,
270+
List<String> dsoTypes,
271+
IndexableObject scope)
259272
throws IllegalArgumentException {
260-
DiscoverQuery queryArgs = buildBaseQueryForConfiguration(discoveryConfiguration);
273+
DiscoverQuery queryArgs = buildBaseQueryForConfiguration(discoveryConfiguration, scope);
261274

262275
queryArgs.addFilterQueries(convertFiltersToString(context, discoveryConfiguration, searchFilters));
263276

@@ -276,19 +289,27 @@ private DiscoverQuery buildCommonDiscoverQuery(Context context, DiscoveryConfigu
276289
return queryArgs;
277290
}
278291

279-
private DiscoverQuery buildBaseQueryForConfiguration(DiscoveryConfiguration discoveryConfiguration) {
292+
private DiscoverQuery buildBaseQueryForConfiguration(DiscoveryConfiguration discoveryConfiguration,
293+
IndexableObject scope) {
294+
280295
DiscoverQuery queryArgs = new DiscoverQuery();
281296
queryArgs.setDiscoveryConfigurationName(discoveryConfiguration.getId());
282-
queryArgs.addFilterQueries(discoveryConfiguration.getDefaultFilterQueries()
283-
.toArray(
284-
new String[discoveryConfiguration
285-
.getDefaultFilterQueries()
286-
.size()]));
297+
298+
String[] queryArray = discoveryConfiguration.getDefaultFilterQueries()
299+
.toArray(new String[discoveryConfiguration.getDefaultFilterQueries().size()]);
300+
301+
if (scope != null && discoveryConfiguration instanceof DiscoveryRelatedItemConfiguration) {
302+
for (int i = 0; i < queryArray.length; i++) {
303+
queryArray[i] = MessageFormat.format(queryArray[i], scope.getID());
304+
}
305+
}
306+
307+
queryArgs.addFilterQueries(queryArray);
287308
return queryArgs;
288309
}
289310

290311
private void configureSorting(String sortProperty, String sortDirection, DiscoverQuery queryArgs,
291-
DiscoverySortConfiguration searchSortConfiguration)
312+
DiscoverySortConfiguration searchSortConfiguration, IndexableObject scope)
292313
throws IllegalArgumentException, SearchServiceException {
293314
String sortBy = sortProperty;
294315
String sortOrder = sortDirection;
@@ -312,8 +333,18 @@ private void configureSorting(String sortProperty, String sortDirection, Discove
312333
.getSortFieldConfiguration(sortBy);
313334

314335
if (sortFieldConfiguration != null) {
315-
String sortField = searchService
316-
.toSortFieldIndex(sortFieldConfiguration.getMetadataField(), sortFieldConfiguration.getType());
336+
337+
String sortField;
338+
339+
if (DiscoverySortFunctionConfiguration.SORT_FUNCTION.equals(sortFieldConfiguration.getType())) {
340+
sortField = MessageFormat.format(
341+
((DiscoverySortFunctionConfiguration) sortFieldConfiguration).getFunction(scope.getID()),
342+
scope.getID());
343+
} else {
344+
sortField = searchService
345+
.toSortFieldIndex(
346+
sortFieldConfiguration.getMetadataField(), sortFieldConfiguration.getType());
347+
}
317348

318349
if ("asc".equalsIgnoreCase(sortOrder)) {
319350
queryArgs.setSortField(sortField, DiscoverQuery.SORT_ORDER.asc);
@@ -408,11 +439,16 @@ private String[] convertFiltersToString(Context context, DiscoveryConfiguration
408439
throw new IllegalArgumentException(searchFilter.getName() + " is not a valid search filter");
409440
}
410441

411-
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
412-
filter.getIndexFieldName(),
413-
searchFilter.getOperator(),
414-
searchFilter.getValue(),
415-
discoveryConfiguration);
442+
String field = filter.getIndexFieldName();
443+
444+
DiscoverFilterQuery filterQuery =
445+
searchService.toFilterQuery(
446+
context,
447+
field,
448+
searchFilter.getOperator(),
449+
searchFilter.getValue(),
450+
discoveryConfiguration
451+
);
416452

417453
if (filterQuery != null) {
418454
filterQueries.add(filterQuery.getFilterQuery());

dspace-api/src/test/java/org/dspace/discovery/utils/DiscoverQueryBuilderTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,9 @@ public void testBuildFacetQuery() throws Exception {
322322
assertThat(discoverQuery.getMaxResults(), is(0));
323323
assertThat(discoverQuery.getStart(), is(0));
324324
assertThat(discoverQuery.getFacetMinCount(), is(1));
325-
assertThat(discoverQuery.getFacetOffset(), is(10));
326325
assertThat(discoverQuery.getFacetFields(), hasSize(1));
327326
assertThat(discoverQuery.getFacetFields(), contains(
328-
discoverFacetFieldMatcher(new DiscoverFacetField("subject", TYPE_TEXT, 11, COUNT, "prefix"))
327+
discoverFacetFieldMatcher(new DiscoverFacetField("subject", TYPE_TEXT, 11, COUNT, "prefix", 10))
329328
));
330329
}
331330

dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/DiscoverFacetResultsConverter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ private void setRequestInformation(Context context, String facetName, String pre
8282
facetResultsRest.setPrefix(prefix);
8383
facetResultsRest.setScope(dsoScope);
8484
facetResultsRest.setDsoTypes(dsoTypes);
85-
85+
if (configuration != null) {
86+
facetResultsRest.setConfiguration(configuration.getId());
87+
}
8688
facetResultsRest.setFacetEntry(convertFacetEntry(facetName, searchResult, configuration, page, projection));
8789

8890
facetResultsRest.setSort(SearchResultsRest.Sorting.fromPage(page));

dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DiscoveryRestRepository.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.dspace.discovery.SearchServiceException;
3535
import org.dspace.discovery.configuration.DiscoveryConfiguration;
3636
import org.dspace.discovery.configuration.DiscoveryConfigurationService;
37+
import org.dspace.discovery.configuration.DiscoveryRelatedItemConfiguration;
3738
import org.springframework.beans.factory.annotation.Autowired;
3839
import org.springframework.data.domain.PageRequest;
3940
import org.springframework.data.domain.Pageable;
@@ -98,13 +99,19 @@ public SearchResultsRest getSearchObjects(final String query, final List<String>
9899
DiscoveryConfiguration discoveryConfiguration = searchConfigurationService
99100
.getDiscoveryConfigurationByNameOrIndexableObject(context, configuration, scopeObject);
100101

101-
DiscoverResult searchResult = null;
102-
DiscoverQuery discoverQuery = null;
102+
boolean isRelatedItem = discoveryConfiguration instanceof DiscoveryRelatedItemConfiguration;
103+
104+
DiscoverResult searchResult;
105+
DiscoverQuery discoverQuery;
103106

104107
try {
105108
discoverQuery = queryBuilder
106109
.buildQuery(context, scopeObject, discoveryConfiguration, query, searchFilters, dsoTypes, page);
107-
searchResult = searchService.search(context, scopeObject, discoverQuery);
110+
if (isRelatedItem) {
111+
searchResult = searchService.search(context, discoverQuery);
112+
} else {
113+
searchResult = searchService.search(context, scopeObject, discoverQuery);
114+
}
108115

109116
} catch (SearchServiceException e) {
110117
log.error("Error while searching with Discovery", e);
@@ -140,9 +147,22 @@ public FacetResultsRest getFacetObjects(String facetName, String prefix, String
140147
DiscoveryConfiguration discoveryConfiguration = searchConfigurationService
141148
.getDiscoveryConfigurationByNameOrIndexableObject(context, configuration, scopeObject);
142149

143-
DiscoverQuery discoverQuery = queryBuilder.buildFacetQuery(context, scopeObject, discoveryConfiguration, prefix,
144-
query, searchFilters, dsoTypes, page, facetName);
145-
DiscoverResult searchResult = searchService.search(context, scopeObject, discoverQuery);
150+
boolean isRelatedItem = discoveryConfiguration instanceof DiscoveryRelatedItemConfiguration;
151+
152+
DiscoverResult searchResult;
153+
DiscoverQuery discoverQuery;
154+
try {
155+
discoverQuery = queryBuilder.buildFacetQuery(context, scopeObject, discoveryConfiguration, prefix, query,
156+
searchFilters, dsoTypes, page, facetName);
157+
if (isRelatedItem) {
158+
searchResult = searchService.search(context, discoverQuery);
159+
} else {
160+
searchResult = searchService.search(context, scopeObject, discoverQuery);
161+
}
162+
} catch (SearchServiceException e) {
163+
log.error("Error while searching with Discovery", e);
164+
throw new IllegalArgumentException("Error while searching with Discovery: " + e.getMessage());
165+
}
146166

147167
FacetResultsRest facetResultsRest = discoverFacetResultsConverter.convert(context, facetName, prefix, query,
148168
dsoTypes, dsoScope, searchFilters, searchResult, discoveryConfiguration, page,
@@ -159,12 +179,20 @@ public SearchResultsRest getAllFacets(String query, List<String> dsoTypes, Strin
159179
DiscoveryConfiguration discoveryConfiguration = searchConfigurationService
160180
.getDiscoveryConfigurationByNameOrIndexableObject(context, configuration, scopeObject);
161181

182+
boolean isRelatedItem = discoveryConfiguration instanceof DiscoveryRelatedItemConfiguration;
183+
162184
DiscoverResult searchResult = null;
163-
DiscoverQuery discoverQuery = null;
185+
DiscoverQuery discoverQuery;
164186

165187
try {
166188
discoverQuery = queryBuilder
167189
.buildQuery(context, scopeObject, discoveryConfiguration, query, searchFilters, dsoTypes, page);
190+
191+
if (isRelatedItem) {
192+
searchResult = searchService.search(context, discoverQuery);
193+
} else {
194+
searchResult = searchService.search(context, scopeObject, discoverQuery);
195+
}
168196
searchResult = searchService.search(context, scopeObject, discoverQuery);
169197

170198
} catch (SearchServiceException e) {

0 commit comments

Comments
 (0)