diff --git a/config/alfresco/model/solrFacetModel.xml b/config/alfresco/model/solrFacetModel.xml index 17d8bea4b9..35da06a990 100644 --- a/config/alfresco/model/solrFacetModel.xml +++ b/config/alfresco/model/solrFacetModel.xml @@ -119,16 +119,6 @@ false - - Index - d:int - true - - false - false - false - - Is enabled d:boolean diff --git a/config/alfresco/subsystems/Search/solr/facet/solr-facets-config.properties b/config/alfresco/subsystems/Search/solr/facet/solr-facets-config.properties index 050a1dcd5e..fabed8fbb6 100644 --- a/config/alfresco/subsystems/Search/solr/facet/solr-facets-config.properties +++ b/config/alfresco/subsystems/Search/solr/facet/solr-facets-config.properties @@ -1,6 +1,6 @@ # # Alfresco default facets -# Note: If you have changed the filter’s default value(s) via Share, then any subsequent changes of the default values won’t be applied to the filter on server startup. +# Note: If you have changed the filter's™ default value(s) via Share, then any subsequent changes of those default values won't be applied to the filter on server startup. # # Field-Facet-Qname => cm:content.mimetype @@ -11,9 +11,8 @@ default.cm\:content.mimetype.maxFilters=5 default.cm\:content.mimetype.hitThreshold=1 default.cm\:content.mimetype.minFilterValueLength=4 default.cm\:content.mimetype.sortBy=DESCENDING -default.cm\:content.mimetype.scope=SCOPED_SITES +default.cm\:content.mimetype.scope=ALL default.cm\:content.mimetype.scopedSites= -default.cm\:content.mimetype.index=0 default.cm\:content.mimetype.isEnabled=true # Field-Facet-Qname => cm:description.__ @@ -24,10 +23,9 @@ default.cm\:description.__.maxFilters=5 default.cm\:description.__.hitThreshold=1 default.cm\:description.__.minFilterValueLength=4 default.cm\:description.__.sortBy=DESCENDING -default.cm\:description.__.scope=SCOPED_SITES +default.cm\:description.__.scope=ALL default.cm\:description.__.scopedSites= -default.cm\:description.__.index=1 -default.cm\:description.__.isEnabled=true +default.cm\:description.__.isEnabled=false # Field-Facet-Qname => cm:creator.__.u default.cm\:creator.__.u.filterID=filter_creator @@ -37,9 +35,8 @@ default.cm\:creator.__.u.maxFilters=5 default.cm\:creator.__.u.hitThreshold=1 default.cm\:creator.__.u.minFilterValueLength=4 default.cm\:creator.__.u.sortBy=ALPHABETICALLY -default.cm\:creator.__.u.scope=SCOPED_SITES +default.cm\:creator.__.u.scope=ALL default.cm\:creator.__.u.scopedSites= -default.cm\:creator.__.u.index=2 default.cm\:creator.__.u.isEnabled=true # Field-Facet-Qname => cm:modifier.__.u @@ -50,9 +47,8 @@ default.cm\:modifier.__.u.maxFilters=5 default.cm\:modifier.__.u.hitThreshold=1 default.cm\:modifier.__.u.minFilterValueLength=4 default.cm\:modifier.__.u.sortBy=ALPHABETICALLY -default.cm\:modifier.__.u.scope=SCOPED_SITES +default.cm\:modifier.__.u.scope=ALL default.cm\:modifier.__.u.scopedSites= -default.cm\:modifier.__.u.index=3 default.cm\:modifier.__.u.isEnabled=true # Field-Facet-Qname => cm:created @@ -63,9 +59,8 @@ default.cm\:created.maxFilters=5 default.cm\:created.hitThreshold=1 default.cm\:created.minFilterValueLength=4 default.cm\:created.sortBy=ALPHABETICALLY -default.cm\:created.scope=SCOPED_SITES +default.cm\:created.scope=ALL default.cm\:created.scopedSites= -default.cm\:created.index=4 default.cm\:created.isEnabled=true default.cm\:created.EXTRA-PROP.blockIncludeFacetRequest=true @@ -77,9 +72,8 @@ default.cm\:modified.maxFilters=5 default.cm\:modified.hitThreshold=1 default.cm\:modified.minFilterValueLength=4 default.cm\:modified.sortBy=ALPHABETICALLY -default.cm\:modified.scope=SCOPED_SITES +default.cm\:modified.scope=ALL default.cm\:modified.scopedSites= -default.cm\:modified.index=5 default.cm\:modified.isEnabled=true default.cm\:modified.EXTRA-PROP.blockIncludeFacetRequest=true @@ -91,8 +85,7 @@ default.cm\:content.size.maxFilters=5 default.cm\:content.size.hitThreshold=1 default.cm\:content.size.minFilterValueLength=4 default.cm\:content.size.sortBy=ALPHABETICALLY -default.cm\:content.size.scope=SCOPED_SITES +default.cm\:content.size.scope=ALL default.cm\:content.size.scopedSites= -default.cm\:content.size.index=6 default.cm\:content.size.isEnabled=true default.cm\:content.size.EXTRA-PROP.blockIncludeFacetRequest=true diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparator.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparator.java index 55f12337cd..5592bfb256 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparator.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparator.java @@ -38,6 +38,11 @@ public class SolrFacetComparator implements Comparator @Override public int compare(SolrFacetProperties facet1, SolrFacetProperties facet2) { + if (sortedIDs.isEmpty()) + { + return facet1.getFilterID().compareTo(facet2.getFilterID()); + } + Pair facetIndicesInSortedList = find(facet1, facet2); if (bothSorted(facetIndicesInSortedList)) @@ -47,19 +52,7 @@ public class SolrFacetComparator implements Comparator } else if (neitherSorted(facetIndicesInSortedList)) { - // Sorting is by the index value defined in the facet itself. - final int indexDifference = facet1.getIndex() - facet2.getIndex(); - - if (indexDifference == 0) - { - // This could happen if an end user defines/overrides indexes to be equal. - // We'll sort based on facet ID if it does happen. - return facet1.getFilterID().compareTo(facet2.getFilterID()); - } - else - { - return indexDifference; - } + return facet1.getFilterID().compareTo(facet2.getFilterID()); } else { diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfig.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfig.java index 73ad83c73c..1e196c2911 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfig.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfig.java @@ -39,6 +39,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; +import org.springframework.extensions.surf.util.ParameterCheck; /** * This class picks up all the loaded properties passed to it and uses a naming @@ -58,7 +59,6 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; *
  • custom.cm\:content.mimetype.sortBy=DESCENDING
  • *
  • custom.cm\:content.mimetype.scope=SCOPED_SITES
  • *
  • custom.cm\:content.mimetype.scopedSites=site1,site2,site3
  • - *
  • custom.cm\:content.mimetype.index=0
  • *
  • custom.cm\:content.mimetype.isEnabled=true
  • * * Also, if there is a need to add additional properties, the following needs to be @@ -91,8 +91,8 @@ public class SolrFacetConfig extends AbstractLifecycleBean public SolrFacetConfig(Properties rawProperties, String inheritanceOrder) { - PropertyCheck.mandatory(this, "rawProperties", rawProperties); - PropertyCheck.mandatory(this, "inheritanceOrder", inheritanceOrder); + ParameterCheck.mandatory("rawProperties", rawProperties); + ParameterCheck.mandatory("inheritanceOrder", inheritanceOrder); this.rawProperties = rawProperties; @@ -223,12 +223,7 @@ public class SolrFacetConfig extends AbstractLifecycleBean String sortBy = propValues.get(ValueName.PROP_SORTBY.getPropValueName(field)); String scope = propValues.get(ValueName.PROP_SCOPE.getPropValueName(field)); Set scopedSites = getScopedSites(propValues.get(ValueName.PROP_SCOPED_SITES.getPropValueName(field))); - int index = getIntegerValue(propValues.get(ValueName.PROP_INDEX.getPropValueName(field))); - if(index < 0) - { - throw new SolrFacetConfigException("Index must be greater than or equal to 0"); - } - boolean isEnabled = Boolean.valueOf(propValues.get(ValueName.PROP_IS_ENABLED.getPropValueName(field))); + Boolean isEnabled = Boolean.valueOf(propValues.get(ValueName.PROP_IS_ENABLED.getPropValueName(field))); Set customProps = getCustomProps(facetFields.get(field), field, propValues); // Construct the FacetProperty object @@ -242,14 +237,13 @@ public class SolrFacetConfig extends AbstractLifecycleBean .minFilterValueLength(minFilterValueLength) .sortBy(sortBy) .scope(scope) - .index(index) .isEnabled(isEnabled) .isDefault(true) .scopedSites(scopedSites) .customProperties(customProps).build(); facetProperties.put(filterID, fp); - } + } // Done return facetProperties; @@ -273,7 +267,7 @@ public class SolrFacetConfig extends AbstractLifecycleBean private static void getPropertyAndPropOderControl(Properties properties, Set propNames, Set propOrderControl) { - + for (Object propKeyObj : properties.keySet()) { String propKey = (String) propKeyObj; @@ -327,14 +321,14 @@ public class SolrFacetConfig extends AbstractLifecycleBean } return set; } - + private static Set getCustomProps(Set additionalProps, String field, Map propValues) { if (additionalProps == null) { return Collections.emptySet(); } - + Set customProps = new HashSet<>(); for (String extraInfo : additionalProps) { @@ -385,7 +379,7 @@ public class SolrFacetConfig extends AbstractLifecycleBean { PROP_FILTER_ID("filterID"), PROP_DISPLAY_NAME("displayName"), PROP_MAX_FILTERS("maxFilters"), PROP_HIT_THRESHOLD("hitThreshold"), PROP_MIN_FILTER_VALUE_LENGTH("minFilterValueLength"), PROP_SORTBY("sortBy"), PROP_SCOPE("scope"), PROP_SCOPED_SITES("scopedSites"), - PROP_INDEX("index"), PROP_IS_ENABLED("isEnabled"), PROP_DISPLAY_CONTROL("displayControl"); + PROP_IS_ENABLED("isEnabled"), PROP_DISPLAY_CONTROL("displayControl"); private ValueName(String propValueName) { diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetModel.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetModel.java index 68645ecf31..8248ad1694 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetModel.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetModel.java @@ -56,16 +56,14 @@ public interface SolrFacetModel public static final QName PROP_SCOPED_SITES = QName.createQName(SOLR_FACET_MODEL_URL, "scopedSites"); - public static final QName PROP_INDEX = QName.createQName(SOLR_FACET_MODEL_URL, "index"); - public static final QName PROP_IS_ENABLED = QName.createQName(SOLR_FACET_MODEL_URL, "isEnabled"); public static final QName PROP_IS_DEFAULT = QName.createQName(SOLR_FACET_MODEL_URL, "isDefault"); public static final QName PROP_EXTRA_INFORMATION = QName.createQName(SOLR_FACET_CUSTOM_PROPERTY_URL, "extraInformation"); - + /** The type of the facet container folder. */ public static final QName TYPE_FACETS = QName.createQName(SOLR_FACET_MODEL_URL, "facets"); - + public static final QName PROP_FACET_ORDER = QName.createQName(SOLR_FACET_MODEL_URL, "facetOrder"); } diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetProperties.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetProperties.java index 49938973c6..ffadae08b7 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetProperties.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetProperties.java @@ -46,8 +46,7 @@ public class SolrFacetProperties implements Serializable private final String sortBy; private final String scope; private final Set scopedSites; - private final int index; - private final boolean isEnabled; + private final Boolean isEnabled; private final boolean isDefault; // is loaded from properties files? private final Set customProperties; @@ -67,7 +66,6 @@ public class SolrFacetProperties implements Serializable this.minFilterValueLength = builder.minFilterValueLength; this.sortBy = builder.sortBy; this.scope = builder.scope; - this.index = builder.index; this.isEnabled = builder.isEnabled; this.isDefault = builder.isDefault; this.scopedSites = Collections.unmodifiableSet(new HashSet(builder.scopedSites)); @@ -157,17 +155,9 @@ public class SolrFacetProperties implements Serializable } /** - * @return the index + * @return null if the value is not set */ - public int getIndex() - { - return this.index; - } - - /** - * @return the isEnabled - */ - public boolean isEnabled() + public Boolean isEnabled() { return this.isEnabled; } @@ -191,7 +181,6 @@ public class SolrFacetProperties implements Serializable { return Collections.unmodifiableSet(new HashSet(this.customProperties)); } - /* * @see java.lang.Object#hashCode() @@ -251,7 +240,7 @@ public class SolrFacetProperties implements Serializable .append(this.maxFilters).append(", hitThreshold=").append(this.hitThreshold) .append(", minFilterValueLength=").append(this.minFilterValueLength).append(", sortBy=") .append(this.sortBy).append(", scope=").append(this.scope).append(", scopedSites=") - .append(this.scopedSites).append(", index=").append(this.index).append(", isEnabled=").append(this.isEnabled) + .append(this.scopedSites).append(", isEnabled=").append(this.isEnabled) .append(", isDefault=").append(this.isDefault).append(", customProperties=").append(this.customProperties) .append("]"); return sb.toString(); @@ -263,14 +252,13 @@ public class SolrFacetProperties implements Serializable private QName facetQName; private String displayName; private String displayControl; - private int maxFilters; - private int hitThreshold; - private int minFilterValueLength; + private int maxFilters = -1; + private int hitThreshold = -1;; + private int minFilterValueLength = -1; private String sortBy; private String scope; private Set scopedSites = Collections.emptySet(); - private int index; - private boolean isEnabled; + private Boolean isEnabled; private boolean isDefault; private Set customProperties = Collections.emptySet(); @@ -295,7 +283,6 @@ public class SolrFacetProperties implements Serializable this.sortBy = that.sortBy; this.scope = that.scope; this.scopedSites = that.scopedSites; - this.index = that.index; this.isEnabled = that.isEnabled; this.isDefault = that.isDefault; this.customProperties = that.customProperties; @@ -364,13 +351,7 @@ public class SolrFacetProperties implements Serializable return this; } - public Builder index(int index) - { - this.index = index; - return this; - } - - public Builder isEnabled(boolean isEnabled) + public Builder isEnabled(Boolean isEnabled) { this.isEnabled = isEnabled; return this; @@ -396,7 +377,7 @@ public class SolrFacetProperties implements Serializable return new SolrFacetProperties(this); } } - + public static class CustomProperties implements Serializable { private static final long serialVersionUID = 2250062300454166258L; diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetService.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetService.java index f4a90f179e..2d61b5980e 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetService.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetService.java @@ -20,17 +20,13 @@ package org.alfresco.repo.search.impl.solr.facet; import java.util.List; - -import org.alfresco.repo.search.impl.solr.facet.Exceptions.DuplicateFacetId; -import org.alfresco.repo.search.impl.solr.facet.Exceptions.IllegalArgument; -import org.alfresco.repo.search.impl.solr.facet.Exceptions.MissingFacetId; -import org.alfresco.repo.search.impl.solr.facet.Exceptions.UnrecognisedFacetId; import org.alfresco.service.cmr.repository.NodeRef; /** * Solr Facet service configuration API. * * @author Jamal Kaabi-Mofrad + * @since 5.0 */ public interface SolrFacetService { @@ -41,7 +37,7 @@ public interface SolrFacetService * @return List of {@code SolrFacetProperties} or an empty list if none exists */ public List getFacets(); - + /** * Gets the facet by filter Id. * @@ -90,8 +86,6 @@ public interface SolrFacetService */ public void deleteFacet(String filterID); - public int getNextIndex(); - /** * Reorders existing facets to the provided order. * diff --git a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetServiceImpl.java b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetServiceImpl.java index fc15732eac..0d0ae756c4 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetServiceImpl.java +++ b/source/java/org/alfresco/repo/search/impl/solr/facet/SolrFacetServiceImpl.java @@ -21,27 +21,26 @@ package org.alfresco.repo.search.impl.solr.facet; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.NavigableMap; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; -import org.alfresco.repo.node.NodeServicePolicies.BeforeUpdateNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; -import org.alfresco.repo.node.NodeServicePolicies.OnUpdateNodePolicy; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -71,13 +70,14 @@ import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; /** + * Solr Facet Service Implementation. + * * @author Jamal Kaabi-Mofrad + * @since 5.0 */ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrFacetService, NodeServicePolicies.OnCreateNodePolicy, - NodeServicePolicies.OnUpdateNodePolicy, - NodeServicePolicies.BeforeDeleteNodePolicy, - NodeServicePolicies.BeforeUpdateNodePolicy + NodeServicePolicies.BeforeDeleteNodePolicy { private static final Log logger = LogFactory.getLog(SolrFacetServiceImpl.class); /** @@ -102,8 +102,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF private SimpleCache singletonCache; // eg. for facetsHomeNodeRef private final String KEY_FACETS_HOME_NODEREF = "key.facetshome.noderef"; private SimpleCache facetNodeRefCache; // for filterID to nodeRef lookup - private NavigableMap facetsMap = new ConcurrentSkipListMap<>(); // TODO - private int maxAllowedFilters = 100; + private ConcurrentMap defaultFacetsMap = new ConcurrentHashMap(10); /** * @param authorityService the authorityService to set @@ -193,14 +192,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF this.facetNodeRefCache = facetNodeRefCache; } - /** - * @param maxAllowedFilters the maxAllowedFilters to set - */ - public void setMaxAllowedFilters(int maxAllowedFilters) - { - this.maxAllowedFilters = maxAllowedFilters; - } - @Override public boolean isSearchAdmin(String userName) { @@ -217,17 +208,24 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF { // Sort the facets into display order final SolrFacetComparator comparator = new SolrFacetComparator(getFacetOrder()); - + SortedSet result = new TreeSet<>(comparator); - result.addAll(facetsMap.values()); - + List children = nodeService.getChildAssocs(getFacetsRoot()); + + for (ChildAssociationRef ref : children) + { + result.add(getFacetProperties(ref.getChildRef())); + } + // add the default filters + result.addAll(defaultFacetsMap.values()); + return new ArrayList<>(result); } - + public List getFacetOrder() { final NodeRef facetContainer = getFacetsRoot(); - + @SuppressWarnings("unchecked") final List facetOrder = (List) nodeService.getProperty(facetContainer, SolrFacetModel.PROP_FACET_ORDER); return facetOrder; @@ -242,12 +240,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF * the nodeService. */ NodeRef nodeRef = getFacetNodeRef(filterID); - return (nodeRef == null) ? getDefaultLoadedFacet(filterID) : getFacetProperties(nodeRef); - } - - private SolrFacetProperties getDefaultLoadedFacet(String filterID) - { - return facetConfig.getDefaultFacets().get(filterID); + return (nodeRef == null) ? defaultFacetsMap.get(filterID) : getFacetProperties(nodeRef); } @Override @@ -297,26 +290,40 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF } String filterID = (String) properties.get(ContentModel.PROP_NAME); - QName fieldQName = (QName) properties.get(SolrFacetModel.PROP_FIELD_TYPE); - String displayName = (String) properties.get(SolrFacetModel.PROP_FIELD_LABEL); - String displayControl = (String) properties.get(SolrFacetModel.PROP_DISPLAY_CONTROL); - int maxFilters = (Integer) properties.get(SolrFacetModel.PROP_MAX_FILTERS); - int hitThreshold = (Integer) properties.get(SolrFacetModel.PROP_HIT_THRESHOLD); - int minFilterValueLength = (Integer) properties.get(SolrFacetModel.PROP_MIN_FILTER_VALUE_LENGTH); - String sortBy = (String) properties.get(SolrFacetModel.PROP_SORT_BY); - String scope = (String) properties.get(SolrFacetModel.PROP_SCOPE); - int index = (Integer) properties.get(SolrFacetModel.PROP_INDEX); - boolean isEnabled = (Boolean) properties.get(SolrFacetModel.PROP_IS_ENABLED); boolean isDefault = (Boolean) properties.get(SolrFacetModel.PROP_IS_DEFAULT); + + SolrFacetProperties defaultFacet = defaultFacetsMap.get(filterID); + if(defaultFacet == null) + { + defaultFacet = new SolrFacetProperties.Builder().build(); + } + + QName fieldQName = getDefaultIfNull(defaultFacet.getFacetQName(), (QName) properties.get(SolrFacetModel.PROP_FIELD_TYPE)); + String displayName = getDefaultIfNull(defaultFacet.getDisplayName(), (String) properties.get(SolrFacetModel.PROP_FIELD_LABEL)); + String displayControl = getDefaultIfNull(defaultFacet.getDisplayControl(), (String) properties.get(SolrFacetModel.PROP_DISPLAY_CONTROL)); + int maxFilters = getDefaultIfNull(defaultFacet.getMaxFilters(), (Integer) properties.get(SolrFacetModel.PROP_MAX_FILTERS)); + int hitThreshold = getDefaultIfNull(defaultFacet.getHitThreshold(), (Integer) properties.get(SolrFacetModel.PROP_HIT_THRESHOLD)); + int minFilterValueLength = getDefaultIfNull(defaultFacet.getMinFilterValueLength(), (Integer) properties.get(SolrFacetModel.PROP_MIN_FILTER_VALUE_LENGTH)); + String sortBy = getDefaultIfNull(defaultFacet.getSortBy(), (String) properties.get(SolrFacetModel.PROP_SORT_BY)); + String scope = getDefaultIfNull(defaultFacet.getScope(), (String) properties.get(SolrFacetModel.PROP_SCOPE)); + Boolean isEnabled = getDefaultIfNull(defaultFacet.isEnabled(), (Boolean) properties.get(SolrFacetModel.PROP_IS_ENABLED)); @SuppressWarnings("unchecked") List scSites = (List) properties.get(SolrFacetModel.PROP_SCOPED_SITES); - Set scopedSites = (scSites == null) ? null : new HashSet<>(scSites); + Set scopedSites = getDefaultIfNull(defaultFacet.getScopedSites(), (scSites == null) ? null : new HashSet<>(scSites)); + Set extraProps = null; Map customProperties = getFacetCustomProperties(properties); - Set extraProps = new HashSet<>(customProperties.size()); - for(Entry cp : customProperties.entrySet()) + if (customProperties.isEmpty()) { - extraProps.add(new CustomProperties(cp.getKey(), (String) properties.get(ContentModel.PROP_TITLE), null, cp.getValue())); + extraProps = defaultFacet.getCustomProperties(); + } + else + { + extraProps = new HashSet<>(customProperties.size()); + for (Entry cp : customProperties.entrySet()) + { + extraProps.add(new CustomProperties(cp.getKey(), (String) properties.get(ContentModel.PROP_TITLE), null, cp.getValue())); + } } // Construct the FacetProperty object SolrFacetProperties fp = new SolrFacetProperties.Builder() @@ -329,7 +336,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF .minFilterValueLength(minFilterValueLength) .sortBy(sortBy) .scope(scope) - .index(index) .isEnabled(isEnabled) .isDefault(isDefault) .scopedSites(scopedSites) @@ -338,6 +344,11 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF return fp; } + private T getDefaultIfNull(T defaultValue, T newValue) + { + return (newValue == null) ? defaultValue : newValue; + } + @Override public NodeRef createFacetNode(SolrFacetProperties facetProperties) { @@ -346,12 +357,11 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF private NodeRef createFacetNodeImpl(final SolrFacetProperties facetProperties, boolean checkDefaultFP) { - final String filterID = facetProperties.getFilterID(); NodeRef facetNodeRef = getFacetNodeRef(filterID); // We need to check the bootstrapped Facet properties (i.e loaded from properties file(s)) as well, // in order to not allow the user to create a new facet with the same filterID as the bootstrapped FP. - if (facetNodeRef != null || (checkDefaultFP && getDefaultLoadedFacet(filterID) != null)) + if (facetNodeRef != null || (checkDefaultFP && defaultFacetsMap.get(filterID) != null)) { throw new SolrFacetConfigException("Unable to create facet because the filterID [" + filterID + "] is already in use."); } @@ -406,7 +416,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF NodeRef facetNodeRef = getFacetNodeRef(filterID); if (facetNodeRef == null) { - SolrFacetProperties fp = getDefaultLoadedFacet(filterID); + SolrFacetProperties fp = defaultFacetsMap.get(filterID); if (fp != null) { // As we don't create nodes for the bootstrapped FP on server @@ -436,6 +446,51 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF } } + private SolrFacetProperties makeValidFacetPropObj(SolrFacetProperties newFP) + { + + SolrFacetProperties bootstraptedFP = defaultFacetsMap.get(newFP.getFilterID()); + // null means there is no default facet + if(bootstraptedFP == null) + { + return new SolrFacetProperties.Builder(newFP).isDefault(false).build(); + } + + QName fieldQName = getValue(bootstraptedFP.getFacetQName(), newFP.getFacetQName(), null); + String displayName = getValue(bootstraptedFP.getDisplayName(), newFP.getDisplayName(), null); + String displayControl = getValue(bootstraptedFP.getDisplayControl(), newFP.getDisplayControl(), null); + int maxFilters = getValue(bootstraptedFP.getMaxFilters(), newFP.getMaxFilters(), -1); + int hitThreshold = getValue(bootstraptedFP.getHitThreshold(), newFP.getHitThreshold(), -1); + int minFilterValueLength = getValue(bootstraptedFP.getMinFilterValueLength(), newFP.getMinFilterValueLength(), -1); + String sortBy = getValue(bootstraptedFP.getSortBy(), newFP.getSortBy(), null); + String scope = getValue(bootstraptedFP.getScope(), newFP.getScope(), null); + Boolean isEnabled = getValue(bootstraptedFP.isEnabled(), newFP.isEnabled(), null); + Set scopedSites = getValue(bootstraptedFP.getScopedSites(), newFP.getScopedSites(), null); + Set extraProps = getValue(bootstraptedFP.getCustomProperties(), newFP.getCustomProperties(), null); + // Construct the FacetProperty object + SolrFacetProperties fp = new SolrFacetProperties.Builder() + .filterID(newFP.getFilterID()) + .facetQName(fieldQName) + .displayName(displayName) + .displayControl(displayControl) + .maxFilters(maxFilters) + .hitThreshold(hitThreshold) + .minFilterValueLength(minFilterValueLength) + .sortBy(sortBy) + .scope(scope) + .isEnabled(isEnabled) + .isDefault(true) + .scopedSites(scopedSites) + .customProperties(extraProps).build(); + + return fp; + } + + private T getValue(T originalValue, T newValue, T defaultValueIfEquals) + { + return (originalValue.equals(newValue) ? defaultValueIfEquals : newValue); + } + @Override public void deleteFacet(String filterID) { @@ -445,7 +500,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF throw new SolrFacetConfigException("The [" + filterID + "] facet cannot be found."); } - SolrFacetProperties defaultFP = getDefaultLoadedFacet(filterID); + SolrFacetProperties defaultFP = defaultFacetsMap.get(filterID); if (defaultFP != null) { throw new SolrFacetConfigException("The default [" + filterID + "] facet cannot be deleted. It can only be disabled."); @@ -464,33 +519,51 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF throw new SolrFacetConfigException("Filter Id cannot be null."); } + // construct a valid facet property object + facetProperties = makeValidFacetPropObj(facetProperties); Set customProperties = facetProperties.getCustomProperties(); - Map properties = new HashMap(14 + customProperties.size()); + Map properties = new HashMap(13 + customProperties.size()); properties.put(ContentModel.PROP_NAME, facetProperties.getFilterID()); - properties.put(SolrFacetModel.PROP_FIELD_TYPE, facetProperties.getFacetQName()); - properties.put(SolrFacetModel.PROP_FIELD_LABEL, facetProperties.getDisplayName()); - properties.put(SolrFacetModel.PROP_DISPLAY_CONTROL, facetProperties.getDisplayControl()); - properties.put(SolrFacetModel.PROP_MAX_FILTERS, facetProperties.getMaxFilters()); - properties.put(SolrFacetModel.PROP_HIT_THRESHOLD, facetProperties.getHitThreshold()); - properties.put(SolrFacetModel.PROP_MIN_FILTER_VALUE_LENGTH, facetProperties.getMinFilterValueLength()); - properties.put(SolrFacetModel.PROP_SCOPE, facetProperties.getScope()); - properties.put(SolrFacetModel.PROP_SORT_BY, facetProperties.getSortBy()); - properties.put(SolrFacetModel.PROP_SCOPED_SITES, (Serializable) facetProperties.getScopedSites()); - properties.put(SolrFacetModel.PROP_INDEX, facetProperties.getIndex()); - properties.put(SolrFacetModel.PROP_IS_ENABLED, facetProperties.isEnabled()); + properties.put(SolrFacetModel.PROP_IS_DEFAULT, facetProperties.isDefault()); - SolrFacetProperties fp = getDefaultLoadedFacet(facetProperties.getFilterID()); - properties.put(SolrFacetModel.PROP_IS_DEFAULT, (fp == null) ? false : fp.isDefault()); + addNodeProperty(properties, SolrFacetModel.PROP_FIELD_TYPE, facetProperties.getFacetQName()); + addNodeProperty(properties, SolrFacetModel.PROP_FIELD_LABEL, facetProperties.getDisplayName()); + addNodeProperty(properties, SolrFacetModel.PROP_DISPLAY_CONTROL, facetProperties.getDisplayControl()); + addNodeProperty(properties, SolrFacetModel.PROP_MAX_FILTERS, facetProperties.getMaxFilters()); + addNodeProperty(properties, SolrFacetModel.PROP_HIT_THRESHOLD, facetProperties.getHitThreshold()); + addNodeProperty(properties, SolrFacetModel.PROP_MIN_FILTER_VALUE_LENGTH, facetProperties.getMinFilterValueLength()); + addNodeProperty(properties, SolrFacetModel.PROP_SCOPE, facetProperties.getScope()); + addNodeProperty(properties, SolrFacetModel.PROP_SORT_BY, facetProperties.getSortBy()); + addNodeProperty(properties, SolrFacetModel.PROP_SCOPED_SITES, (Serializable) facetProperties.getScopedSites()); + addNodeProperty(properties, SolrFacetModel.PROP_IS_ENABLED, facetProperties.isEnabled()); for (CustomProperties cp : customProperties) { - properties.put(cp.getName(), cp.getValue()); + addNodeProperty(properties, cp.getName(), cp.getValue()); } return properties; } + private void addNodeProperty(Map properties, QName qname, Serializable propValue) + { + if (propValue == null) + { + return; + } + if (propValue instanceof Integer && ((Integer) propValue) < 0) + { + return; + } + if (propValue instanceof Collection && ((Collection) propValue).isEmpty()) + { + return; + } + + properties.put(qname, propValue); + } + public NodeRef getFacetsRoot() { NodeRef facetHomeRef = (NodeRef) singletonCache.get(KEY_FACETS_HOME_NODEREF); @@ -535,18 +608,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF SolrFacetModel.TYPE_FACET_FIELD, new JavaBehaviour(this, "onCreateNode")); - // Filter before update - this.policyComponent.bindClassBehaviour( - BeforeUpdateNodePolicy.QNAME, - SolrFacetModel.TYPE_FACET_FIELD, - new JavaBehaviour(this, "beforeUpdateNode")); - - // Filter update - this.policyComponent.bindClassBehaviour( - OnUpdateNodePolicy.QNAME, - SolrFacetModel.TYPE_FACET_FIELD, - new JavaBehaviour(this, "onUpdateNode")); - // Filter before deletion this.policyComponent.bindClassBehaviour( BeforeDeleteNodePolicy.QNAME, @@ -556,6 +617,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF Map mergedMap = new HashMap<>(100); // Loaded facets Map defaultFP = facetConfig.getDefaultFacets(); + defaultFacetsMap.putAll(defaultFP); // add the default facets to a ConcurrentHashMap for performance reasons mergedMap.putAll(defaultFP); // Persisted facets @@ -563,51 +625,43 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF // The persisted facets will override the default facets mergedMap.putAll(persistedProperties); + List facetOrder = getFacetOrder(); // Sort the merged maps - Comparator> entryComparator = CollectionUtils.toEntryComparator(new SolrFacetComparator(getFacetOrder())); + Comparator> entryComparator = CollectionUtils.toEntryComparator(new SolrFacetComparator(facetOrder)); Map sortedMap = CollectionUtils.sortMapByValue(mergedMap, entryComparator); - LinkedList orderedFacets = new LinkedList<>(sortedMap.values()); - // Get the last index, as the map is sorted by the FP's index value - int maxIndex = orderedFacets.getLast().getIndex(); - int previousIndex = -1; - SolrFacetProperties previousFP = null; - for (SolrFacetProperties facet : orderedFacets) - { - String filterID = facet.getFilterID(); - int index = facet.getIndex(); - if (index == previousIndex) - { - // we can be sure that previousFP is never null, as we don't - // allow the index to be -1; - if (defaultFP.get(previousFP.getFilterID()) != null && persistedProperties.get(filterID) != null) - { - SolrFacetProperties updatedPreviousFacet = new SolrFacetProperties.Builder(previousFP).index(++maxIndex).build(); - mergedMap.put(previousFP.getFilterID(), updatedPreviousFacet); - mergedMap.put(filterID, facet); - } - else - { - SolrFacetProperties updatedCurrentFacet = new SolrFacetProperties.Builder(facet).index(++maxIndex).build(); - mergedMap.put(updatedCurrentFacet.getFilterID(), updatedCurrentFacet); - } - } - else - { - mergedMap.put(filterID, facet); - } - previousIndex = index; - previousFP = facet; - } - - for (SolrFacetProperties fp : mergedMap.values()) - { - facetsMap.put(fp.getIndex(), fp); - } if (logger.isDebugEnabled() && persistedProperties.size() > 0) { logger.debug("The facets [" + persistedProperties + "] have overridden their matched default facets."); } + + final Set newFacetOrder = (facetOrder == null) ? new LinkedHashSet(sortedMap.size()) : new LinkedHashSet(facetOrder); + + for (SolrFacetProperties fp : sortedMap.values()) + { + newFacetOrder.add(fp.getFilterID()); + } + + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Exception + { + reorderFacets(new ArrayList(newFacetOrder)); + return null; + } + }, false); + } + }, AuthenticationUtil.getSystemUserName()); + + if (logger.isDebugEnabled()) + { + logger.debug("The facets order [" + newFacetOrder + "] have been persisted."); + } } private Map getPersistedFacetProperties() @@ -629,38 +683,29 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF // nothing to do } - @Override - public void beforeUpdateNode(NodeRef nodeRef) - { - // Remove the facet, in order to not end up with duplicate facets but different index - SolrFacetProperties fp = getFacetProperties(nodeRef); - this.facetsMap.remove(fp.getIndex()); - } - - @Override - public void onUpdateNode(NodeRef nodeRef) - { - SolrFacetProperties fp = getFacetProperties(nodeRef); - this.facetsMap.put(fp.getIndex(), fp); - } - @Override public void onCreateNode(ChildAssociationRef childAssocRef) { SolrFacetProperties fp = getFacetProperties(childAssocRef.getChildRef()); - this.facetsMap.put(fp.getIndex(), fp); this.facetNodeRefCache.put(fp.getFilterID(), childAssocRef.getChildRef()); - + // We must also add the new filterID to the facetOrder property. final NodeRef facetsRoot = getFacetsRoot(); - + @SuppressWarnings("unchecked") ArrayList facetOrder = (ArrayList) nodeService.getProperty(facetsRoot, SolrFacetModel.PROP_FACET_ORDER); - // FIXME This could be null. - + if (facetOrder == null) + { + List facets = getFacets(); + facetOrder = new ArrayList(facets.size()); + for (SolrFacetProperties facet : facets) + { + facetOrder.add(facet.getFilterID()); + } + } // We'll put it at the end (arbitrarily). facetOrder.add(fp.getFilterID()); - + nodeService.setProperty(facetsRoot, SolrFacetModel.PROP_FACET_ORDER, facetOrder); } @@ -668,43 +713,20 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF public void beforeDeleteNode(NodeRef nodeRef) { String filterID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - int index = (Integer) nodeService.getProperty(nodeRef, SolrFacetModel.PROP_INDEX); - - this.facetsMap.remove(index); this.facetNodeRefCache.remove(filterID); - + // We must also remove the filterID from the facetOrder property. final NodeRef facetsRoot = getFacetsRoot(); - + @SuppressWarnings("unchecked") ArrayList facetOrder = (ArrayList) nodeService.getProperty(facetsRoot, SolrFacetModel.PROP_FACET_ORDER); - + if (facetOrder.remove(filterID)) { nodeService.setProperty(facetsRoot, SolrFacetModel.PROP_FACET_ORDER, facetOrder); } } - @Override - public int getNextIndex() - { - synchronized (facetsMap) - { - if (facetsMap.size() >= maxAllowedFilters) - { - throw new SolrFacetConfigException("You have reached the maximum number of allowed filters. Please delete an existing filter in order to make a new one!"); - } - int max = facetsMap.lastKey(); - if (max >= Integer.MAX_VALUE) - { - reorder(); - max = facetsMap.lastKey(); - } - - return max + 1; - } - } - /** * Gets a map containing the facet's custom properties * @@ -724,46 +746,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF return customProperties; } - /** - * This will reorder the facetsMap, hence, the invoker needs to use an - * appropriate locking mechanism - */ - private void reorder() - { - boolean order = false; - int previous = 0; - for (int i : facetsMap.keySet()) - { - if (i != previous) - { - order = true; - break; - } - previous++; - } - - if (order) - { - Map tempMap = new LinkedHashMap<>(); - int index = 0; - for (SolrFacetProperties fp : facetsMap.values()) - { - if (fp.getIndex() != index) - { - fp = new SolrFacetProperties.Builder(fp).index(index).build(); - } - tempMap.put(index, fp); - index++; - } - facetsMap.clear(); - - for (SolrFacetProperties fp : tempMap.values()) - { - updateFacet(fp); - } - } - } - @Override public void reorderFacets(List facetIds) { // We need to validate the provided facet IDs diff --git a/source/test-java/org/alfresco/Repository01TestSuite.java b/source/test-java/org/alfresco/Repository01TestSuite.java index 192f6a32c8..3c25017872 100644 --- a/source/test-java/org/alfresco/Repository01TestSuite.java +++ b/source/test-java/org/alfresco/Repository01TestSuite.java @@ -416,4 +416,9 @@ public class Repository01TestSuite extends TestSuite suite.addTestSuite(org.alfresco.repo.dictionary.DictionaryModelTypeTest.class); suite.addTestSuite(org.alfresco.repo.tagging.UpdateTagScopesActionExecuterTest.class); } + + static void tests66(TestSuite suite) + { + suite.addTest(org.alfresco.repo.search.impl.solr.facet.SolrFacetTestSuite.suite()); + } } diff --git a/source/test-java/org/alfresco/repo/search/SearchTestSuite.java b/source/test-java/org/alfresco/repo/search/SearchTestSuite.java index 7afa85f541..ada267d42d 100644 --- a/source/test-java/org/alfresco/repo/search/SearchTestSuite.java +++ b/source/test-java/org/alfresco/repo/search/SearchTestSuite.java @@ -18,7 +18,6 @@ */ package org.alfresco.repo.search; -import junit.framework.JUnit4TestAdapter; import junit.framework.Test; import junit.framework.TestSuite; @@ -31,7 +30,6 @@ import org.alfresco.repo.search.impl.lucene.index.IndexInfoTest; import org.alfresco.repo.search.impl.parsers.CMISTest; import org.alfresco.repo.search.impl.parsers.CMIS_FTSTest; import org.alfresco.repo.search.impl.parsers.FTSTest; -import org.alfresco.repo.search.impl.solr.facet.SolrFacetHelperTest; import org.alfresco.util.NumericEncodingTest; /** @@ -63,11 +61,7 @@ public class SearchTestSuite extends TestSuite suite.addTestSuite(CMIS_FTSTest.class); suite.addTestSuite(CMISTest.class); suite.addTestSuite(FTSTest.class); - suite.addTest(new JUnit4TestAdapter(SolrFacetHelperTest.class)); - - - - + return suite; } } diff --git a/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparatorTest.java b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparatorTest.java index eaf0cdeeeb..77659a2d03 100644 --- a/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparatorTest.java +++ b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetComparatorTest.java @@ -34,15 +34,16 @@ public class SolrFacetComparatorTest { @Test public void simpleSortOfSortedFacets() throws Exception { - List expectedIds = Arrays.asList(new String[] { "a", "b", "c"}); + List expectedIds = Arrays.asList(new String[] { "a", "b", "c", "d"}); SolrFacetProperties.Builder builder = new SolrFacetProperties.Builder(); List facets = Arrays.asList(new SolrFacetProperties[] { - builder.filterID("c").index(1).build(), - builder.filterID("b").index(2).build(), - builder.filterID("a").index(3).build(), + builder.filterID("a").build(), + builder.filterID("d").build(), + builder.filterID("b").build(), + builder.filterID("c").build(), }); Collections.sort(facets, new SolrFacetComparator(expectedIds)); diff --git a/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfigTest.java b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfigTest.java new file mode 100644 index 0000000000..f2586a58e0 --- /dev/null +++ b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetConfigTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.search.impl.solr.facet; + +import java.util.Map; +import java.util.Properties; + +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.util.ApplicationContextHelper; +import org.junit.*; + +import static org.junit.Assert.*; + +import org.springframework.context.ApplicationEvent; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * This class contains tests for the class {@link SolrFacetConfig} + * + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class SolrFacetConfigTest +{ + private static ClassPathXmlApplicationContext context; + private static Properties rawProperties; + private static SolrFacetConfig facetConfig; + + @BeforeClass + public static void setUp() throws Exception + { + context = new ClassPathXmlApplicationContext(new String[] { "classpath:facets/test-facet-property-context.xml" }, + ApplicationContextHelper.getApplicationContext()); + + rawProperties = context.getBean("solrFacetRawPropertiesTest", Properties.class); + facetConfig = context.getBean("solrFacetConfigsTest", SolrFacetConfig.class); + + } + + @AfterClass + public static void tearDown() throws Exception + { + context.close(); + } + + @Test + public void testBasic() throws Exception + { + SolrFacetConfig config = null; + try + { + config = new SolrFacetConfig(null, ""); + fail("Null properties should have been detected"); + } + catch (IllegalArgumentException e) + { + // Expected + } + try + { + config = new SolrFacetConfig(rawProperties, null); + fail("Null properties should have been detected"); + } + catch (IllegalArgumentException e) + { + // Expected + } + config = new SolrFacetConfig(rawProperties, "default,custom"); + config.setNamespaceService(context.getBean("namespaceService", NamespaceService.class)); + try + { + config.getDefaultFacets(); + fail("Initialization should be done."); + } + catch (IllegalStateException e) + { + // Expected + } + } + + @Test + public void testDefault() throws Exception + { + Map defaultProps = facetConfig.getDefaultFacets(); + assertNotNull(defaultProps); + assertEquals("Incorrect number of properties", 4, defaultProps.size()); + + // loaded from /facets/facets-config-sample.properties + SolrFacetProperties contentSizeFP = defaultProps.get("test_filter_content_size"); + assertEquals("Incorrect QNAME", "{http://www.alfresco.org/model/content/1.0}content.size", contentSizeFP.getFacetQName().toString()); + assertEquals("faceted-search.facet-menu.facet.size", contentSizeFP.getDisplayName()); + assertEquals("alfresco/search/FacetFilters", contentSizeFP.getDisplayControl()); + assertEquals(5, contentSizeFP.getMaxFilters()); + assertEquals(1, contentSizeFP.getHitThreshold()); + assertEquals(4, contentSizeFP.getMinFilterValueLength()); + assertEquals("ALPHABETICALLY", contentSizeFP.getSortBy()); + assertEquals("ALL", contentSizeFP.getScope()); + assertEquals(0, contentSizeFP.getScopedSites().size()); + assertEquals(true, contentSizeFP.isEnabled()); + assertEquals(1, contentSizeFP.getCustomProperties().size()); + String customValue = (String) contentSizeFP.getCustomProperties().iterator().next().getValue(); + assertTrue(Boolean.valueOf(customValue)); + + // loaded from /facets/extension/facets-config-custom-sample.properties + SolrFacetProperties descFP = defaultProps.get("test_filter_description"); + assertEquals("Incorrect QNAME", "{http://www.alfresco.org/model/content/1.0}description", descFP.getFacetQName().toString()); + assertEquals("faceted-search.facet-menu.facet.description", descFP.getDisplayName()); + assertEquals("alfresco/search/FacetFilters", descFP.getDisplayControl()); + assertEquals(3, descFP.getMaxFilters()); + assertEquals(1, descFP.getHitThreshold()); + assertEquals(2, descFP.getMinFilterValueLength()); + assertEquals("DESCENDING", descFP.getSortBy()); + assertEquals("SCOPED_SITES", descFP.getScope()); + assertEquals(0, descFP.getScopedSites().size()); + assertEquals(true, descFP.isEnabled()); + + // See if the overrides worked + SolrFacetProperties creatorFP = defaultProps.get("test_filter_creator"); + assertEquals("Incorrect QNAME", "{http://www.alfresco.org/model/content/1.0}creator.__.u", creatorFP.getFacetQName().toString()); + + String msg = "The value has not been overridden with the value from the custom properties"; + assertEquals(msg, 10, creatorFP.getMaxFilters()); + assertEquals(msg, 5, creatorFP.getHitThreshold()); + assertEquals(msg, 14, creatorFP.getMinFilterValueLength()); + assertEquals(msg, 1, creatorFP.getScopedSites().size()); + assertEquals("site1", creatorFP.getScopedSites().iterator().next()); + + } + + @Test + public void testOverrideOrder() throws Exception + { + ApplicationEvent applicationEvent = new ApplicationEvent(this) + { + private static final long serialVersionUID = 1L; + }; + + /* + * Override order: default,custom + */ + SolrFacetConfig config = new SolrFacetConfig(rawProperties, "default,custom"); + config.setNamespaceService(context.getBean("namespaceService", NamespaceService.class)); + config.onBootstrap(applicationEvent); + + SolrFacetProperties creatorFP = config.getDefaultFacets().get("test_filter_creator"); + assertEquals("Incorrect QNAME", "{http://www.alfresco.org/model/content/1.0}creator.__.u", creatorFP.getFacetQName().toString()); + assertEquals(10, creatorFP.getMaxFilters()); + assertEquals(5, creatorFP.getHitThreshold()); + assertEquals(14, creatorFP.getMinFilterValueLength()); + assertEquals(1, creatorFP.getScopedSites().size()); + assertEquals("site1", creatorFP.getScopedSites().iterator().next()); + + /* + * Override order: custom,default + */ + config = new SolrFacetConfig(rawProperties, "custom,default"); + config.setNamespaceService(context.getBean("namespaceService", NamespaceService.class)); + config.onBootstrap(applicationEvent); + + creatorFP = config.getDefaultFacets().get("test_filter_creator"); + assertEquals("Incorrect QNAME", "{http://www.alfresco.org/model/content/1.0}creator.__.u", creatorFP.getFacetQName().toString()); + assertEquals(5, creatorFP.getMaxFilters()); + assertEquals(1, creatorFP.getHitThreshold()); + assertEquals(4, creatorFP.getMinFilterValueLength()); + assertEquals(0, creatorFP.getScopedSites().size()); + } +} \ No newline at end of file diff --git a/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetTestSuite.java b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetTestSuite.java new file mode 100644 index 0000000000..0613a2a63f --- /dev/null +++ b/source/test-java/org/alfresco/repo/search/impl/solr/facet/SolrFacetTestSuite.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.search.impl.solr.facet; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author Jamal Kaabi-Mofrad + */ +public class SolrFacetTestSuite extends TestSuite +{ + + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + suite.addTest(new JUnit4TestAdapter(SolrFacetHelperTest.class)); + suite.addTest(new JUnit4TestAdapter(SolrFacetServiceImplTest.class)); + suite.addTest(new JUnit4TestAdapter(SolrFacetConfigTest.class)); + + return suite; + } +} diff --git a/source/test-resources/facets/extension/facets-config-custom-sample.properties b/source/test-resources/facets/extension/facets-config-custom-sample.properties new file mode 100644 index 0000000000..805e69cdbc --- /dev/null +++ b/source/test-resources/facets/extension/facets-config-custom-sample.properties @@ -0,0 +1,26 @@ +# Overrides test_filter_creator in the /facets/facets-config-sample.properties +custom.cm\:creator.__.u.filterID=test_filter_creator +custom.cm\:creator.__.u.displayName=faceted-search.facet-menu.facet.creator +custom.cm\:creator.__.u.displayControl=alfresco/search/FacetFilters +custom.cm\:creator.__.u.maxFilters=10 +custom.cm\:creator.__.u.hitThreshold=5 +custom.cm\:creator.__.u.minFilterValueLength=14 +custom.cm\:creator.__.u.sortBy=ALPHABETICALLY +custom.cm\:creator.__.u.scope=ALL +custom.cm\:creator.__.u.scopedSites=site1 +custom.cm\:creator.__.u.index=0 +custom.cm\:creator.__.u.isEnabled=true + +# Add a new Filter +# Field-Facet-Qname => cm:description.__ +custom.cm\:description.filterID=test_filter_description +custom.cm\:description.displayName=faceted-search.facet-menu.facet.description +custom.cm\:description.displayControl=alfresco/search/FacetFilters +custom.cm\:description.maxFilters=3 +custom.cm\:description.hitThreshold=1 +custom.cm\:description.minFilterValueLength=2 +custom.cm\:description.sortBy=DESCENDING +custom.cm\:description.scope=SCOPED_SITES +custom.cm\:description.scopedSites= +custom.cm\:description.index=4 +custom.cm\:description.isEnabled=true \ No newline at end of file diff --git a/source/test-resources/facets/facets-config-sample.properties b/source/test-resources/facets/facets-config-sample.properties new file mode 100644 index 0000000000..4a160172dd --- /dev/null +++ b/source/test-resources/facets/facets-config-sample.properties @@ -0,0 +1,36 @@ +# Field-Facet-Qname => cm:creator.__.u +default.cm\:creator.__.u.filterID=test_filter_creator +default.cm\:creator.__.u.displayName=faceted-search.facet-menu.facet.creator +default.cm\:creator.__.u.displayControl=alfresco/search/FacetFilters +default.cm\:creator.__.u.maxFilters=5 +default.cm\:creator.__.u.hitThreshold=1 +default.cm\:creator.__.u.minFilterValueLength=4 +default.cm\:creator.__.u.sortBy=ALPHABETICALLY +default.cm\:creator.__.u.scope=ALL +default.cm\:creator.__.u.scopedSites= +default.cm\:creator.__.u.isEnabled=true + +# Field-Facet-Qname => cm:modifier.__.u +default.cm\:modifier.__.u.filterID=test_filter_modifier +default.cm\:modifier.__.u.displayName=faceted-search.facet-menu.facet.modifier +default.cm\:modifier.__.u.displayControl=alfresco/search/FacetFilters +default.cm\:modifier.__.u.maxFilters=5 +default.cm\:modifier.__.u.hitThreshold=1 +default.cm\:modifier.__.u.minFilterValueLength=4 +default.cm\:modifier.__.u.sortBy=ALPHABETICALLY +default.cm\:modifier.__.u.scope=SCOPED_SITES +default.cm\:modifier.__.u.scopedSites= +default.cm\:modifier.__.u.isEnabled=true + +# Field-Facet-Qname => cm:content.size +default.cm\:content.size.filterID=test_filter_content_size +default.cm\:content.size.displayName=faceted-search.facet-menu.facet.size +default.cm\:content.size.displayControl=alfresco/search/FacetFilters +default.cm\:content.size.maxFilters=5 +default.cm\:content.size.hitThreshold=1 +default.cm\:content.size.minFilterValueLength=4 +default.cm\:content.size.sortBy=ALPHABETICALLY +default.cm\:content.size.scope=ALL +default.cm\:content.size.scopedSites= +default.cm\:content.size.isEnabled=true +default.cm\:content.size.EXTRA-PROP.blockIncludeFacetRequest=true \ No newline at end of file diff --git a/source/test-resources/facets/test-facet-property-context.xml b/source/test-resources/facets/test-facet-property-context.xml new file mode 100644 index 0000000000..8f1406b6b8 --- /dev/null +++ b/source/test-resources/facets/test-facet-property-context.xml @@ -0,0 +1,21 @@ + + + + + + + + + classpath*:facets/facets-config-sample.properties + classpath*:facets/extension/facets-config-custom-sample.properties + + + + + + + default,custom + + + + \ No newline at end of file