Fix: default sort option (lastModified) for discovery

This commit is contained in:
Bui Thai Hai
2023-05-25 09:57:13 +07:00
parent c0446d45dc
commit eb46a99dff
9 changed files with 132 additions and 3 deletions

View File

@@ -9,6 +9,7 @@ package org.dspace.discovery.configuration;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -22,6 +23,11 @@ public class DiscoverySortConfiguration {
private List<DiscoverySortFieldConfiguration> sortFields = new ArrayList<DiscoverySortFieldConfiguration>();
/**
* Default sort configuration to use when needed
*/
@Nullable private DiscoverySortFieldConfiguration defaultSortField;
public List<DiscoverySortFieldConfiguration> getSortFields() {
return sortFields;
}
@@ -30,6 +36,14 @@ public class DiscoverySortConfiguration {
this.sortFields = sortFields;
}
public DiscoverySortFieldConfiguration getDefaultSortField() {
return defaultSortField;
}
public void setDefaultSortField(DiscoverySortFieldConfiguration configuration) {
this.defaultSortField = configuration;
}
public DiscoverySortFieldConfiguration getSortFieldConfiguration(String sortField) {
if (StringUtils.isBlank(sortField)) {
return null;

View File

@@ -332,7 +332,9 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
private String getDefaultSortDirection(DiscoverySortConfiguration searchSortConfiguration, String sortOrder) {
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
if (searchSortConfiguration.getDefaultSortField() != null) {
sortOrder = searchSortConfiguration.getDefaultSortField().getDefaultSortOrder().name();
} else if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
!searchSortConfiguration.getSortFields().isEmpty()) {
sortOrder = searchSortConfiguration.getSortFields().get(0).getDefaultSortOrder().name();
}
@@ -342,7 +344,9 @@ public class DiscoverQueryBuilder implements InitializingBean {
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
String sortBy;// Attempt to find the default one, if none found we use SCORE
sortBy = "score";
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
if (searchSortConfiguration.getDefaultSortField() != null) {
sortBy = searchSortConfiguration.getDefaultSortField().getMetadataField();
} else if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
!searchSortConfiguration.getSortFields().isEmpty()) {
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getSortFields().get(0);
if (StringUtils.isBlank(defaultSort.getMetadataField())) {

View File

@@ -8,13 +8,16 @@
package org.dspace.discovery;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.dspace.AbstractIntegrationTestWithDatabase;
@@ -24,6 +27,7 @@ import org.dspace.authorize.AuthorizeException;
import org.dspace.builder.ClaimedTaskBuilder;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.PoolTaskBuilder;
import org.dspace.builder.WorkflowItemBuilder;
@@ -39,6 +43,8 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
import org.dspace.discovery.indexobject.IndexableClaimedTask;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.discovery.indexobject.IndexableItem;
@@ -731,6 +737,68 @@ public class DiscoveryIT extends AbstractIntegrationTestWithDatabase {
}
}
/**
* Test designed to check if default sort option for Discovery is working, using <code>workspace</code>
* DiscoveryConfiguration <br/>
* <b>Note</b>: this test will be skipped if <code>workspace</code> do not have a default sort option set and of
* metadataType <code>dc_date_accessioned</code> or <code>lastModified</code>
* @throws SearchServiceException
*/
@Test
public void searchWithDefaultSortServiceTest() throws SearchServiceException {
DiscoveryConfiguration workspaceConf = SearchUtils.getDiscoveryConfiguration("workspace", null);
// Skip if no default sort option set for workspaceConf
if (workspaceConf.getSearchSortConfiguration().getDefaultSortField() == null) {
return;
}
DiscoverySortFieldConfiguration defaultSortField =
workspaceConf.getSearchSortConfiguration().getDefaultSortField();
// Populate the testing objects: create items in eperson's workspace and perform search in it
int numberItems = 10;
context.turnOffAuthorisationSystem();
EPerson submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org").build();
context.setCurrentUser(submitter);
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
for (int i = 0; i < numberItems; i++) {
ItemBuilder.createItem(context, collection)
.withTitle("item " + i)
.build();
}
// Build query with default parameters (except for workspaceConf)
DiscoverQuery discoverQuery = SearchUtils.getQueryBuilder()
.buildQuery(context, new IndexableCollection(collection), workspaceConf,"",null,"Item",null,null,
null,null);
DiscoverResult result = searchService.search(context, discoverQuery);
if (defaultSortField.getMetadataField().equals("dc_date_accessioned")) {
// Verify that search results are sort by dc_date_accessioned
LinkedList<String> dc_date_accesioneds = result.getIndexableObjects().stream()
.map(o -> ((Item) o.getIndexedObject()).getMetadata())
.map(l -> l.stream().filter(m -> m.getMetadataField().toString().equals("dc_date_accessioned"))
.map(m -> m.getValue()).findFirst().orElse("")
)
.collect(Collectors.toCollection(LinkedList::new));
assertFalse(dc_date_accesioneds.isEmpty());
for (int i = 1; i < dc_date_accesioneds.size() - 1; i++) {
assertTrue(dc_date_accesioneds.get(i).compareTo(dc_date_accesioneds.get(i + 1)) >= 0);
}
} else if (defaultSortField.getMetadataField().equals("lastModified")) {
LinkedList<String> lastModifieds = result.getIndexableObjects().stream()
.map(o -> ((Item) o.getIndexedObject()).getLastModified().toString())
.collect(Collectors.toCollection(LinkedList::new));
assertFalse(lastModifieds.isEmpty());
for (int i = 1; i < lastModifieds.size() - 1; i++) {
assertTrue(lastModifieds.get(i).compareTo(lastModifieds.get(i + 1)) >= 0);
}
}
}
private void assertSearchQuery(String resourceType, int size) throws SearchServiceException {
assertSearchQuery(resourceType, size, size, 0, -1);
}

View File

@@ -80,6 +80,15 @@ public class DiscoverConfigurationConverter
sortOption.setSortOrder(discoverySearchSortConfiguration.getDefaultSortOrder().name());
searchConfigurationRest.addSortOption(sortOption);
}
DiscoverySortFieldConfiguration defaultSortField = searchSortConfiguration.getDefaultSortField();
if (defaultSortField != null) {
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
sortOption.setName(defaultSortField.getMetadataField());
sortOption.setActualName(defaultSortField.getType());
sortOption.setSortOrder(defaultSortField.getDefaultSortOrder().name());
searchConfigurationRest.setDefaultSortOption(sortOption);
}
}
}

View File

@@ -31,6 +31,8 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
private List<Filter> filters = new LinkedList<>();
private List<SortOption> sortOptions = new LinkedList<>();
private SortOption defaultSortOption;
public String getCategory() {
return CATEGORY;
}
@@ -75,6 +77,14 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
return sortOptions;
}
public SortOption getDefaultSortOption() {
return defaultSortOption;
}
public void setDefaultSortOption(SortOption defaultSortOption) {
this.defaultSortOption = defaultSortOption;
}
@Override
public boolean equals(Object object) {
return (object instanceof SearchConfigurationRest &&

View File

@@ -1286,8 +1286,10 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
context.restoreAuthSystemState();
//Update this test since dc.date.accessioned is now configured for workspace configuration,
// which will return status 400 instead of 422 if left unchanged
getClient().perform(get("/api/discover/search/objects")
.param("sort", "dc.date.accessioned, ASC")
.param("sort", "person.familyName, ASC")
.param("configuration", "workspace"))
.andExpect(status().isUnprocessableEntity());
}

View File

@@ -115,6 +115,8 @@ public class RestDiscoverQueryBuilderTest {
sortConfiguration.setSortFields(listSortField);
sortConfiguration.setDefaultSortField(defaultSort);
discoveryConfiguration.setSearchSortConfiguration(sortConfiguration);
DiscoverySearchFilterFacet subjectFacet = new DiscoverySearchFilterFacet();
@@ -167,6 +169,19 @@ public class RestDiscoverQueryBuilderTest {
page.getOffset(), "SCORE", "ASC");
}
@Test
public void testSortByDefaultSortField() throws Exception {
page = PageRequest.of(2, 10, Sort.Direction.DESC, "dc.date.accessioned");
restQueryBuilder.buildQuery(context, null, discoveryConfiguration, null, null, emptyList(), page);
verify(discoverQueryBuilder, times(1))
.buildQuery(context, null, discoveryConfiguration, null, emptyList(), emptyList(),
page.getPageSize(), page.getOffset(),
discoveryConfiguration.getSearchSortConfiguration().getDefaultSortField().getMetadataField(),
discoveryConfiguration.getSearchSortConfiguration().getDefaultSortField()
.getDefaultSortOrder().name().toUpperCase());
}
@Test(expected = DSpaceBadRequestException.class)
public void testCatchIllegalArgumentException() throws Exception {
when(discoverQueryBuilder.buildQuery(any(), any(), any(), any(), any(), anyList(), any(), any(), any(),

View File

@@ -865,8 +865,11 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--The default sort filter to use for the initial workspace loading-->
<property name="defaultSortField" ref="sortDateAccessioned" />
<property name="sortFields">
<list>
<ref bean="sortDateAccessioned" />
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
@@ -938,6 +941,8 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--The default sort filter to use for the initial workspace loading-->
<property name="defaultSortField" ref="sortLastModified" />
<property name="sortFields">
<list>
<ref bean="sortLastModified" />
@@ -1015,6 +1020,7 @@
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<property name="sortFields">
<list>
<ref bean="sortLastModified" />
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />

View File

@@ -283,6 +283,7 @@
<!-- used by the DSpace Discovery Solr Indexer to track the last time a document was indexed -->
<field name="SolrIndexer.lastIndexed" type="date" indexed="true" stored="true" default="NOW" multiValued="false" omitNorms="true" />
<field name="lastModified" type="date" indexed="true" stored="true" default="NOW" multiValued="false" omitNorms="true" />
<copyField source="lastModified" dest="lastModified_dt" />
<!-- used to filter out items that are older versions of another item -->
<field name="latestVersion" type="boolean" indexed="true" stored="true" default="true" multiValued="false" omitNorms="true"/>