Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (5.0/Cloud)

80520: Merged WAT1 (5.0/Cloud) to HEAD-BUG-FIX (5.0/Cloud)
      74961: ACE-1582: Added index control and custom data support for the facet configs.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@82817 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Will Abson
2014-09-03 15:53:15 +00:00
parent b0d07f4c04
commit 26880952a0
7 changed files with 510 additions and 94 deletions

View File

@@ -18,6 +18,8 @@
<!-- Solr Facets Namespace --> <!-- Solr Facets Namespace -->
<namespaces> <namespaces>
<namespace uri="http://www.alfresco.org/model/solrfacet/1.0" prefix="srft" /> <namespace uri="http://www.alfresco.org/model/solrfacet/1.0" prefix="srft" />
<!-- if you want to extend the properties of a facet then use this namespace to mark the custom properties -->
<namespace uri="http://www.alfresco.org/model/solrfacetcustomproperty/1.0" prefix="srftcustom"/>
</namespaces> </namespaces>
<types> <types>
@@ -155,4 +157,18 @@
<parent>cm:folder</parent> <parent>cm:folder</parent>
</type> </type>
</types> </types>
<aspects>
<!-- Custom Facet Properties Aspect -->
<aspect name="srft:customProperties">
<title>Facet Custom Properties</title>
<properties>
<property name="srftcustom:extraInformation">
<title>Additional Facet Information</title>
<type>d:text</type>
</property>
</properties>
</aspect>
</aspects>
</model> </model>

View File

@@ -12,7 +12,7 @@ default.cm\:content.mimetype.hitThreshold=1
default.cm\:content.mimetype.minFilterValueLength=4 default.cm\:content.mimetype.minFilterValueLength=4
default.cm\:content.mimetype.sortBy=DESCENDING default.cm\:content.mimetype.sortBy=DESCENDING
default.cm\:content.mimetype.scope=SCOPED_SITES default.cm\:content.mimetype.scope=SCOPED_SITES
default.cm\:content.mimetype.scopedSites=site1,site2,site3 default.cm\:content.mimetype.scopedSites=
default.cm\:content.mimetype.index=0 default.cm\:content.mimetype.index=0
default.cm\:content.mimetype.isEnabled=true default.cm\:content.mimetype.isEnabled=true
@@ -25,7 +25,7 @@ default.cm\:description.__.hitThreshold=1
default.cm\:description.__.minFilterValueLength=4 default.cm\:description.__.minFilterValueLength=4
default.cm\:description.__.sortBy=DESCENDING default.cm\:description.__.sortBy=DESCENDING
default.cm\:description.__.scope=SCOPED_SITES default.cm\:description.__.scope=SCOPED_SITES
default.cm\:description.__.scopedSites=site1,site2,site3 default.cm\:description.__.scopedSites=
default.cm\:description.__.index=1 default.cm\:description.__.index=1
default.cm\:description.__.isEnabled=true default.cm\:description.__.isEnabled=true
@@ -38,7 +38,7 @@ default.cm\:creator.__.u.hitThreshold=1
default.cm\:creator.__.u.minFilterValueLength=4 default.cm\:creator.__.u.minFilterValueLength=4
default.cm\:creator.__.u.sortBy=ALPHABETICALLY default.cm\:creator.__.u.sortBy=ALPHABETICALLY
default.cm\:creator.__.u.scope=SCOPED_SITES default.cm\:creator.__.u.scope=SCOPED_SITES
default.cm\:creator.__.u.scopedSites=site1,site2,site3 default.cm\:creator.__.u.scopedSites=
default.cm\:creator.__.u.index=2 default.cm\:creator.__.u.index=2
default.cm\:creator.__.u.isEnabled=true default.cm\:creator.__.u.isEnabled=true
@@ -51,7 +51,7 @@ default.cm\:modifier.__.u.hitThreshold=1
default.cm\:modifier.__.u.minFilterValueLength=4 default.cm\:modifier.__.u.minFilterValueLength=4
default.cm\:modifier.__.u.sortBy=ALPHABETICALLY default.cm\:modifier.__.u.sortBy=ALPHABETICALLY
default.cm\:modifier.__.u.scope=SCOPED_SITES default.cm\:modifier.__.u.scope=SCOPED_SITES
default.cm\:modifier.__.u.scopedSites=site1,site2,site3 default.cm\:modifier.__.u.scopedSites=
default.cm\:modifier.__.u.index=3 default.cm\:modifier.__.u.index=3
default.cm\:modifier.__.u.isEnabled=true default.cm\:modifier.__.u.isEnabled=true
@@ -59,40 +59,40 @@ default.cm\:modifier.__.u.isEnabled=true
default.cm\:created.filterID=filter_created default.cm\:created.filterID=filter_created
default.cm\:created.displayName=faceted-search.facet-menu.facet.created default.cm\:created.displayName=faceted-search.facet-menu.facet.created
default.cm\:created.displayControl=alfresco/search/FacetFilters default.cm\:created.displayControl=alfresco/search/FacetFilters
default.cm\:created.blockIncludeFacetRequest=true
default.cm\:created.maxFilters=5 default.cm\:created.maxFilters=5
default.cm\:created.hitThreshold=1 default.cm\:created.hitThreshold=1
default.cm\:created.minFilterValueLength=4 default.cm\:created.minFilterValueLength=4
default.cm\:created.sortBy=ALPHABETICALLY default.cm\:created.sortBy=ALPHABETICALLY
default.cm\:created.scope=SCOPED_SITES default.cm\:created.scope=SCOPED_SITES
default.cm\:created.scopedSites=site1,site2,site3 default.cm\:created.scopedSites=
default.cm\:created.index=4 default.cm\:created.index=4
default.cm\:created.isEnabled=true default.cm\:created.isEnabled=true
default.cm\:created.EXTRA-PROP.blockIncludeFacetRequest=true
# Field-Facet-Qname => cm:modified # Field-Facet-Qname => cm:modified
default.cm\:modified.filterID=filter_modified default.cm\:modified.filterID=filter_modified
default.cm\:modified.displayName=faceted-search.facet-menu.facet.modified default.cm\:modified.displayName=faceted-search.facet-menu.facet.modified
default.cm\:modified.displayControl=alfresco/search/FacetFilters default.cm\:modified.displayControl=alfresco/search/FacetFilters
default.cm\:modified.blockIncludeFacetRequest=true
default.cm\:modified.maxFilters=5 default.cm\:modified.maxFilters=5
default.cm\:modified.hitThreshold=1 default.cm\:modified.hitThreshold=1
default.cm\:modified.minFilterValueLength=4 default.cm\:modified.minFilterValueLength=4
default.cm\:modified.sortBy=ALPHABETICALLY default.cm\:modified.sortBy=ALPHABETICALLY
default.cm\:modified.scope=SCOPED_SITES default.cm\:modified.scope=SCOPED_SITES
default.cm\:modified.scopedSites=site1,site2,site3 default.cm\:modified.scopedSites=
default.cm\:modified.index=5 default.cm\:modified.index=5
default.cm\:modified.isEnabled=true default.cm\:modified.isEnabled=true
default.cm\:modified.EXTRA-PROP.blockIncludeFacetRequest=true
# Field-Facet-Qname => cm:content.size # Field-Facet-Qname => cm:content.size
default.cm\:content.size.filterID=filter_content_size default.cm\:content.size.filterID=filter_content_size
default.cm\:content.size.displayName=faceted-search.facet-menu.facet.size default.cm\:content.size.displayName=faceted-search.facet-menu.facet.size
default.cm\:content.size.displayControl=alfresco/search/FacetFilters default.cm\:content.size.displayControl=alfresco/search/FacetFilters
default.cm\:content.size.blockIncludeFacetRequest=true
default.cm\:content.size.maxFilters=5 default.cm\:content.size.maxFilters=5
default.cm\:content.size.hitThreshold=1 default.cm\:content.size.hitThreshold=1
default.cm\:content.size.minFilterValueLength=4 default.cm\:content.size.minFilterValueLength=4
default.cm\:content.size.sortBy=ALPHABETICALLY default.cm\:content.size.sortBy=ALPHABETICALLY
default.cm\:content.size.scope=SCOPED_SITES default.cm\:content.size.scope=SCOPED_SITES
default.cm\:content.size.scopedSites=site1,site2,site3 default.cm\:content.size.scopedSites=
default.cm\:content.size.index=6 default.cm\:content.size.index=6
default.cm\:content.size.isEnabled=true default.cm\:content.size.isEnabled=true
default.cm\:content.size.EXTRA-PROP.blockIncludeFacetRequest=true

View File

@@ -19,15 +19,19 @@
package org.alfresco.repo.search.impl.solr.facet; package org.alfresco.repo.search.impl.solr.facet;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.search.impl.solr.facet.SolrFacetProperties.CustomProperties;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
@@ -47,6 +51,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
* <ul> * <ul>
* <li>custom.cm\:content.mimetype.filterID=filter_abc</li> * <li>custom.cm\:content.mimetype.filterID=filter_abc</li>
* <li>custom.cm\:content.mimetype.displayName=faceted-search.facet-menu.facet.formats</li> * <li>custom.cm\:content.mimetype.displayName=faceted-search.facet-menu.facet.formats</li>
* <li>custom.cm\:content.mimetype.displayControl=alfresco/search/FacetFilters</li>
* <li>custom.cm\:content.mimetype.maxFilters=5</li> * <li>custom.cm\:content.mimetype.maxFilters=5</li>
* <li>custom.cm\:content.mimetype.hitThreshold=1</li> * <li>custom.cm\:content.mimetype.hitThreshold=1</li>
* <li>custom.cm\:content.mimetype.minFilterValueLength=4</li> * <li>custom.cm\:content.mimetype.minFilterValueLength=4</li>
@@ -56,6 +61,12 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
* <li>custom.cm\:content.mimetype.index=0</li> * <li>custom.cm\:content.mimetype.index=0</li>
* <li>custom.cm\:content.mimetype.isEnabled=true</li> * <li>custom.cm\:content.mimetype.isEnabled=true</li>
* </ul> * </ul>
* Also, if there is a need to add additional properties, the following needs to be
* put into a properties file:
* <ul>
* <li>custom.cm\:content.mimetype<b>.EXTRA-PROP.</b>blockIncludeFacetRequest=true</li>
* <li>custom.cm\:content.mimetype<b>.EXTRA-PROP.</b>moreProp=additionalInfo</li>
* </ul>
* The inheritance order is strictly defined using property:<br/> * The inheritance order is strictly defined using property:<br/>
* <b>${solr_facets.inheritanceHierarchy}</b><br/> * <b>${solr_facets.inheritanceHierarchy}</b><br/>
* The default inheritance orders are:<br/> * The default inheritance orders are:<br/>
@@ -69,6 +80,9 @@ public class SolrFacetConfig extends AbstractLifecycleBean
{ {
private static final Log logger = LogFactory.getLog(SolrFacetConfig.class); private static final Log logger = LogFactory.getLog(SolrFacetConfig.class);
private static final String KEY_EXTRA_INFO = ".EXTRA-PROP.";
private static final int KEY_EXTRA_INFO_LENGTH = KEY_EXTRA_INFO.length();
private final Properties rawProperties; private final Properties rawProperties;
private final Set<String> propInheritanceOrder; private final Set<String> propInheritanceOrder;
@@ -163,15 +177,39 @@ public class SolrFacetConfig extends AbstractLifecycleBean
} }
} }
Set<String> facetFields = new HashSet<>(); Map<String, Set<String>> facetFields = new HashMap<>();
for(String key : propValues.keySet()) for(String key : propValues.keySet())
{ {
facetFields.add(key.substring(0, key.lastIndexOf('.'))); String facetQName = null;
Set<String> extraProp = null;
int index = key.indexOf(KEY_EXTRA_INFO);
if (index > 0)
{
String extraInfo = key.substring(index + KEY_EXTRA_INFO_LENGTH);
facetQName = key.substring(0, index);
extraProp = facetFields.get(facetQName);
if (extraProp == null)
{
extraProp = new HashSet<>();
}
if (extraInfo.length() > 0)
{
extraProp.add(extraInfo);
}
}
else
{
index = key.lastIndexOf('.');
facetQName = key.substring(0, index);
extraProp = facetFields.get(facetQName);
}
facetFields.put(facetQName, extraProp);
} }
// Build the facet config objects // Build the facet config objects
Map<String, SolrFacetProperties> facetProperties = new HashMap<>(100); Map<String, SolrFacetProperties> facetProperties = new HashMap<>(100);
for (String field : facetFields) for (String field : facetFields.keySet())
{ {
// FacetProperty attributes // FacetProperty attributes
// Resolve facet field into QName // Resolve facet field into QName
@@ -186,7 +224,12 @@ public class SolrFacetConfig extends AbstractLifecycleBean
String scope = propValues.get(ValueName.PROP_SCOPE.getPropValueName(field)); String scope = propValues.get(ValueName.PROP_SCOPE.getPropValueName(field));
Set<String> scopedSites = getScopedSites(propValues.get(ValueName.PROP_SCOPED_SITES.getPropValueName(field))); Set<String> scopedSites = getScopedSites(propValues.get(ValueName.PROP_SCOPED_SITES.getPropValueName(field)));
int index = getIntegerValue(propValues.get(ValueName.PROP_INDEX.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<CustomProperties> customProps = getCustomProps(facetFields.get(field), field, propValues);
// Construct the FacetProperty object // Construct the FacetProperty object
SolrFacetProperties fp = new SolrFacetProperties.Builder() SolrFacetProperties fp = new SolrFacetProperties.Builder()
@@ -202,7 +245,8 @@ public class SolrFacetConfig extends AbstractLifecycleBean
.index(index) .index(index)
.isEnabled(isEnabled) .isEnabled(isEnabled)
.isDefault(true) .isDefault(true)
.scopedSites(scopedSites).build(); .scopedSites(scopedSites)
.customProperties(customProps).build();
facetProperties.put(filterID, fp); facetProperties.put(filterID, fp);
} }
@@ -284,6 +328,35 @@ public class SolrFacetConfig extends AbstractLifecycleBean
return set; return set;
} }
private static Set<CustomProperties> getCustomProps(Set<String> additionalProps, String field, Map<String, String> propValues)
{
if (additionalProps == null)
{
return Collections.emptySet();
}
Set<CustomProperties> customProps = new HashSet<>();
for (String extraInfo : additionalProps)
{
String value = propValues.get(field + KEY_EXTRA_INFO + extraInfo);
if (value != null)
{
QName qName = QName.createQName(SolrFacetModel.SOLR_FACET_CUSTOM_PROPERTY_URL, extraInfo);
String[] extra = value.split(",");
if (extra.length == 1)
{
customProps.add(new CustomProperties(qName, null, null, extra[0]));
}
else
{
List<String> list = Arrays.asList(extra);
customProps.add(new CustomProperties(qName, null, null, (Serializable) list));
}
}
}
return customProps;
}
private static int getIntegerValue(String propValue) private static int getIntegerValue(String propValue)
{ {
try try

View File

@@ -31,8 +31,13 @@ public interface SolrFacetModel
public static final String SOLR_FACET_MODEL_URL = "http://www.alfresco.org/model/solrfacet/1.0"; public static final String SOLR_FACET_MODEL_URL = "http://www.alfresco.org/model/solrfacet/1.0";
public static final String PREFIX = "srft"; public static final String PREFIX = "srft";
public static final String SOLR_FACET_CUSTOM_PROPERTY_URL = "http://www.alfresco.org/model/solrfacetcustomproperty/1.0";
public static final String SOLR_FACET_CUSTOM_PROPERTY_PREFIX = "srftcustom";
public static final QName TYPE_FACET_FIELD = QName.createQName(SOLR_FACET_MODEL_URL, "facetField"); public static final QName TYPE_FACET_FIELD = QName.createQName(SOLR_FACET_MODEL_URL, "facetField");
public static final QName ASPECT_CUSTOM_PROPERTIES = QName.createQName(SOLR_FACET_MODEL_URL, "customProperties");
public static final QName PROP_FIELD_TYPE = QName.createQName(SOLR_FACET_MODEL_URL, "fieldType"); public static final QName PROP_FIELD_TYPE = QName.createQName(SOLR_FACET_MODEL_URL, "fieldType");
public static final QName PROP_FIELD_LABEL = QName.createQName(SOLR_FACET_MODEL_URL, "fieldLabel"); public static final QName PROP_FIELD_LABEL = QName.createQName(SOLR_FACET_MODEL_URL, "fieldLabel");
@@ -56,4 +61,6 @@ public interface SolrFacetModel
public static final QName PROP_IS_ENABLED = QName.createQName(SOLR_FACET_MODEL_URL, "isEnabled"); 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_IS_DEFAULT = QName.createQName(SOLR_FACET_MODEL_URL, "isDefault");
public static final QName PROP_EXTRA_INFORMATION = QName.createQName(SOLR_FACET_CUSTOM_PROPERTY_URL, "extraInformation");
} }

View File

@@ -19,20 +19,23 @@
package org.alfresco.repo.search.impl.solr.facet; package org.alfresco.repo.search.impl.solr.facet;
import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
/** /**
* Domain-Specific Language (DSL) style builder class for encapsulating the * Domain-Specific Language (DSL) style builder class for encapsulating the facet properties.
* facet properties.
* *
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
*/ */
public class SolrFacetProperties implements Comparable<SolrFacetProperties> public class SolrFacetProperties implements Serializable
{ {
private static final long serialVersionUID = 2991173095752087202L;
private final String filterID; private final String filterID;
private final QName facetQName; private final QName facetQName;
private final String displayName; private final String displayName;
@@ -46,6 +49,7 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
private final int index; private final int index;
private final boolean isEnabled; private final boolean isEnabled;
private final boolean isDefault; // is loaded from properties files? private final boolean isDefault; // is loaded from properties files?
private final Set<CustomProperties> customProperties;
/** /**
* Initialises a newly created <code>SolrFacetProperty</code> object * Initialises a newly created <code>SolrFacetProperty</code> object
@@ -66,7 +70,8 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
this.index = builder.index; this.index = builder.index;
this.isEnabled = builder.isEnabled; this.isEnabled = builder.isEnabled;
this.isDefault = builder.isDefault; this.isDefault = builder.isDefault;
this.scopedSites = (builder.scopedSites == null) ? null :Collections.unmodifiableSet(new HashSet<String>(builder.scopedSites)); this.scopedSites = Collections.unmodifiableSet(new HashSet<String>(builder.scopedSites));
this.customProperties = Collections.unmodifiableSet(new HashSet<CustomProperties>(builder.customProperties));
} }
/** /**
@@ -142,16 +147,12 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
} }
/** /**
* Returns an unmodifiable view of the Scoped Sites set or null * Returns an unmodifiable view of the Scoped Sites set. Never null.
* *
* @return the scopedSites * @return the scopedSites
*/ */
public Set<String> getScopedSites() public Set<String> getScopedSites()
{ {
if (this.scopedSites == null)
{
return null;
}
return Collections.unmodifiableSet(new HashSet<String>(this.scopedSites)); return Collections.unmodifiableSet(new HashSet<String>(this.scopedSites));
} }
@@ -181,6 +182,17 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
return this.isDefault; return this.isDefault;
} }
/**
* Returns an unmodifiable view of the custom properties set. Never null.
*
* @return the customProperties
*/
public Set<CustomProperties> getCustomProperties()
{
return Collections.unmodifiableSet(new HashSet<CustomProperties>(this.customProperties));
}
/* /*
* @see java.lang.Object#hashCode() * @see java.lang.Object#hashCode()
*/ */
@@ -226,22 +238,13 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
return true; return true;
} }
/*
* @see java.lang.Comparable#compareTo(T)
*/
@Override
public int compareTo(SolrFacetProperties that)
{
return Integer.compare(this.index, that.index);
}
/* /*
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */
@Override @Override
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(320); StringBuilder sb = new StringBuilder(400);
sb.append("FacetProperty [filterID=").append(this.filterID).append(", facetQName=") sb.append("FacetProperty [filterID=").append(this.filterID).append(", facetQName=")
.append(this.facetQName).append(", displayName=").append(this.displayName) .append(this.facetQName).append(", displayName=").append(this.displayName)
.append(", displayControl=").append(this.displayControl).append(", maxFilters=") .append(", displayControl=").append(this.displayControl).append(", maxFilters=")
@@ -249,7 +252,8 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
.append(", minFilterValueLength=").append(this.minFilterValueLength).append(", sortBy=") .append(", minFilterValueLength=").append(this.minFilterValueLength).append(", sortBy=")
.append(this.sortBy).append(", scope=").append(this.scope).append(", scopedSites=") .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(", index=").append(this.index).append(", isEnabled=").append(this.isEnabled)
.append(", isDefault=").append(this.isDefault).append("]"); .append(", isDefault=").append(this.isDefault).append(", customProperties=").append(this.customProperties)
.append("]");
return sb.toString(); return sb.toString();
} }
@@ -264,10 +268,38 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
private int minFilterValueLength; private int minFilterValueLength;
private String sortBy; private String sortBy;
private String scope; private String scope;
private Set<String> scopedSites; private Set<String> scopedSites = Collections.emptySet();
private int index; private int index;
private boolean isEnabled; private boolean isEnabled;
private boolean isDefault; private boolean isDefault;
private Set<CustomProperties> customProperties = Collections.emptySet();
public Builder()
{
}
/**
* Copy builder
*
* @param that existing {@code SolrFacetProperties} object
*/
public Builder(SolrFacetProperties that)
{
this.filterID = that.filterID;
this.facetQName = that.facetQName;
this.displayName = that.displayName;
this.displayControl = that.displayControl;
this.maxFilters = that.maxFilters;
this.hitThreshold = that.hitThreshold;
this.minFilterValueLength = that.minFilterValueLength;
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;
}
public Builder filterID(String filterID) public Builder filterID(String filterID)
{ {
@@ -325,7 +357,10 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
public Builder scopedSites(Set<String> scopedSites) public Builder scopedSites(Set<String> scopedSites)
{ {
this.scopedSites = scopedSites; if (scopedSites != null)
{
this.scopedSites = scopedSites;
}
return this; return this;
} }
@@ -347,9 +382,106 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
return this; return this;
} }
public Builder customProperties(Set<CustomProperties> customProperties)
{
if (customProperties != null)
{
this.customProperties = customProperties;
}
return this;
}
public SolrFacetProperties build() public SolrFacetProperties build()
{ {
return new SolrFacetProperties(this); return new SolrFacetProperties(this);
} }
} }
public static class CustomProperties implements Serializable
{
private static final long serialVersionUID = 2250062300454166258L;
private final QName name;
private final String title;
private final String type;
private final Serializable value;
public CustomProperties(QName name, String title, String type, Serializable value)
{
this.name = name;
this.title = title;
this.type = type;
this.value = value;
}
public QName getName()
{
return this.name;
}
/**
* @return the title
*/
public String getTitle()
{
return this.title;
}
/**
* @return the type
*/
public String getType()
{
return this.type;
}
public Serializable getValue()
{
return this.value;
}
/*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
/*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null || !(obj instanceof CustomProperties))
{
return false;
}
CustomProperties other = (CustomProperties) obj;
return EqualsHelper.nullSafeEquals(this.name, other.name);
}
/*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder builder = new StringBuilder(100);
builder.append("CustomProperties [name=").append(this.name).append(", title=")
.append(this.title).append(", type=").append(this.type).append(", value=")
.append(this.value).append("]");
return builder.toString();
}
}
} }

View File

@@ -19,7 +19,7 @@
package org.alfresco.repo.search.impl.solr.facet; package org.alfresco.repo.search.impl.solr.facet;
import java.util.Map; import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -34,10 +34,9 @@ public interface SolrFacetService
/** /**
* Gets all the available facets. * Gets all the available facets.
* *
* @return Map of {@code SolrFacetProperties} with the * @return List of {@code SolrFacetProperties} or an empty list if none exists
* {@code SolrFacetProperties.filterID} as the key or an empty map if none exists
*/ */
public Map<String, SolrFacetProperties> getFacets(); public List<SolrFacetProperties> getFacets();
/** /**
* Gets the facet by filter Id. * Gets the facet by filter Id.
@@ -86,4 +85,6 @@ public interface SolrFacetService
* @param filterID the filter Id * @param filterID the filter Id
*/ */
public void deleteFacet(String filterID); public void deleteFacet(String filterID);
public int getNextIndex();
} }

View File

@@ -21,24 +21,30 @@ package org.alfresco.repo.search.impl.solr.facet;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentMap;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; 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.OnCreateNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnUpdateNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdateNodePolicy;
import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.search.impl.solr.facet.SolrFacetProperties.CustomProperties;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
@@ -60,14 +66,13 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/** /**
*
*
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
*/ */
public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrFacetService, public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrFacetService,
NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnUpdateNodePolicy, NodeServicePolicies.OnUpdateNodePolicy,
NodeServicePolicies.BeforeDeleteNodePolicy NodeServicePolicies.BeforeDeleteNodePolicy,
NodeServicePolicies.BeforeUpdateNodePolicy
{ {
private static final Log logger = LogFactory.getLog(SolrFacetServiceImpl.class); private static final Log logger = LogFactory.getLog(SolrFacetServiceImpl.class);
/** /**
@@ -92,7 +97,8 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
private SimpleCache<String, Object> singletonCache; // eg. for facetsHomeNodeRef private SimpleCache<String, Object> singletonCache; // eg. for facetsHomeNodeRef
private final String KEY_FACETS_HOME_NODEREF = "key.facetshome.noderef"; private final String KEY_FACETS_HOME_NODEREF = "key.facetshome.noderef";
private SimpleCache<String, NodeRef> facetNodeRefCache; // for filterID to nodeRef lookup private SimpleCache<String, NodeRef> facetNodeRefCache; // for filterID to nodeRef lookup
private ConcurrentMap<String, SolrFacetProperties> facetsMap = new ConcurrentHashMap<>(); private NavigableMap<Integer, SolrFacetProperties> facetsMap = new ConcurrentSkipListMap<>();
private int maxAllowedFilters = 100;
/** /**
* @param authorityService the authorityService to set * @param authorityService the authorityService to set
@@ -182,6 +188,14 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
this.facetNodeRefCache = facetNodeRefCache; this.facetNodeRefCache = facetNodeRefCache;
} }
/**
* @param maxAllowedFilters the maxAllowedFilters to set
*/
public void setMaxAllowedFilters(int maxAllowedFilters)
{
this.maxAllowedFilters = maxAllowedFilters;
}
@Override @Override
public boolean isSearchAdmin(String userName) public boolean isSearchAdmin(String userName)
{ {
@@ -194,16 +208,26 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
} }
@Override @Override
public Map<String, SolrFacetProperties> getFacets() public List<SolrFacetProperties> getFacets()
{ {
Map<String, SolrFacetProperties> sortedMap = CollectionUtils.sortMapByValue(facetsMap); return new ArrayList<>(facetsMap.values());
return sortedMap;
} }
@Override @Override
public SolrFacetProperties getFacet(String filterID) public SolrFacetProperties getFacet(String filterID)
{ {
return facetsMap.get(filterID); /*
* Note: There is no need to worry about the state of the SolrFacetProperties returned from
* facetConfig (getDefaultLoadedFacet), as if the FP has been modified, then we'll get it from
* the nodeService.
*/
NodeRef nodeRef = getFacetNodeRef(filterID);
return (nodeRef == null) ? getDefaultLoadedFacet(filterID) : getFacetProperties(nodeRef);
}
private SolrFacetProperties getDefaultLoadedFacet(String filterID)
{
return facetConfig.getDefaultFacets().get(filterID);
} }
@Override @Override
@@ -268,6 +292,12 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
List<String> scSites = (List<String>) properties.get(SolrFacetModel.PROP_SCOPED_SITES); List<String> scSites = (List<String>) properties.get(SolrFacetModel.PROP_SCOPED_SITES);
Set<String> scopedSites = (scSites == null) ? null : new HashSet<>(scSites); Set<String> scopedSites = (scSites == null) ? null : new HashSet<>(scSites);
Map<QName, Serializable> customProperties = getFacetCustomProperties(properties);
Set<CustomProperties> extraProps = new HashSet<>(customProperties.size());
for(Entry<QName, Serializable> cp : customProperties.entrySet())
{
extraProps.add(new CustomProperties(cp.getKey(), (String) properties.get(ContentModel.PROP_TITLE), null, cp.getValue()));
}
// Construct the FacetProperty object // Construct the FacetProperty object
SolrFacetProperties fp = new SolrFacetProperties.Builder() SolrFacetProperties fp = new SolrFacetProperties.Builder()
.filterID(filterID) .filterID(filterID)
@@ -282,7 +312,8 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
.index(index) .index(index)
.isEnabled(isEnabled) .isEnabled(isEnabled)
.isDefault(isDefault) .isDefault(isDefault)
.scopedSites(scopedSites).build(); .scopedSites(scopedSites)
.customProperties(extraProps).build();
return fp; return fp;
} }
@@ -298,9 +329,9 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
final String filterID = facetProperties.getFilterID(); final String filterID = facetProperties.getFilterID();
NodeRef facetNodeRef = getFacetNodeRef(filterID); NodeRef facetNodeRef = getFacetNodeRef(filterID);
// We need to check the bootstrapped Facet properties as well, in order // We need to check the bootstrapped Facet properties (i.e loaded from properties file(s)) as well,
// to not allow the user to create a new facet with the same filterID as the bootstrapped FP. // in order to not allow the user to create a new facet with the same filterID as the bootstrapped FP.
if (facetNodeRef != null || (checkDefaultFP && getFacet(filterID) != null)) if (facetNodeRef != null || (checkDefaultFP && getDefaultLoadedFacet(filterID) != null))
{ {
throw new SolrFacetConfigException("Unable to create facet because the filterID [" + filterID + "] is already in use."); throw new SolrFacetConfigException("Unable to create facet because the filterID [" + filterID + "] is already in use.");
} }
@@ -312,7 +343,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
throw new SolrFacetConfigException("Facets root folder does not exist."); throw new SolrFacetConfigException("Facets root folder does not exist.");
} }
return facetNodeRef = AuthenticationUtil.runAs(new RunAsWork<NodeRef>() return facetNodeRef = AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
{ {
@Override @Override
public NodeRef doWork() throws Exception public NodeRef doWork() throws Exception
@@ -324,7 +355,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
behaviourFilter.disableBehaviour(facetRoot, ContentModel.ASPECT_AUDITABLE); behaviourFilter.disableBehaviour(facetRoot, ContentModel.ASPECT_AUDITABLE);
try try
{ {
Map<QName, Serializable> properties = createNodeProperties(facetProperties, true); Map<QName, Serializable> properties = createNodeProperties(facetProperties);
// We don't want the node to be indexed // We don't want the node to be indexed
properties.put(ContentModel.PROP_IS_INDEXED, false); properties.put(ContentModel.PROP_IS_INDEXED, false);
NodeRef ref = nodeService.createNode(facetRoot, ContentModel.ASSOC_CONTAINS, NodeRef ref = nodeService.createNode(facetRoot, ContentModel.ASSOC_CONTAINS,
@@ -355,7 +386,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
NodeRef facetNodeRef = getFacetNodeRef(filterID); NodeRef facetNodeRef = getFacetNodeRef(filterID);
if (facetNodeRef == null) if (facetNodeRef == null)
{ {
SolrFacetProperties fp = getFacet(filterID); SolrFacetProperties fp = getDefaultLoadedFacet(filterID);
if (fp != null) if (fp != null)
{ {
// As we don't create nodes for the bootstrapped FP on server // As we don't create nodes for the bootstrapped FP on server
@@ -370,7 +401,12 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
} }
else else
{ {
Map<QName, Serializable> properties = createNodeProperties(facetProperties, false); String name = (String) nodeService.getProperty(facetNodeRef, ContentModel.PROP_NAME);
if (!filterID.equals(name))
{
throw new SolrFacetConfigException("The filterID cannot be renamed.");
}
Map<QName, Serializable> properties = createNodeProperties(facetProperties);
// Set the updated properties back onto the facet node reference // Set the updated properties back onto the facet node reference
this.nodeService.setProperties(facetNodeRef, properties); this.nodeService.setProperties(facetNodeRef, properties);
} }
@@ -384,17 +420,16 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
public void deleteFacet(String filterID) public void deleteFacet(String filterID)
{ {
NodeRef facetNodeRef = getFacetNodeRef(filterID); NodeRef facetNodeRef = getFacetNodeRef(filterID);
SolrFacetProperties defaultFP = facetConfig.getDefaultFacets().get(filterID); if (facetNodeRef == null)
if(defaultFP != null)
{
throw new SolrFacetConfigException("The default [" + filterID + "] facet cannot be deleted. It can only be disabled.");
}
if(facetNodeRef == null)
{ {
throw new SolrFacetConfigException("The [" + filterID + "] facet cannot be found."); throw new SolrFacetConfigException("The [" + filterID + "] facet cannot be found.");
} }
SolrFacetProperties defaultFP = getDefaultLoadedFacet(filterID);
if (defaultFP != null)
{
throw new SolrFacetConfigException("The default [" + filterID + "] facet cannot be deleted. It can only be disabled.");
}
nodeService.deleteNode(facetNodeRef); nodeService.deleteNode(facetNodeRef);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -402,19 +437,17 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
} }
} }
private Map<QName, Serializable> createNodeProperties(SolrFacetProperties facetProperties, boolean withFilterId) private Map<QName, Serializable> createNodeProperties(SolrFacetProperties facetProperties)
{ {
if (facetProperties.getFilterID() == null) if (facetProperties.getFilterID() == null)
{ {
throw new SolrFacetConfigException("Filter Id cannot be null."); throw new SolrFacetConfigException("Filter Id cannot be null.");
} }
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(14); Set<CustomProperties> customProperties = facetProperties.getCustomProperties();
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(14 + customProperties.size());
if (withFilterId) properties.put(ContentModel.PROP_NAME, facetProperties.getFilterID());
{
properties.put(ContentModel.PROP_NAME, facetProperties.getFilterID());
}
properties.put(SolrFacetModel.PROP_FIELD_TYPE, facetProperties.getFacetQName()); properties.put(SolrFacetModel.PROP_FIELD_TYPE, facetProperties.getFacetQName());
properties.put(SolrFacetModel.PROP_FIELD_LABEL, facetProperties.getDisplayName()); properties.put(SolrFacetModel.PROP_FIELD_LABEL, facetProperties.getDisplayName());
properties.put(SolrFacetModel.PROP_DISPLAY_CONTROL, facetProperties.getDisplayControl()); properties.put(SolrFacetModel.PROP_DISPLAY_CONTROL, facetProperties.getDisplayControl());
@@ -427,9 +460,14 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
properties.put(SolrFacetModel.PROP_INDEX, facetProperties.getIndex()); properties.put(SolrFacetModel.PROP_INDEX, facetProperties.getIndex());
properties.put(SolrFacetModel.PROP_IS_ENABLED, facetProperties.isEnabled()); properties.put(SolrFacetModel.PROP_IS_ENABLED, facetProperties.isEnabled());
SolrFacetProperties fp = facetConfig.getDefaultFacets().get(facetProperties.getFilterID()); SolrFacetProperties fp = getDefaultLoadedFacet(facetProperties.getFilterID());
properties.put(SolrFacetModel.PROP_IS_DEFAULT, (fp == null) ? false : fp.isDefault()); properties.put(SolrFacetModel.PROP_IS_DEFAULT, (fp == null) ? false : fp.isDefault());
for (CustomProperties cp : customProperties)
{
properties.put(cp.getName(), cp.getValue());
}
return properties; return properties;
} }
@@ -468,7 +506,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
return facetHomeRef; return facetHomeRef;
} }
@Override @Override
protected void onBootstrap(ApplicationEvent event) protected void onBootstrap(ApplicationEvent event)
{ {
@@ -478,46 +515,89 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
SolrFacetModel.TYPE_FACET_FIELD, SolrFacetModel.TYPE_FACET_FIELD,
new JavaBehaviour(this, "onCreateNode")); new JavaBehaviour(this, "onCreateNode"));
// Filter before update
this.policyComponent.bindClassBehaviour(
BeforeUpdateNodePolicy.QNAME,
SolrFacetModel.TYPE_FACET_FIELD,
new JavaBehaviour(this, "beforeUpdateNode"));
// Filter update // Filter update
this.policyComponent.bindClassBehaviour( this.policyComponent.bindClassBehaviour(
OnUpdateNodePolicy.QNAME, OnUpdateNodePolicy.QNAME,
SolrFacetModel.TYPE_FACET_FIELD, SolrFacetModel.TYPE_FACET_FIELD,
new JavaBehaviour(this, "onUpdateNode")); new JavaBehaviour(this, "onUpdateNode"));
// Filter deletion // Filter before deletion
this.policyComponent.bindClassBehaviour( this.policyComponent.bindClassBehaviour(
BeforeDeleteNodePolicy.QNAME, BeforeDeleteNodePolicy.QNAME,
SolrFacetModel.TYPE_FACET_FIELD, SolrFacetModel.TYPE_FACET_FIELD,
new JavaBehaviour(this, "beforeDeleteNode")); new JavaBehaviour(this, "beforeDeleteNode"));
Map<String, SolrFacetProperties> mergedMap = new HashMap<>(100);
// Loaded facets
Map<String, SolrFacetProperties> defaultFP = facetConfig.getDefaultFacets(); Map<String, SolrFacetProperties> defaultFP = facetConfig.getDefaultFacets();
for(Entry<String, SolrFacetProperties> fpEntry : defaultFP.entrySet()) mergedMap.putAll(defaultFP);
{
facetsMap.put(fpEntry.getKey(), fpEntry.getValue());
}
List<SolrFacetProperties> persistedProperties = getPersistedFacetProperties(); // Persisted facets
Map<String, SolrFacetProperties> persistedProperties = getPersistedFacetProperties();
// The persisted facets will override the default facets // The persisted facets will override the default facets
for(SolrFacetProperties fp : persistedProperties) mergedMap.putAll(persistedProperties);
// Sort the merged maps
Map<String, SolrFacetProperties> sortedMap = CollectionUtils.sortMapByValue(mergedMap, getIndextComparator());
LinkedList<SolrFacetProperties> 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)
{ {
facetsMap.put(fp.getFilterID(), fp); 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) if (logger.isDebugEnabled() && persistedProperties.size() > 0)
{ {
logger.debug("The facets [" + persistedProperties + "] have overridden their matched default facets."); logger.debug("The facets [" + persistedProperties + "] have overridden their matched default facets.");
} }
} }
private List<SolrFacetProperties> getPersistedFacetProperties() private Map<String, SolrFacetProperties> getPersistedFacetProperties()
{ {
List<ChildAssociationRef> list = nodeService.getChildAssocs(getFacetsRoot()); List<ChildAssociationRef> list = nodeService.getChildAssocs(getFacetsRoot());
List<SolrFacetProperties> facets = new ArrayList<>(list.size()); Map<String, SolrFacetProperties> facets = new HashMap<>(list.size());
for (ChildAssociationRef associationRef : list) for (ChildAssociationRef associationRef : list)
{ {
SolrFacetProperties fp = getFacetProperties(associationRef.getChildRef()); SolrFacetProperties fp = getFacetProperties(associationRef.getChildRef());
facets.add(fp); facets.put(fp.getFilterID(), fp);
} }
return facets; return facets;
} }
@@ -528,18 +608,26 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
// nothing to do // 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 @Override
public void onUpdateNode(NodeRef nodeRef) public void onUpdateNode(NodeRef nodeRef)
{ {
SolrFacetProperties fp = getFacetProperties(nodeRef); SolrFacetProperties fp = getFacetProperties(nodeRef);
this.facetsMap.put(fp.getFilterID(), fp); this.facetsMap.put(fp.getIndex(), fp);
} }
@Override @Override
public void onCreateNode(ChildAssociationRef childAssocRef) public void onCreateNode(ChildAssociationRef childAssocRef)
{ {
SolrFacetProperties fp = getFacetProperties(childAssocRef.getChildRef()); SolrFacetProperties fp = getFacetProperties(childAssocRef.getChildRef());
this.facetsMap.put(fp.getFilterID(), fp); this.facetsMap.put(fp.getIndex(), fp);
this.facetNodeRefCache.put(fp.getFilterID(), childAssocRef.getChildRef()); this.facetNodeRefCache.put(fp.getFilterID(), childAssocRef.getChildRef());
} }
@@ -547,7 +635,106 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
public void beforeDeleteNode(NodeRef nodeRef) public void beforeDeleteNode(NodeRef nodeRef)
{ {
String filterID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String filterID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
this.facetsMap.remove(filterID); int index = (Integer) nodeService.getProperty(nodeRef, SolrFacetModel.PROP_INDEX);
this.facetsMap.remove(index);
this.facetNodeRefCache.remove(filterID); this.facetNodeRefCache.remove(filterID);
} }
/**
* Note: this comparator imposes orderings that are inconsistent with equals
* method of the {@link SolrFacetProperties}."
*
* @return
*/
private Comparator<Entry<String, SolrFacetProperties>> getIndextComparator()
{
return new Comparator<Entry<String, SolrFacetProperties>>()
{
public int compare(Entry<String, SolrFacetProperties> facet1,
Entry<String, SolrFacetProperties> facet2)
{
return Integer.compare(facet1.getValue().getIndex(), facet2.getValue().getIndex());
}
};
}
@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
*
* @return Map<QName, Serializable> map containing the custom properties of the facet
*/
private Map<QName, Serializable> getFacetCustomProperties(Map<QName, Serializable> properties)
{
Map<QName, Serializable> customProperties = new HashMap<QName, Serializable>(5);
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
{
if (SolrFacetModel.SOLR_FACET_CUSTOM_PROPERTY_URL.equals(entry.getKey().getNamespaceURI()))
{
customProperties.put(entry.getKey(), entry.getValue());
}
}
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<Integer, SolrFacetProperties> 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);
}
}
}
} }