
org.dspace.discovery.utils.DiscoverQueryBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery.utils;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.discovery.DiscoverFacetField;
import org.dspace.discovery.DiscoverFilterQuery;
import org.dspace.discovery.DiscoverHitHighlightingField;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.FacetYearRange;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
import org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration;
import org.dspace.discovery.configuration.DiscoverySearchFilter;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
import org.dspace.discovery.configuration.DiscoverySortConfiguration;
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
import org.dspace.discovery.indexobject.factory.IndexFactory;
import org.dspace.discovery.utils.parameter.QueryBuilderSearchFilter;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
public class DiscoverQueryBuilder implements InitializingBean {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DiscoverQueryBuilder.class);
@Autowired
private SearchService searchService;
@Autowired
private ConfigurationService configurationService;
@Autowired
private List indexableFactories;
private int pageSizeLimit;
@Override
public void afterPropertiesSet() throws Exception {
pageSizeLimit = configurationService.getIntProperty("rest.search.max.results", 100);
}
/**
* Build a discovery query
*
* @param context the DSpace context
* @param scope the scope for this discovery query
* @param discoveryConfiguration the discovery configuration for this discovery query
* @param query the query string for this discovery query
* @param searchFilters the search filters for this discovery query
* @param dsoType only include search results with this type
* @param pageSize the page size for this discovery query
* @param offset the offset for this discovery query
* @param sortProperty the sort property for this discovery query
* @param sortDirection the sort direction for this discovery query
*/
public DiscoverQuery buildQuery(Context context, IndexableObject scope,
DiscoveryConfiguration discoveryConfiguration,
String query, List searchFilters,
String dsoType, Integer pageSize, Long offset, String sortProperty,
String sortDirection) throws SearchServiceException {
List dsoTypes = dsoType != null ? singletonList(dsoType) : emptyList();
return buildQuery(context, scope, discoveryConfiguration, query, searchFilters, dsoTypes, pageSize, offset,
sortProperty, sortDirection);
}
/**
* Build a discovery query
*
* @param context the DSpace context
* @param scope the scope for this discovery query
* @param discoveryConfiguration the discovery configuration for this discovery query
* @param query the query string for this discovery query
* @param searchFilters the search filters for this discovery query
* @param dsoTypes only include search results with one of these types
* @param pageSize the page size for this discovery query
* @param offset the offset for this discovery query
* @param sortProperty the sort property for this discovery query
* @param sortDirection the sort direction for this discovery query
*/
public DiscoverQuery buildQuery(Context context, IndexableObject scope,
DiscoveryConfiguration discoveryConfiguration,
String query, List searchFilters,
List dsoTypes, Integer pageSize, Long offset, String sortProperty,
String sortDirection)
throws IllegalArgumentException, SearchServiceException {
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
dsoTypes);
//When all search criteria are set, configure facet results
addFaceting(context, scope, queryArgs, discoveryConfiguration);
//Configure pagination and sorting
configurePagination(pageSize, offset, queryArgs);
configureSorting(sortProperty, sortDirection, queryArgs, discoveryConfiguration.getSearchSortConfiguration());
addDiscoveryHitHighlightFields(discoveryConfiguration, queryArgs);
return queryArgs;
}
private void addDiscoveryHitHighlightFields(DiscoveryConfiguration discoveryConfiguration,
DiscoverQuery queryArgs) {
if (discoveryConfiguration.getHitHighlightingConfiguration() != null) {
List metadataFields = discoveryConfiguration
.getHitHighlightingConfiguration().getMetadataFields();
for (DiscoveryHitHighlightFieldConfiguration fieldConfiguration : metadataFields) {
queryArgs.addHitHighlightingField(
new DiscoverHitHighlightingField(fieldConfiguration.getField(), fieldConfiguration.getMaxSize(),
fieldConfiguration.getSnippets()));
}
}
}
/**
* Create a discovery facet query.
*
* @param context the DSpace context
* @param scope the scope for this discovery query
* @param discoveryConfiguration the discovery configuration for this discovery query
* @param prefix limit the facets results to those starting with the given prefix.
* @param query the query string for this discovery query
* @param searchFilters the search filters for this discovery query
* @param dsoType only include search results with this type
* @param pageSize the page size for this discovery query
* @param offset the offset for this discovery query
* @param facetName the facet field
*/
public DiscoverQuery buildFacetQuery(Context context, IndexableObject scope,
DiscoveryConfiguration discoveryConfiguration,
String prefix, String query, List searchFilters,
String dsoType, Integer pageSize, Long offset, String facetName)
throws IllegalArgumentException {
List dsoTypes = dsoType != null ? singletonList(dsoType) : emptyList();
return buildFacetQuery(
context, scope, discoveryConfiguration, prefix, query, searchFilters, dsoTypes, pageSize, offset,
facetName);
}
/**
* Create a discovery facet query.
*
* @param context the DSpace context
* @param scope the scope for this discovery query
* @param discoveryConfiguration the discovery configuration for this discovery query
* @param prefix limit the facets results to those starting with the given prefix.
* @param query the query string for this discovery query
* @param searchFilters the search filters for this discovery query
* @param dsoTypes only include search results with one of these types
* @param pageSize the page size for this discovery query
* @param offset the offset for this discovery query
* @param facetName the facet field
*/
public DiscoverQuery buildFacetQuery(Context context, IndexableObject scope,
DiscoveryConfiguration discoveryConfiguration,
String prefix, String query, List searchFilters,
List dsoTypes, Integer pageSize, Long offset, String facetName)
throws IllegalArgumentException {
DiscoverQuery queryArgs = buildCommonDiscoverQuery(context, discoveryConfiguration, query, searchFilters,
dsoTypes);
//When all search criteria are set, configure facet results
addFacetingForFacets(context, scope, prefix, queryArgs, discoveryConfiguration, facetName, pageSize);
//We don' want any search results, we only want facet values
queryArgs.setMaxResults(0);
//Configure pagination
configurePaginationForFacets(offset, queryArgs);
return queryArgs;
}
private void configurePaginationForFacets(Long offset, DiscoverQuery queryArgs) {
if (offset != null) {
queryArgs.setFacetOffset(Math.toIntExact(offset));
}
}
private DiscoverQuery addFacetingForFacets(Context context, IndexableObject scope, String prefix,
DiscoverQuery queryArgs, DiscoveryConfiguration discoveryConfiguration,
String facetName, Integer pageSize)
throws IllegalArgumentException {
DiscoverySearchFilterFacet facet = discoveryConfiguration.getSidebarFacet(facetName);
if (facet != null) {
queryArgs.setFacetMinCount(1);
pageSize = pageSize != null ? Math.min(pageSizeLimit, pageSize) : pageSizeLimit;
fillFacetIntoQueryArgs(context, scope, prefix, queryArgs, facet, pageSize);
} else {
throw new IllegalArgumentException(facetName + " is not a valid search facet");
}
return queryArgs;
}
private void fillFacetIntoQueryArgs(Context context, IndexableObject scope, String prefix,
DiscoverQuery queryArgs, DiscoverySearchFilterFacet facet, final int pageSize) {
if (facet.getType().equals(DiscoveryConfigurationParameters.TYPE_DATE)) {
try {
FacetYearRange facetYearRange =
searchService.getFacetYearRange(context, scope, facet, queryArgs.getFilterQueries(), queryArgs);
queryArgs.addYearRangeFacet(facet, facetYearRange);
} catch (Exception e) {
log.error(LogHelper.getHeader(context, "Error in Discovery while setting up date facet range",
"date facet: " + facet), e);
}
} else {
//Add one to our facet limit to make sure that if we have more then the shown facets that we show our
// "show more" url
int facetLimit = pageSize + 1;
//This should take care of the sorting for us
prefix = StringUtils.isNotBlank(prefix) ? prefix.toLowerCase() : null;
queryArgs.addFacetField(new DiscoverFacetField(facet.getIndexFieldName(), facet.getType(), facetLimit,
facet.getSortOrderSidebar(),
StringUtils.trimToNull(prefix)));
}
}
private DiscoverQuery buildCommonDiscoverQuery(Context context, DiscoveryConfiguration discoveryConfiguration,
String query,
List searchFilters, List dsoTypes)
throws IllegalArgumentException {
DiscoverQuery queryArgs = buildBaseQueryForConfiguration(discoveryConfiguration);
queryArgs.addFilterQueries(convertFiltersToString(context, discoveryConfiguration, searchFilters));
//Set search query
if (StringUtils.isNotBlank(query)) {
queryArgs.setQuery(query);
}
//Limit results to DSO types
if (isNotEmpty(dsoTypes)) {
dsoTypes.stream()
.map(this::getDsoType)
.forEach(queryArgs::addDSpaceObjectFilter);
}
return queryArgs;
}
private DiscoverQuery buildBaseQueryForConfiguration(DiscoveryConfiguration discoveryConfiguration) {
DiscoverQuery queryArgs = new DiscoverQuery();
queryArgs.setDiscoveryConfigurationName(discoveryConfiguration.getId());
queryArgs.addFilterQueries(discoveryConfiguration.getDefaultFilterQueries()
.toArray(
new String[discoveryConfiguration
.getDefaultFilterQueries()
.size()]));
return queryArgs;
}
private void configureSorting(String sortProperty, String sortDirection, DiscoverQuery queryArgs,
DiscoverySortConfiguration searchSortConfiguration)
throws IllegalArgumentException, SearchServiceException {
String sortBy = sortProperty;
String sortOrder = sortDirection;
//Load defaults if we did not receive values
if (sortBy == null) {
sortBy = getDefaultSortField(searchSortConfiguration);
}
if (sortOrder == null) {
sortOrder = getDefaultSortDirection(searchSortConfiguration, sortOrder);
}
if (StringUtils.isNotBlank(sortBy) && !isConfigured(sortBy, searchSortConfiguration)) {
throw new SearchServiceException(
"The field: " + sortBy + " is not configured for the configuration!");
}
//Update Discovery query
DiscoverySortFieldConfiguration sortFieldConfiguration = searchSortConfiguration
.getSortFieldConfiguration(sortBy);
if (sortFieldConfiguration != null) {
String sortField = searchService
.toSortFieldIndex(sortFieldConfiguration.getMetadataField(), sortFieldConfiguration.getType());
if ("asc".equalsIgnoreCase(sortOrder)) {
queryArgs.setSortField(sortField, DiscoverQuery.SORT_ORDER.asc);
} else if ("desc".equalsIgnoreCase(sortOrder)) {
queryArgs.setSortField(sortField, DiscoverQuery.SORT_ORDER.desc);
} else {
throw new IllegalArgumentException(sortOrder + " is not a valid sort order");
}
} else {
throw new IllegalArgumentException(sortBy + " is not a valid sort field");
}
}
private boolean isConfigured(String sortBy, DiscoverySortConfiguration searchSortConfiguration) {
return Objects.nonNull(searchSortConfiguration.getSortFieldConfiguration(sortBy));
}
private String getDefaultSortDirection(DiscoverySortConfiguration searchSortConfiguration, String sortOrder) {
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();
}
return sortOrder;
}
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
String sortBy;// Attempt to find the default one, if none found we use SCORE
sortBy = "score";
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())) {
return sortBy;
}
sortBy = defaultSort.getMetadataField();
}
return sortBy;
}
private void configurePagination(Integer size, Long offset, DiscoverQuery queryArgs) {
queryArgs.setMaxResults(size != null ? Math.min(pageSizeLimit, size) : pageSizeLimit);
queryArgs.setStart(offset != null ? Math.toIntExact(offset) : 0);
}
private String getDsoType(String dsoType) throws IllegalArgumentException {
for (IndexFactory indexFactory : indexableFactories) {
if (StringUtils.equalsIgnoreCase(indexFactory.getType(), dsoType)) {
return indexFactory.getType();
}
}
throw new IllegalArgumentException(dsoType + " is not a valid DSpace Object type");
}
public void setIndexableFactories(List indexableFactories) {
this.indexableFactories = indexableFactories;
}
private DiscoverQuery addFaceting(Context context, IndexableObject scope, DiscoverQuery queryArgs,
DiscoveryConfiguration discoveryConfiguration) {
List facets = discoveryConfiguration.getSidebarFacets();
log.debug("facets for configuration " + discoveryConfiguration.getId() + ": " + (facets != null ? facets
.size() : null));
if (facets != null) {
queryArgs.setFacetMinCount(1);
/** enable faceting of search results */
for (DiscoverySearchFilterFacet facet : facets) {
fillFacetIntoQueryArgs(context, scope, null, queryArgs, facet, facet.getFacetLimit());
}
}
return queryArgs;
}
private String[] convertFiltersToString(Context context, DiscoveryConfiguration discoveryConfiguration,
List searchFilters)
throws IllegalArgumentException {
ArrayList filterQueries = new ArrayList<>(CollectionUtils.size(searchFilters));
try {
for (QueryBuilderSearchFilter searchFilter : CollectionUtils.emptyIfNull(searchFilters)) {
DiscoverySearchFilter filter = discoveryConfiguration.getSearchFilter(searchFilter.getName());
if (filter == null) {
throw new IllegalArgumentException(searchFilter.getName() + " is not a valid search filter");
}
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
filter.getIndexFieldName(),
searchFilter.getOperator(),
searchFilter.getValue(),
discoveryConfiguration);
if (filterQuery != null) {
filterQueries.add(filterQuery.getFilterQuery());
}
}
} catch (SQLException e) {
throw new IllegalArgumentException("There was a problem parsing the search filters.", e);
}
return filterQueries.toArray(new String[filterQueries.size()]);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy