diff --git a/config/alfresco/solr-facets-context.xml b/config/alfresco/solr-facets-context.xml index 9d96377336..3fb02a4460 100644 --- a/config/alfresco/solr-facets-context.xml +++ b/config/alfresco/solr-facets-context.xml @@ -27,6 +27,7 @@ + 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 2d61b5980e..013274909d 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,7 +20,15 @@ package org.alfresco.repo.search.impl.solr.facet; import java.util.List; +import java.util.Set; + +import org.alfresco.repo.dictionary.Facetable; +import org.alfresco.repo.search.impl.solr.facet.Exceptions.DuplicateFacetId; +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.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; /** * Solr Facet service configuration API. @@ -96,4 +104,21 @@ public interface SolrFacetService * @throws DuplicateFacetId if there is a duplicate filter ID in the list. */ public void reorderFacets(List filterIds); + + /** + * This method offers a convenient access point for getting all Facetable + * content properties defined in the repository. + * @return a collection of facetable {@link PropertyDefinition}s. + * @see Facetable + */ + public Set getFacetableProperties(); + + /** + * This method offers a convenient access point for getting all Facetable + * content properties defined on the specified content class (type or aspect). + * @param contentClass the QName of an aspect or type, whose facetable properties are sought. + * @return a collection of facetable {@link PropertyDefinition}s. + * @see Facetable + */ + public Set getFacetableProperties(QName contentClass); } 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 47867549b3..753baf2f9c 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 @@ -38,6 +38,7 @@ import java.util.concurrent.ConcurrentMap; import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.dictionary.Facetable; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; @@ -53,6 +54,9 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -91,6 +95,7 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF private static final StoreRef FACET_STORE = new StoreRef("workspace://SpacesStore"); private AuthorityService authorityService; + private DictionaryService dictionaryService; protected NodeService nodeService; private NamespaceService namespaceService; private SearchService searchService; @@ -112,6 +117,14 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF this.authorityService = authorityService; } + /** + * @param dictionaryService the dictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** * @param nodeService the nodeService to set */ @@ -755,4 +768,84 @@ public class SolrFacetServiceImpl extends AbstractLifecycleBean implements SolrF nodeService.setProperty(getFacetsRoot(), SolrFacetModel.PROP_FACET_ORDER, serializableProp); } } + + @Override public Set getFacetableProperties() + { + final Set result = new HashSet<>(); + + final List allContentClasses = CollectionUtils.flatten(dictionaryService.getAllAspects(), dictionaryService.getAllTypes()); + + for (QName contentClass : allContentClasses) + { + result.addAll(getFacetableProperties(contentClass)); + } + + return result; + } + + @Override public Set getFacetableProperties(QName contentClass) + { + final Set result = new HashSet<>(); + + final Map propertyDefs = dictionaryService.getPropertyDefs(contentClass); + + if (propertyDefs != null) + { + for (Map.Entry prop : propertyDefs.entrySet()) + { + final Facetable propIsFacetable = prop.getValue().getFacetable(); + + switch (propIsFacetable) + { + case TRUE: + result.add(prop.getValue()); + break; + case FALSE: + // The value is not facetable. Do nothing. + break; + case UNSET: + // These values may be facetable. + final DataTypeDefinition datatype = prop.getValue().getDataType(); + if (isNumeric(datatype) || isDateLike(datatype) || isFacetableText(datatype)) + { + result.add(prop.getValue()); + break; + } + break; + default: + // This should never happen. If it does, it's a programming error. + throw new IllegalStateException("Failed to handle " + Facetable.class.getSimpleName() + " type: " + propIsFacetable); + } + } + } + + return result; + } + + // TODO Consider moving into dictionary code. + private boolean isNumeric(DataTypeDefinition datatype) + { + boolean result; + try + { + Class clazz = Class.forName(datatype.getJavaClassName()); + result = Number.class.isAssignableFrom(clazz); + } catch (ClassNotFoundException e) + { + result = false; + } + return result; + } + + private boolean isDateLike(DataTypeDefinition datatype) + { + return DataTypeDefinition.DATE.equals(datatype.getName()) || + DataTypeDefinition.DATETIME.equals(datatype.getName()); + } + + private boolean isFacetableText(DataTypeDefinition datatype) + { + // For now at least, we're excluding MLTEXT + return DataTypeDefinition.TEXT.equals(datatype.getName()); + } }