/**
* Copyright 2008 The University of North Carolina at Chapel Hill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.unc.lib.dl.search.solr.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Stores properties related to searching retrieved from a properties file. Includes default values and lists of
* possible field types for validation or lookup purposes.
*
* @author bbpennel
*/
public class SearchSettings extends AbstractSettings {
private static final Logger log = LoggerFactory.getLogger(SearchSettings.class);
private Properties properties;
// Upper limit to the number of characters allowed in a single query field.
public int queryMaxLength;
// Default operator for looking up keyword terms.
public String defaultOperator;
// Set of possible boolean operators for looking up keyword terms.
public Set<String> operators;
// Default number of rows to return for a search request.
public int defaultPerPage;
// Default number of entries to return for a collection browse request.
public int defaultCollectionsPerPage;
// Default number of entries to retrieve for embedded/widget result lists
public int defaultListResultsPerPage;
// Upper limit to the number of results allowed to be returned in a single search request.
public int maxPerPage;
// Upper limit to the number of page navigation links to display at a time.
public int pagesToDisplay;
// Max number of neighbor items to display in the neighbor view
public int maxNeighborResults;
// Values for allow depths for structure browse
public int structuredDepthMax;
public int structuredDepthDefault;
// Search field parameter names as they appear in GET requests to controllers
public Map<String, String> searchFieldParams;
// Inverted field parameter names for easily getting keys by parameter name
public Map<String, String> searchFieldKeys;
// Search field display labels
public Map<String, String> searchFieldLabels;
// Fields which are allowed to be directly queried via keyword searches
public Set<String> searchableFields;
// Fields which should be searched as ranged queries.
public Set<String> rangeSearchableFields;
// Fields which are searched as date fields
public Set<String> dateSearchableFields;
// Fields which are filterable as facets
public Set<String> facetNames;
// Facets shown by default in normal search results
public Set<String> searchFacetNames;
// Fields which are filterable as facets in a collection browse
public Set<String> collectionBrowseFacetNames;
// Fields which are filterable as facets in a structure browse
public Set<String> facetNamesStructureBrowse;
// Classes for facet fields. If not specified, then it is a GenericFacet
public Map<String, Class<?>> facetClasses;
// Delimiter which separates the components of a hierarchical facet in string form (such as tier, search value).
public String facetSubfieldDelimiter;
// Delimiter string which separates tiers of a hierarchical facet from each other.
public String facetTierDelimiter;
// Default number of facets entries to return for a single facet field result set.
public int facetsPerGroup;
// Default number of facets entries to return for a single facet field result set.
public int expandedFacetsPerGroup;
// Max number of facet entries that can be returned for a single facet field result set.
public int maxFacetsPerGroup;
// Indicates whether to limit search results to only those with administrative viewing privileges
public Boolean allowPatronAccess;
// Fields which should be treated as access filters
public Set<String> accessFields;
// Access filter fields which users are allowed to filter by.
public Set<String> accessFilterableFields;
// Set of accepted resource types/content models that results can be limited to.
public Set<String> resourceTypes;
// Search manipulation related actions
public Map<String, String> actions;
// Request parameter names for parameters used to construct search states.
public Map<String, String> searchStateParams;
// Resource/content model constants
public String resourceTypeFile;
public String resourceTypeAggregate;
public String resourceTypeFolder;
public String resourceTypeCollection;
// Default set of resource types to retrieve in a search request
public List<String> defaultResourceTypes;
// Default set of resources types to retrieve in collection browse requests.
public List<String> defaultCollectionResourceTypes;
// Sort types, which are groupings of any number of field names with matching sort orders.
public Map<String, List<SortField>> sortTypes;
// Display names for sort types.
public Map<String, String> sortDisplayNames;
// Order in which sort names should be displayed to the user.
public List<String> sortDisplayOrder;
// Sort direction constants
public String sortReverse;
public String sortNormal;
public Map<String, List<String>> resultFields;
public SearchSettings() {
}
/**
* Retrieves and stores all search related constants from the provided properties file
*
* @param properties
*/
@Autowired(required = true)
public void setProperties(Properties properties) {
this.properties = properties;
facetNames = new LinkedHashSet<String>();
searchFacetNames = new LinkedHashSet<String>();
collectionBrowseFacetNames = new LinkedHashSet<String>();
facetNamesStructureBrowse = new LinkedHashSet<String>();
this.facetClasses = new HashMap<String, Class<?>>();
operators = new HashSet<String>();
searchableFields = new HashSet<String>();
rangeSearchableFields = new HashSet<String>();
searchFieldParams = new HashMap<String, String>();
searchFieldKeys = new HashMap<String,String>();
searchFieldLabels = new HashMap<String, String>();
dateSearchableFields = new HashSet<String>();
actions = new HashMap<String, String>();
searchStateParams = new HashMap<String, String>();
resourceTypes = new HashSet<String>();
defaultResourceTypes = new ArrayList<String>();
defaultCollectionResourceTypes = new ArrayList<String>();
sortTypes = new HashMap<String, List<SortField>>();
sortDisplayNames = new HashMap<String, String>();
sortDisplayOrder = new ArrayList<String>();
accessFields = new HashSet<String>();
accessFilterableFields = new HashSet<String>();
resultFields = new HashMap<String, List<String>>();
// Query validation properties
setQueryMaxLength(Integer.parseInt(properties.getProperty("search.query.maxLength", "255")));
setDefaultOperator(properties.getProperty("search.query.defaultOperator", ""));
populateCollectionFromProperty("search.query.operators", operators, properties, ",");
setDefaultPerPage(Integer.parseInt(properties.getProperty("search.results.defaultPerPage", "0")));
setDefaultCollectionsPerPage(Integer.parseInt(properties.getProperty("search.results.defaultCollectionsPerPage",
"0")));
setDefaultListResultsPerPage(Integer.parseInt(properties.getProperty("search.results.defaultListResultsPerPage",
"0")));
setMaxPerPage(Integer.parseInt(properties.getProperty("search.results.maxPerPage", "0")));
setPagesToDisplay(Integer.parseInt(properties.getProperty("search.results.pagesToDisplay", "0")));
setMaxNeighborResults(Integer.parseInt(properties.getProperty("search.results.neighborItems", "0")));
setStructuredDepthDefault(Integer.parseInt(properties.getProperty("search.structure.depth.default", "0")));
setStructuredDepthMax(Integer.parseInt(properties.getProperty("search.structure.depth.max", "0")));
setMaxNeighborResults(Integer.parseInt(properties.getProperty("search.results.neighborItems", "0")));
setMaxNeighborResults(Integer.parseInt(properties.getProperty("search.results.neighborItems", "0")));
// Facet properties
setFacetsPerGroup(Integer.parseInt(properties.getProperty("search.facet.facetsPerGroup", "0")));
setExpandedFacetsPerGroup(Integer.parseInt(properties.getProperty("search.facet.expandedFacetsPerGroup", "0")));
setMaxFacetsPerGroup(Integer.parseInt(properties.getProperty("search.facet.maxFacetsPerGroup", "0")));
populateCollectionFromProperty("search.facet.fields", facetNames, properties, ",");
populateCollectionFromProperty("search.facet.defaultSearch", searchFacetNames, properties, ",");
populateCollectionFromProperty("search.facet.defaultCollectionBrowse", collectionBrowseFacetNames, properties,
",");
populateCollectionFromProperty("search.facet.defaultStructureBrowse", facetNamesStructureBrowse, properties, ",");
try {
populateClassMapFromProperty("search.facet.class.", "edu.unc.lib.dl.search.solr.model.", this.facetClasses,
properties);
} catch (ClassNotFoundException e) {
log.error("Invalid facet class specified in search.facet.class property", e);
}
setFacetSubfieldDelimiter(properties.getProperty("search.facet.subfieldDelimiter", ""));
setFacetTierDelimiter(properties.getProperty("search.facet.tierDelimiter", ""));
// Field names
populateCollectionFromProperty("search.field.searchable", searchableFields, properties, ",");
populateCollectionFromProperty("search.field.rangeSearchable", rangeSearchableFields, properties, ",");
populateCollectionFromProperty("search.field.dateSearchable", dateSearchableFields, properties, ",");
populateMapFromProperty("search.field.paramName.", searchFieldParams, properties);
searchFieldKeys = getInvertedHashMap(searchFieldParams);
populateMapFromProperty("search.field.display.", searchFieldLabels, properties);
populateMapFromProperty("search.actions.", actions, properties);
populateMapFromProperty("search.url.param.", searchStateParams, properties);
populateListMapFromProperty("search.results.fields", resultFields, properties);
// Populate sort types
setSortReverse(properties.getProperty("search.sort.order.reverse", ""));
setSortNormal(properties.getProperty("search.sort.order.normal", ""));
populateMapFromProperty("search.sort.name.", sortDisplayNames, properties);
populateCollectionFromProperty("search.sort.displayOrder", sortDisplayOrder, properties, "\\|");
// Access field names
this.setAllowPatronAccess(new Boolean(properties.getProperty("search.access.allowPatrons", "true")));
populateCollectionFromProperty("search.access.fields", accessFields, properties, ",");
populateCollectionFromProperty("search.access.filterableFields", accessFilterableFields, properties, ",");
// Resource Types
setResourceTypeFile(properties.getProperty("search.resource.type.file", ""));
setResourceTypeAggregate(properties.getProperty("search.resource.type.aggregate", ""));
setResourceTypeFolder(properties.getProperty("search.resource.type.folder", ""));
setResourceTypeCollection(properties.getProperty("search.resource.type.collection", ""));
populateCollectionFromProperty("search.resource.types", resourceTypes, properties, ",");
populateCollectionFromProperty("search.resource.searchDefault", defaultResourceTypes, properties, ",");
populateCollectionFromProperty("search.resource.collectionDefault", defaultCollectionResourceTypes, properties,
",");
Iterator<Map.Entry<Object, Object>> propIt = properties.entrySet().iterator();
while (propIt.hasNext()) {
Map.Entry<Object, Object> propEntry = propIt.next();
String propertyKey = (String) propEntry.getKey();
// Populate sort types
if (propertyKey.indexOf("search.sort.type.") == 0) {
String sortTypes[] = ((String) propEntry.getValue()).split(",");
List<SortField> sortFields = new ArrayList<SortField>();
for (String sortField : sortTypes) {
sortFields.add(new SortField(sortField));
}
this.sortTypes.put(propertyKey.substring(propertyKey.lastIndexOf(".") + 1), sortFields);
}
}
}
public int getFacetsPerGroup() {
return facetsPerGroup;
}
public void setFacetsPerGroup(int facetsPerGroup) {
this.facetsPerGroup = facetsPerGroup;
}
public int getExpandedFacetsPerGroup() {
return expandedFacetsPerGroup;
}
public void setExpandedFacetsPerGroup(int expandedFacetsPerGroup) {
this.expandedFacetsPerGroup = expandedFacetsPerGroup;
}
public int getQueryMaxLength() {
return queryMaxLength;
}
public void setQueryMaxLength(int queryMaxLength) {
this.queryMaxLength = queryMaxLength;
}
public int getDefaultPerPage() {
return defaultPerPage;
}
public void setDefaultPerPage(int defaultPerPage) {
this.defaultPerPage = defaultPerPage;
}
public int getDefaultCollectionsPerPage() {
return defaultCollectionsPerPage;
}
public void setDefaultCollectionsPerPage(int defaultCollectionsPerPage) {
this.defaultCollectionsPerPage = defaultCollectionsPerPage;
}
public int getMaxPerPage() {
return maxPerPage;
}
public void setMaxPerPage(int maxPerPage) {
this.maxPerPage = maxPerPage;
}
public Set<String> getSearchableFields() {
return searchableFields;
}
public void setSearchableFields(Set<String> searchableFields) {
this.searchableFields = searchableFields;
}
public Set<String> getFacetNames() {
return facetNames;
}
public void setFacetNames(Set<String> facetNames) {
this.facetNames = facetNames;
}
public Set<String> getFacetNamesStructureBrowse() {
return facetNamesStructureBrowse;
}
public void setFacetNamesStructureBrowse(Set<String> facetNamesStructureBrowse) {
this.facetNamesStructureBrowse = facetNamesStructureBrowse;
}
public Map<String, List<SortField>> getSortTypes() {
return sortTypes;
}
public void setSortTypes(Map<String, List<SortField>> sortTypes) {
this.sortTypes = sortTypes;
}
public String getSortReverse() {
return sortReverse;
}
public void setSortReverse(String sortReverse) {
this.sortReverse = sortReverse;
}
public String getSortNormal() {
return sortNormal;
}
public void setSortNormal(String sortNormal) {
this.sortNormal = sortNormal;
}
public Map<String, Class<?>> getFacetClasses() {
return facetClasses;
}
public void setFacetClasses(Map<String, Class<?>> facetClasses) {
this.facetClasses = facetClasses;
}
public Set<String> getRangeSearchableFields() {
return rangeSearchableFields;
}
public void setRangeSearchableFields(Set<String> rangeSearchableFields) {
this.rangeSearchableFields = rangeSearchableFields;
}
public Boolean getAllowPatronAccess() {
return allowPatronAccess;
}
public void setAllowPatronAccess(Boolean allowPatronAccess) {
this.allowPatronAccess = allowPatronAccess;
}
public Set<String> getAccessFields() {
return accessFields;
}
public void setAccessFields(Set<String> accessFields) {
this.accessFields = accessFields;
}
public Set<String> getAccessFilterableFields() {
return accessFilterableFields;
}
public void setAccessFilterableFields(Set<String> accessFilterableFields) {
this.accessFilterableFields = accessFilterableFields;
}
public boolean isResourceTypeContainer(String resourceType) {
return (resourceTypeCollection.equals(resourceType) || resourceTypeFolder.equals(resourceType) || resourceTypeAggregate
.equals(resourceType));
}
public Set<String> getResourceTypes() {
return resourceTypes;
}
public void setResourceTypes(Set<String> resourceTypes) {
this.resourceTypes = resourceTypes;
}
public List<String> getDefaultResourceTypes() {
return defaultResourceTypes;
}
public void setDefaultResourceTypes(List<String> defaultResourceTypes) {
this.defaultResourceTypes = defaultResourceTypes;
}
public String getDefaultOperator() {
return defaultOperator;
}
public void setDefaultOperator(String defaultOperator) {
this.defaultOperator = defaultOperator;
}
public Set<String> getOperators() {
return operators;
}
public void setOperators(Set<String> operators) {
this.operators = operators;
}
public Map<String, String> getActions() {
return actions;
}
public void setActions(Map<String, String> actions) {
this.actions = actions;
}
public String actionName(String actionKey) {
return this.actions.get(actionKey);
}
public String getActionName(String actionKey) {
return this.actionName(actionKey);
}
public List<String> getDefaultCollectionResourceTypes() {
return defaultCollectionResourceTypes;
}
public void setDefaultCollectionResourceTypes(List<String> defaultCollectionResourceTypes) {
this.defaultCollectionResourceTypes = defaultCollectionResourceTypes;
}
public Map<String, String> getSearchStateParams() {
return searchStateParams;
}
public void setSearchStateParams(Map<String, String> searchStateParams) {
this.searchStateParams = searchStateParams;
}
public String searchStateParam(String key) {
return this.searchStateParams.get(key);
}
public String getSearchStateParam(String key) {
return this.searchStateParam(key);
}
public int getMaxFacetsPerGroup() {
return maxFacetsPerGroup;
}
public void setMaxFacetsPerGroup(int maxFacetsPerGroup) {
this.maxFacetsPerGroup = maxFacetsPerGroup;
}
public String getFacetSubfieldDelimiter() {
return facetSubfieldDelimiter;
}
public void setFacetSubfieldDelimiter(String facetSubfieldDelimiter) {
this.facetSubfieldDelimiter = facetSubfieldDelimiter;
}
public String getFacetTierDelimiter() {
return facetTierDelimiter;
}
public void setFacetTierDelimiter(String facetTierDelimiter) {
this.facetTierDelimiter = facetTierDelimiter;
}
/**
* Storage class for holding sort field and direction pairs.
*
* @author bbpennel
*
*/
public class SortField {
private String fieldName;
private String sortOrder;
public SortField(String sortField) {
String sortValues[] = sortField.split("\\|");
this.fieldName = sortValues[0];
this.sortOrder = sortValues[1];
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getSortOrder() {
return sortOrder;
}
public void setSortOrder(String sortOrder) {
this.sortOrder = sortOrder;
}
}
public String searchFieldParam(String key) {
return searchFieldParams.get(key);
}
public String getSearchFieldParam(String key) {
return this.searchFieldParam(key);
}
public String searchFieldKey(String name) {
return searchFieldKeys.get(name);
}
public Map<String, String> getSearchFieldParams() {
return searchFieldParams;
}
public void setSearchFieldParams(Map<String, String> searchFieldParams) {
this.searchFieldParams = searchFieldParams;
}
public Map<String, String> getSearchFieldLabels() {
return searchFieldLabels;
}
public void setSearchFieldLabels(Map<String, String> searchFieldLabels) {
this.searchFieldLabels = searchFieldLabels;
}
public int getPagesToDisplay() {
return pagesToDisplay;
}
public void setPagesToDisplay(int pagesToDisplay) {
this.pagesToDisplay = pagesToDisplay;
}
public Map<String, String> getSortDisplayNames() {
return sortDisplayNames;
}
public void setSortDisplayNames(Map<String, String> sortDisplayNames) {
this.sortDisplayNames = sortDisplayNames;
}
public List<String> getSortDisplayOrder() {
return sortDisplayOrder;
}
public void setSortDisplayOrder(List<String> sortDisplayOrder) {
this.sortDisplayOrder = sortDisplayOrder;
}
public String getResourceTypeFile() {
return resourceTypeFile;
}
public void setResourceTypeFile(String resourceTypeFile) {
this.resourceTypeFile = resourceTypeFile;
}
public String getResourceTypeAggregate() {
return resourceTypeAggregate;
}
public void setResourceTypeAggregate(String resourceTypeAggregate) {
this.resourceTypeAggregate = resourceTypeAggregate;
}
public String getResourceTypeFolder() {
return resourceTypeFolder;
}
public void setResourceTypeFolder(String resourceTypeFolder) {
this.resourceTypeFolder = resourceTypeFolder;
}
public String getResourceTypeCollection() {
return resourceTypeCollection;
}
public void setResourceTypeCollection(String resourceTypeCollection) {
this.resourceTypeCollection = resourceTypeCollection;
}
public Set<String> getCollectionBrowseFacetNames() {
return collectionBrowseFacetNames;
}
public void setCollectionBrowseFacetNames(Set<String> collectionBrowseFacetNames) {
this.collectionBrowseFacetNames = collectionBrowseFacetNames;
}
public int getDefaultListResultsPerPage() {
return defaultListResultsPerPage;
}
public void setDefaultListResultsPerPage(int defaultListResultsPerPage) {
this.defaultListResultsPerPage = defaultListResultsPerPage;
}
public Set<String> getDateSearchableFields() {
return dateSearchableFields;
}
public void setDateSearchableFields(Set<String> dateSearchableFields) {
this.dateSearchableFields = dateSearchableFields;
}
public int getMaxNeighborResults() {
return maxNeighborResults;
}
public void setMaxNeighborResults(int maxNeighborResults) {
this.maxNeighborResults = maxNeighborResults;
}
public int getStructuredDepthMax() {
return structuredDepthMax;
}
public void setStructuredDepthMax(int structuredDepthMax) {
this.structuredDepthMax = structuredDepthMax;
}
public int getStructuredDepthDefault() {
return structuredDepthDefault;
}
public void setStructuredDepthDefault(int structuredDepthDefault) {
this.structuredDepthDefault = structuredDepthDefault;
}
public String getProperty(String key) {
return this.properties.getProperty(key);
}
@Override
public String toString() {
return "SearchSettings [queryMaxLength=" + queryMaxLength + ", defaultOperator=" + defaultOperator
+ ", operators=" + operators + ", defaultPerPage=" + defaultPerPage + ", defaultCollectionsPerPage="
+ defaultCollectionsPerPage + ", defaultListResultsPerPage=" + defaultListResultsPerPage + ", maxPerPage="
+ maxPerPage + ", pagesToDisplay=" + pagesToDisplay + ", maxNeighborResults=" + maxNeighborResults
+ ", searchFieldParams=" + searchFieldParams + ", searchFieldLabels=" + searchFieldLabels
+ ", searchableFields=" + searchableFields + ", rangeSearchableFields=" + rangeSearchableFields
+ ", dateSearchableFields=" + dateSearchableFields + ", facetNames=" + facetNames
+ ", collectionBrowseFacetNames=" + collectionBrowseFacetNames + ", facetSubfieldDelimiter="
+ facetSubfieldDelimiter + ", facetTierDelimiter=" + facetTierDelimiter + ", facetsPerGroup="
+ facetsPerGroup + ", maxFacetsPerGroup=" + maxFacetsPerGroup + ", accessFields=" + accessFields
+ ", accessFilterableFields=" + accessFilterableFields + ", resourceTypes=" + resourceTypes + ", actions="
+ actions + ", searchStateParams=" + searchStateParams + ", resourceTypeFile=" + resourceTypeFile
+ ", resourceTypeFolder=" + resourceTypeFolder + ", resourceTypeCollection=" + resourceTypeCollection
+ ", defaultResourceTypes=" + defaultResourceTypes + ", defaultCollectionResourceTypes="
+ defaultCollectionResourceTypes + ", sortTypes=" + sortTypes + ", sortDisplayNames=" + sortDisplayNames
+ ", sortDisplayOrder=" + sortDisplayOrder + ", sortReverse=" + sortReverse + ", sortNormal=" + sortNormal
+ "]";
}
}