mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -19,15 +19,19 @@
|
||||
|
||||
package org.alfresco.repo.search.impl.solr.facet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
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.QName;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
@@ -47,6 +51,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
* <ul>
|
||||
* <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.displayControl=alfresco/search/FacetFilters</li>
|
||||
* <li>custom.cm\:content.mimetype.maxFilters=5</li>
|
||||
* <li>custom.cm\:content.mimetype.hitThreshold=1</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.isEnabled=true</li>
|
||||
* </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/>
|
||||
* <b>${solr_facets.inheritanceHierarchy}</b><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 String KEY_EXTRA_INFO = ".EXTRA-PROP.";
|
||||
private static final int KEY_EXTRA_INFO_LENGTH = KEY_EXTRA_INFO.length();
|
||||
|
||||
private final Properties rawProperties;
|
||||
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())
|
||||
{
|
||||
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
|
||||
Map<String, SolrFacetProperties> facetProperties = new HashMap<>(100);
|
||||
for (String field : facetFields)
|
||||
for (String field : facetFields.keySet())
|
||||
{
|
||||
// FacetProperty attributes
|
||||
// Resolve facet field into QName
|
||||
@@ -186,7 +224,12 @@ public class SolrFacetConfig extends AbstractLifecycleBean
|
||||
String scope = propValues.get(ValueName.PROP_SCOPE.getPropValueName(field));
|
||||
Set<String> 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)));
|
||||
Set<CustomProperties> customProps = getCustomProps(facetFields.get(field), field, propValues);
|
||||
|
||||
// Construct the FacetProperty object
|
||||
SolrFacetProperties fp = new SolrFacetProperties.Builder()
|
||||
@@ -202,8 +245,9 @@ public class SolrFacetConfig extends AbstractLifecycleBean
|
||||
.index(index)
|
||||
.isEnabled(isEnabled)
|
||||
.isDefault(true)
|
||||
.scopedSites(scopedSites).build();
|
||||
|
||||
.scopedSites(scopedSites)
|
||||
.customProperties(customProps).build();
|
||||
|
||||
facetProperties.put(filterID, fp);
|
||||
}
|
||||
|
||||
@@ -283,6 +327,35 @@ public class SolrFacetConfig extends AbstractLifecycleBean
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@@ -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 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 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_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_DEFAULT = QName.createQName(SOLR_FACET_MODEL_URL, "isDefault");
|
||||
|
||||
public static final QName PROP_EXTRA_INFORMATION = QName.createQName(SOLR_FACET_CUSTOM_PROPERTY_URL, "extraInformation");
|
||||
}
|
||||
|
@@ -19,20 +19,23 @@
|
||||
|
||||
package org.alfresco.repo.search.impl.solr.facet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
|
||||
/**
|
||||
* Domain-Specific Language (DSL) style builder class for encapsulating the
|
||||
* facet properties.
|
||||
* Domain-Specific Language (DSL) style builder class for encapsulating the facet properties.
|
||||
*
|
||||
* @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 QName facetQName;
|
||||
private final String displayName;
|
||||
@@ -46,6 +49,7 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
private final int index;
|
||||
private final boolean isEnabled;
|
||||
private final boolean isDefault; // is loaded from properties files?
|
||||
private final Set<CustomProperties> customProperties;
|
||||
|
||||
/**
|
||||
* Initialises a newly created <code>SolrFacetProperty</code> object
|
||||
@@ -66,7 +70,8 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
this.index = builder.index;
|
||||
this.isEnabled = builder.isEnabled;
|
||||
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
|
||||
*/
|
||||
public Set<String> getScopedSites()
|
||||
{
|
||||
if (this.scopedSites == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Collections.unmodifiableSet(new HashSet<String>(this.scopedSites));
|
||||
}
|
||||
|
||||
@@ -181,6 +182,17 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
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()
|
||||
*/
|
||||
@@ -226,22 +238,13 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
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()
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(320);
|
||||
StringBuilder sb = new StringBuilder(400);
|
||||
sb.append("FacetProperty [filterID=").append(this.filterID).append(", facetQName=")
|
||||
.append(this.facetQName).append(", displayName=").append(this.displayName)
|
||||
.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(this.sortBy).append(", scope=").append(this.scope).append(", scopedSites=")
|
||||
.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();
|
||||
}
|
||||
|
||||
@@ -264,10 +268,38 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
private int minFilterValueLength;
|
||||
private String sortBy;
|
||||
private String scope;
|
||||
private Set<String> scopedSites;
|
||||
private Set<String> scopedSites = Collections.emptySet();
|
||||
private int index;
|
||||
private boolean isEnabled;
|
||||
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)
|
||||
{
|
||||
@@ -325,7 +357,10 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
|
||||
public Builder scopedSites(Set<String> scopedSites)
|
||||
{
|
||||
this.scopedSites = scopedSites;
|
||||
if (scopedSites != null)
|
||||
{
|
||||
this.scopedSites = scopedSites;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -347,9 +382,106 @@ public class SolrFacetProperties implements Comparable<SolrFacetProperties>
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customProperties(Set<CustomProperties> customProperties)
|
||||
{
|
||||
if (customProperties != null)
|
||||
{
|
||||
this.customProperties = customProperties;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SolrFacetProperties build()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
package org.alfresco.repo.search.impl.solr.facet;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
@@ -34,10 +34,9 @@ public interface SolrFacetService
|
||||
/**
|
||||
* Gets all the available facets.
|
||||
*
|
||||
* @return Map of {@code SolrFacetProperties} with the
|
||||
* {@code SolrFacetProperties.filterID} as the key or an empty map if none exists
|
||||
* @return List of {@code SolrFacetProperties} or an empty list if none exists
|
||||
*/
|
||||
public Map<String, SolrFacetProperties> getFacets();
|
||||
public List<SolrFacetProperties> getFacets();
|
||||
|
||||
/**
|
||||
* Gets the facet by filter Id.
|
||||
@@ -86,4 +85,6 @@ public interface SolrFacetService
|
||||
* @param filterID the filter Id
|
||||
*/
|
||||
public void deleteFacet(String filterID);
|
||||
|
||||
public int getNextIndex();
|
||||
}
|
||||
|
@@ -21,24 +21,30 @@ package org.alfresco.repo.search.impl.solr.facet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
|
||||
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;
|
||||
import org.alfresco.repo.search.impl.solr.facet.SolrFacetProperties.CustomProperties;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
@@ -60,14 +66,13 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrFacetService,
|
||||
NodeServicePolicies.OnCreateNodePolicy,
|
||||
NodeServicePolicies.OnUpdateNodePolicy,
|
||||
NodeServicePolicies.BeforeDeleteNodePolicy
|
||||
NodeServicePolicies.BeforeDeleteNodePolicy,
|
||||
NodeServicePolicies.BeforeUpdateNodePolicy
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(SolrFacetServiceImpl.class);
|
||||
/**
|
||||
@@ -76,7 +81,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
private static final String ALFRESCO_SEARCH_ADMINISTRATORS_AUTHORITY = "ALFRESCO_SEARCH_ADMINISTRATORS";
|
||||
private static final String GROUP_ALFRESCO_SEARCH_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX
|
||||
+ ALFRESCO_SEARCH_ADMINISTRATORS_AUTHORITY;
|
||||
|
||||
|
||||
/** The store where facets are kept */
|
||||
private static final StoreRef FACET_STORE = new StoreRef("workspace://SpacesStore");
|
||||
|
||||
@@ -87,12 +92,13 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
private RetryingTransactionHelper retryingTransactionHelper;
|
||||
private BehaviourFilter behaviourFilter;
|
||||
private PolicyComponent policyComponent;
|
||||
private SolrFacetConfig facetConfig;
|
||||
private SolrFacetConfig facetConfig;
|
||||
private String facetsRootXPath;
|
||||
private SimpleCache<String, Object> singletonCache; // eg. for facetsHomeNodeRef
|
||||
private final String KEY_FACETS_HOME_NODEREF = "key.facetshome.noderef";
|
||||
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
|
||||
@@ -182,6 +188,14 @@ 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)
|
||||
{
|
||||
@@ -194,16 +208,26 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, SolrFacetProperties> getFacets()
|
||||
public List<SolrFacetProperties> getFacets()
|
||||
{
|
||||
Map<String, SolrFacetProperties> sortedMap = CollectionUtils.sortMapByValue(facetsMap);
|
||||
return sortedMap;
|
||||
return new ArrayList<>(facetsMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -268,6 +292,12 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
List<String> scSites = (List<String>) properties.get(SolrFacetModel.PROP_SCOPED_SITES);
|
||||
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
|
||||
SolrFacetProperties fp = new SolrFacetProperties.Builder()
|
||||
.filterID(filterID)
|
||||
@@ -282,7 +312,8 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
.index(index)
|
||||
.isEnabled(isEnabled)
|
||||
.isDefault(isDefault)
|
||||
.scopedSites(scopedSites).build();
|
||||
.scopedSites(scopedSites)
|
||||
.customProperties(extraProps).build();
|
||||
|
||||
return fp;
|
||||
}
|
||||
@@ -298,9 +329,9 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
|
||||
final String filterID = facetProperties.getFilterID();
|
||||
NodeRef facetNodeRef = getFacetNodeRef(filterID);
|
||||
// We need to check the bootstrapped Facet properties 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 && getFacet(filterID) != null))
|
||||
// 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))
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
return facetNodeRef = AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
|
||||
return facetNodeRef = AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
|
||||
{
|
||||
@Override
|
||||
public NodeRef doWork() throws Exception
|
||||
@@ -324,7 +355,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
behaviourFilter.disableBehaviour(facetRoot, ContentModel.ASPECT_AUDITABLE);
|
||||
try
|
||||
{
|
||||
Map<QName, Serializable> properties = createNodeProperties(facetProperties, true);
|
||||
Map<QName, Serializable> properties = createNodeProperties(facetProperties);
|
||||
// We don't want the node to be indexed
|
||||
properties.put(ContentModel.PROP_IS_INDEXED, false);
|
||||
NodeRef ref = nodeService.createNode(facetRoot, ContentModel.ASSOC_CONTAINS,
|
||||
@@ -355,12 +386,12 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
NodeRef facetNodeRef = getFacetNodeRef(filterID);
|
||||
if (facetNodeRef == null)
|
||||
{
|
||||
SolrFacetProperties fp = getFacet(filterID);
|
||||
SolrFacetProperties fp = getDefaultLoadedFacet(filterID);
|
||||
if (fp != null)
|
||||
{
|
||||
// As we don't create nodes for the bootstrapped FP on server
|
||||
// startup, we need to create a node here, when a user tries to
|
||||
// update the default properties for the first time.
|
||||
// update the default properties for the first time.
|
||||
createFacetNodeImpl(facetProperties, false);
|
||||
}
|
||||
else
|
||||
@@ -370,7 +401,12 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
}
|
||||
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
|
||||
this.nodeService.setProperties(facetNodeRef, properties);
|
||||
}
|
||||
@@ -384,17 +420,16 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
public void deleteFacet(String filterID)
|
||||
{
|
||||
NodeRef facetNodeRef = getFacetNodeRef(filterID);
|
||||
SolrFacetProperties defaultFP = facetConfig.getDefaultFacets().get(filterID);
|
||||
if(defaultFP != null)
|
||||
{
|
||||
throw new SolrFacetConfigException("The default [" + filterID + "] facet cannot be deleted. It can only be disabled.");
|
||||
}
|
||||
|
||||
if(facetNodeRef == null)
|
||||
if (facetNodeRef == null)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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_LABEL, facetProperties.getDisplayName());
|
||||
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_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());
|
||||
|
||||
for (CustomProperties cp : customProperties)
|
||||
{
|
||||
properties.put(cp.getName(), cp.getValue());
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -467,7 +505,6 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
}
|
||||
return facetHomeRef;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
@@ -478,46 +515,89 @@ 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 deletion
|
||||
// Filter before deletion
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
BeforeDeleteNodePolicy.QNAME,
|
||||
SolrFacetModel.TYPE_FACET_FIELD,
|
||||
new JavaBehaviour(this, "beforeDeleteNode"));
|
||||
|
||||
Map<String, SolrFacetProperties> mergedMap = new HashMap<>(100);
|
||||
// Loaded facets
|
||||
Map<String, SolrFacetProperties> defaultFP = facetConfig.getDefaultFacets();
|
||||
for(Entry<String, SolrFacetProperties> fpEntry : defaultFP.entrySet())
|
||||
{
|
||||
facetsMap.put(fpEntry.getKey(), fpEntry.getValue());
|
||||
}
|
||||
|
||||
List<SolrFacetProperties> persistedProperties = getPersistedFacetProperties();
|
||||
mergedMap.putAll(defaultFP);
|
||||
|
||||
// Persisted facets
|
||||
Map<String, SolrFacetProperties> persistedProperties = getPersistedFacetProperties();
|
||||
// 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)
|
||||
{
|
||||
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<SolrFacetProperties> facets = new ArrayList<>(list.size());
|
||||
Map<String, SolrFacetProperties> facets = new HashMap<>(list.size());
|
||||
for (ChildAssociationRef associationRef : list)
|
||||
{
|
||||
SolrFacetProperties fp = getFacetProperties(associationRef.getChildRef());
|
||||
facets.add(fp);
|
||||
facets.put(fp.getFilterID(), fp);
|
||||
}
|
||||
return facets;
|
||||
}
|
||||
@@ -528,18 +608,26 @@ 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.getFilterID(), fp);
|
||||
this.facetsMap.put(fp.getIndex(), fp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateNode(ChildAssociationRef childAssocRef)
|
||||
{
|
||||
SolrFacetProperties fp = getFacetProperties(childAssocRef.getChildRef());
|
||||
this.facetsMap.put(fp.getFilterID(), fp);
|
||||
this.facetsMap.put(fp.getIndex(), fp);
|
||||
this.facetNodeRefCache.put(fp.getFilterID(), childAssocRef.getChildRef());
|
||||
}
|
||||
|
||||
@@ -547,7 +635,106 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF
|
||||
public void beforeDeleteNode(NodeRef nodeRef)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user