mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud)
70675: Merged WAT1 (4.3/Cloud) to HEAD-BUG-FIX (4.3/Cloud) 68557: ACE-1265: added Created date, Modified date, and Content size bucketing. Also, refactored the facet display label implementation. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@70906 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -348,4 +348,10 @@ public class CMISResultSetImpl implements CMISResultSet, Serializable
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
@@ -353,4 +353,10 @@ public class CMISResultSet implements ResultSetSPI<CMISResultSetRow, CMISResultS
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
@@ -26,14 +26,17 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.repo.search.impl.solr.facet.SolrFacetHelper;
|
||||
import org.alfresco.repo.search.impl.solr.facet.SolrFacetHelper.FacetLabelDisplayHandler;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
@@ -48,6 +51,7 @@ import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.util.ISO9075;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.dom4j.Document;
|
||||
@@ -56,6 +60,7 @@ import org.dom4j.io.SAXReader;
|
||||
import org.jaxen.saxpath.base.XPathReader;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.extensions.surf.util.ParameterCheck;
|
||||
|
||||
/**
|
||||
@@ -71,7 +76,7 @@ import org.springframework.extensions.surf.util.ParameterCheck;
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class Search extends BaseScopableProcessorExtension
|
||||
public class Search extends BaseScopableProcessorExtension implements InitializingBean
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(Search.class);
|
||||
|
||||
@@ -85,7 +90,18 @@ public class Search extends BaseScopableProcessorExtension
|
||||
protected Repository repository;
|
||||
|
||||
private SwitchableApplicationContextFactory searchSubsystem;
|
||||
|
||||
/** Solr facet helper */
|
||||
private SolrFacetHelper solrFacetHelper;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
PropertyCheck.mandatory(this, "services", services);
|
||||
|
||||
this.solrFacetHelper = new SolrFacetHelper(services);
|
||||
}
|
||||
/**
|
||||
* Set the default store reference
|
||||
*
|
||||
@@ -694,6 +710,12 @@ public class Search extends BaseScopableProcessorExtension
|
||||
{
|
||||
sp.addFieldFacet(new FieldFacet("@" + field));
|
||||
}
|
||||
|
||||
List<String> facetQueries = solrFacetHelper.getFacetQueries();
|
||||
for (String fq : facetQueries)
|
||||
{
|
||||
sp.addFacetQuery(fq);
|
||||
}
|
||||
}
|
||||
|
||||
// error handling opions
|
||||
@@ -905,15 +927,47 @@ public class Search extends BaseScopableProcessorExtension
|
||||
if (f.getSecond() > 0)
|
||||
{
|
||||
String facetValue = f.getFirst();
|
||||
Field field = getFieldType(ff.getField());
|
||||
String label = (field == null) ? facetValue : field.getLabel(services, facetValue);
|
||||
FacetLabelDisplayHandler handler = solrFacetHelper.getDisplayHandler(ff.getField());
|
||||
String label = (handler == null) ? facetValue : handler.getDisplayLabel(facetValue).getSecond();
|
||||
|
||||
facets.add(new ScriptFacetResult(facetValue, label, f.getSecond()));
|
||||
}
|
||||
}
|
||||
// store facet results per field
|
||||
facetMeta.put(ff.getField(), facets);
|
||||
}
|
||||
|
||||
Set<Entry<String, Integer>> facetQueries = results.getFacetQueries().entrySet();
|
||||
Map<String, List<ScriptFacetResult>> facetQueryMeta = new HashMap<>(facetQueries.size());
|
||||
for(Entry<String, Integer> entry : facetQueries)
|
||||
{
|
||||
// ignore zero hit facet queries
|
||||
if (entry.getValue() > 0)
|
||||
{
|
||||
String key = entry.getKey();
|
||||
// for example the key could be: {!afts}@{http://www.alfresco.org/model/content/1.0}created:[2013-10-29 TO 2014-04-29]
|
||||
// qName => @{http://www.alfresco.org/model/content/1.0}created
|
||||
// 7 => {!afts}
|
||||
String qName = key.substring(7, key.lastIndexOf(':'));
|
||||
|
||||
// Retrieve the previous facet queries
|
||||
List<ScriptFacetResult> fqs = facetQueryMeta.get(qName);
|
||||
if (fqs == null)
|
||||
{
|
||||
fqs = new ArrayList<>();
|
||||
}
|
||||
FacetLabelDisplayHandler handler = solrFacetHelper.getDisplayHandler(qName);
|
||||
Pair<String, String> valueLabelPair = (handler == null) ? new Pair<String, String>(qName,
|
||||
key.substring(qName.length(), key.length())) : handler.getDisplayLabel(key);
|
||||
|
||||
fqs.add(new ScriptFacetResult(valueLabelPair.getFirst(), valueLabelPair.getSecond(), entry.getValue()));
|
||||
|
||||
// store facet query results per field
|
||||
facetQueryMeta.put(qName, fqs);
|
||||
}
|
||||
}
|
||||
meta.put("facets", facetMeta);
|
||||
meta.put("facetQueries", facetQueryMeta);
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
@@ -961,83 +1015,4 @@ public class Search extends BaseScopableProcessorExtension
|
||||
public String column;
|
||||
public boolean asc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private enum Field
|
||||
{
|
||||
CREATOR("creator.__"), MODIFIER("modifier.__"), MIMETYPE("content.mimetype")
|
||||
{
|
||||
@Override
|
||||
/*Package access level*/
|
||||
String getLabel(ServiceRegistry services, String facetValue)
|
||||
{
|
||||
MimetypeService mimetypeService = services.getMimetypeService();
|
||||
Map<String, String> mimetypes = mimetypeService.getDisplaysByMimetype();
|
||||
String displayName = mimetypes.get(facetValue);
|
||||
return displayName == null ? facetValue : displayName.trim();
|
||||
}
|
||||
};
|
||||
|
||||
private Field(String facetField)
|
||||
{
|
||||
this.facetField = facetField;
|
||||
}
|
||||
|
||||
private String facetField;
|
||||
|
||||
private String getFacetField()
|
||||
{
|
||||
return facetField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation which will return the full user name from
|
||||
* the facetValue, if the facetValue represent a userID
|
||||
*
|
||||
* @param services the ServiceRegistry
|
||||
* @param facetValue the facet value
|
||||
* @return the full user name. If the user doesn't exist then, the
|
||||
* {@code facetValue} will be returned.
|
||||
*/
|
||||
/*Package access level*/
|
||||
String getLabel(ServiceRegistry services, String facetValue)
|
||||
{
|
||||
String name = null;
|
||||
|
||||
final NodeRef personRef = services.getPersonService().getPersonOrNull(facetValue);
|
||||
if (personRef != null)
|
||||
{
|
||||
final NodeService nodeService = services.getNodeService();
|
||||
final String firstName = (String) nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
|
||||
final String lastName = (String) nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
|
||||
name = (firstName != null ? firstName + " " : "") + (lastName != null ? lastName : "");
|
||||
}
|
||||
return name == null ? facetValue : name.trim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the facet field.
|
||||
*
|
||||
* @param facetField the facet field value
|
||||
* @return the Field type
|
||||
*/
|
||||
private Field getFieldType(String facetField)
|
||||
{
|
||||
if (facetField == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Field val : Field.values())
|
||||
{
|
||||
if (facetField.endsWith(val.getFacetField()))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ package org.alfresco.repo.search;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -121,5 +122,10 @@ public abstract class AbstractResultSet implements ResultSet
|
||||
return Collections.<Pair<String, Integer>>emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -164,4 +165,10 @@ public class EmptyResultSet implements ResultSet
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.search.impl.querymodel.Query;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
@@ -250,4 +251,10 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
{
|
||||
return wrapped.getNumberFound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return wrapped.getFacetQueries();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.domain.node.NodeDAO;
|
||||
import org.alfresco.repo.search.SimpleResultSetMetaData;
|
||||
@@ -72,6 +73,8 @@ public class SolrJSONResultSet implements ResultSet
|
||||
|
||||
private HashMap<String, List<Pair<String, Integer>>> fieldFacets = new HashMap<String, List<Pair<String, Integer>>>(1);
|
||||
|
||||
private Map<String, Integer> facetQueries = new HashMap<String, Integer>();
|
||||
|
||||
private NodeDAO nodeDao;
|
||||
|
||||
private long lastIndexedTxId;
|
||||
@@ -143,6 +146,16 @@ public class SolrJSONResultSet implements ResultSet
|
||||
if(json.has("facet_counts"))
|
||||
{
|
||||
JSONObject facet_counts = json.getJSONObject("facet_counts");
|
||||
if(facet_counts.has("facet_queries"))
|
||||
{
|
||||
JSONObject facet_queries = facet_counts.getJSONObject("facet_queries");
|
||||
for(Iterator it = facet_queries.keys(); it.hasNext(); /**/)
|
||||
{
|
||||
String fq = (String) it.next();
|
||||
Integer count =Integer.parseInt(facet_queries.getString(fq));
|
||||
facetQueries.put(fq, count);
|
||||
}
|
||||
}
|
||||
if(facet_counts.has("facet_fields"))
|
||||
{
|
||||
JSONObject facet_fields = facet_counts.getJSONObject("facet_fields");
|
||||
@@ -391,4 +404,10 @@ public class SolrJSONResultSet implements ResultSet
|
||||
{
|
||||
return lastIndexedTxId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return Collections.unmodifiableMap(facetQueries);
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -348,6 +347,10 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
|
||||
}
|
||||
|
||||
}
|
||||
for(String facetQuery : searchParameters.getFacetQueries())
|
||||
{
|
||||
url.append("&facet.query=").append(encoder.encode("{!afts}"+facetQuery, "UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
// end of field factes
|
||||
|
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.solr.facet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* A helper class to overcome the limitation of Solr 1.4 for dealing with facets.
|
||||
* <p>
|
||||
* Notice: probably this class or most of its functionalities will be removed
|
||||
* when we upgrade to Solr 4
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
// TODO use Solr4 date math for date buckets...
|
||||
public class SolrFacetHelper
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(SolrFacetHelper.class);
|
||||
|
||||
private static final String FQ_NS_PREFIX = "@{http://www.alfresco.org/model/content/1.0}";
|
||||
private static final String CREATED_FACET_QUERY_PREFIX = FQ_NS_PREFIX + "created:";
|
||||
private static final String MODIFIED_FACET_QUERY_PREFIX = FQ_NS_PREFIX + "modified:";
|
||||
private static final String CONTENT_SIZE_FACET_QUERY_PREFIX = FQ_NS_PREFIX + "content.size:";
|
||||
|
||||
// Content size buckets
|
||||
private static final int KB = 1024;
|
||||
private static final int MB = KB * 1024;
|
||||
private static final int TINY = 10 * KB;
|
||||
private static final int SMALL = 100 * KB;
|
||||
private static final int MEDIUM = MB;
|
||||
private static final int LARGE = 16 * MB;
|
||||
private static final int HUGE = 128 * MB;
|
||||
|
||||
private static final String SIZE_BUCKETS_CACHE_KEY = "sizeBucketsCacheKey";
|
||||
|
||||
/** Facet value and facet query display label handlers */
|
||||
private Map<String, FacetLabelDisplayHandler> displayHandlers;
|
||||
|
||||
/** Thread safe cache for storing the Date buckets facet query */
|
||||
private BucketsCache<LocalDate, List<String>> fqDateCache = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param serviceRegistry
|
||||
*/
|
||||
public SolrFacetHelper(ServiceRegistry serviceRegistry)
|
||||
{
|
||||
this.fqDateCache = new BucketsCache<>(new FacetQueryParamDateBuckets());
|
||||
this.displayHandlers = new HashMap<>(6);
|
||||
|
||||
UserNameDisplayHandler userNameDisplayHandler = new UserNameDisplayHandler(serviceRegistry);
|
||||
MimetypeDisplayHandler mimetypeDisplayHandler = new MimetypeDisplayHandler(serviceRegistry);
|
||||
DateBucketsDisplayHandler createdDateBucketsDisplayHandler = new DateBucketsDisplayHandler("cm:created");
|
||||
DateBucketsDisplayHandler modifiedDateBucketsDisplayHandler = new DateBucketsDisplayHandler("cm:modified");
|
||||
ContentSizeBucketsDisplayHandler contentSizeBucketsDisplayHandler = new ContentSizeBucketsDisplayHandler("cm:content.size");
|
||||
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}creator.__", userNameDisplayHandler);
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}modifier.__", userNameDisplayHandler);
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}content.mimetype", mimetypeDisplayHandler);
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}created", createdDateBucketsDisplayHandler);
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}modified", modifiedDateBucketsDisplayHandler);
|
||||
this.displayHandlers.put("@{http://www.alfresco.org/model/content/1.0}content.size", contentSizeBucketsDisplayHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the facet value and facet query display label handlers.
|
||||
*
|
||||
* @param displayHandlers the displayHandlers to set
|
||||
*/
|
||||
public void setDisplayHandlers(Map<String, FacetLabelDisplayHandler> displayHandlers)
|
||||
{
|
||||
this.displayHandlers = displayHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets predefined set of facet queries. Currently the facet queries are:
|
||||
* <li>Created date buckets</li>
|
||||
* <li>Modified date buckets</li>
|
||||
* <li>Content size buckets</li>
|
||||
*
|
||||
* @return list of facet queries
|
||||
*/
|
||||
public List<String> getFacetQueries()
|
||||
{
|
||||
List<String> facetQueries = new ArrayList<>();
|
||||
List<String> dateBuckets = null;
|
||||
|
||||
try
|
||||
{
|
||||
dateBuckets = fqDateCache.getRangeBuckets(LocalDate.now());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.error(
|
||||
"Error occured while trying to get the date buckets from the cache. Calculating the dates without the cache.", e);
|
||||
dateBuckets = makeDateBuckets(LocalDate.now());
|
||||
}
|
||||
|
||||
// Created and Modified dates facet queries
|
||||
for (String bucket : dateBuckets)
|
||||
{
|
||||
facetQueries.add(CREATED_FACET_QUERY_PREFIX + '[' + bucket + ']');
|
||||
facetQueries.add(MODIFIED_FACET_QUERY_PREFIX + '[' + bucket + ']');
|
||||
}
|
||||
|
||||
// Content size facet query
|
||||
for (String bucket : makeContentSizeBuckets())
|
||||
{
|
||||
facetQueries.add(CONTENT_SIZE_FACET_QUERY_PREFIX + '[' + bucket + ']');
|
||||
}
|
||||
|
||||
return facetQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate facet display label handler
|
||||
*
|
||||
* @param qName
|
||||
* @return the diplayHandler object or null if there is no handler
|
||||
* registered for the given @{code qName}
|
||||
*/
|
||||
public FacetLabelDisplayHandler getDisplayHandler(String qName)
|
||||
{
|
||||
return displayHandlers.get(qName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Date buckets. The dates are in ISO8601 format (yyyy-MM-dd)
|
||||
*
|
||||
* @return list of date ranges. e.g. "2014-04-28 TO 2014-04-29"
|
||||
*/
|
||||
private static List<String> makeDateBuckets(LocalDate currentDate)
|
||||
{
|
||||
List<String> list = new ArrayList<>(5);
|
||||
|
||||
String nowStr = " TO " + currentDate.toString();
|
||||
|
||||
// Bucket => yesterday TO today
|
||||
list.add(currentDate.minusDays(1).toString() + nowStr);
|
||||
|
||||
// Bucket => Last week TO today
|
||||
list.add(currentDate.minusWeeks(1).toString() + nowStr);
|
||||
|
||||
// Bucket => Last month TO today
|
||||
list.add(currentDate.minusMonths(1).toString() + nowStr);
|
||||
|
||||
// Bucket => Last 6 months TO today
|
||||
list.add(currentDate.minusMonths(6).toString() + nowStr);
|
||||
|
||||
// Bucket => Last year TO today
|
||||
list.add(currentDate.minusYears(1).toString() + nowStr);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates display name for the Date buckets.
|
||||
*
|
||||
* @return Map of {@literal <date range, display label key>}
|
||||
*/
|
||||
private static Map<String, String> makeDateBucketsDisplayLabel(LocalDate date)
|
||||
{
|
||||
List<String> dateBuckets = makeDateBuckets(date);
|
||||
Map<String, String> bucketDisplayName = new HashMap<>(5);
|
||||
|
||||
if (dateBuckets.size() != 5)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Date buckets size does not match the bucket display label size!");
|
||||
}
|
||||
|
||||
bucketDisplayName.put(dateBuckets.get(0), "faceted-search.date.one-day.label");
|
||||
bucketDisplayName.put(dateBuckets.get(1), "faceted-search.date.one-week.label");
|
||||
bucketDisplayName.put(dateBuckets.get(2), "faceted-search.date.one-month.label");
|
||||
bucketDisplayName.put(dateBuckets.get(3), "faceted-search.date.six-months.label");
|
||||
bucketDisplayName.put(dateBuckets.get(4), "faceted-search.date.one-year.label");
|
||||
|
||||
return bucketDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Content size buckets
|
||||
*
|
||||
* @return list of size ranges. e.g. "0 TO 1024"
|
||||
*/
|
||||
private static List<String> makeContentSizeBuckets()
|
||||
{
|
||||
List<String> sizeBuckets = new ArrayList<>(6);
|
||||
sizeBuckets.add("0 TO " + TINY);
|
||||
sizeBuckets.add(TINY + " TO " + SMALL);
|
||||
sizeBuckets.add(SMALL + " TO " + MEDIUM);
|
||||
sizeBuckets.add(MEDIUM + " TO " + LARGE);
|
||||
sizeBuckets.add(LARGE + " TO " + HUGE);
|
||||
sizeBuckets.add(HUGE + " TO MAX");
|
||||
|
||||
return sizeBuckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates display name for the Content size buckets.
|
||||
*
|
||||
* @return Map of {@literal <size range, display label key>}
|
||||
*/
|
||||
private static Map<String, String> makeContentSizeBucketsDisplayLabel()
|
||||
{
|
||||
List<String> sizeBuckets = makeContentSizeBuckets();
|
||||
Map<String, String> bucketDisplayName = new HashMap<>(6);
|
||||
|
||||
if (sizeBuckets.size() != 6)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Content size buckets size does not match the bucket display label size!");
|
||||
}
|
||||
|
||||
bucketDisplayName.put(sizeBuckets.get(0), "faceted-search.size.0-10KB.label");
|
||||
bucketDisplayName.put(sizeBuckets.get(1), "faceted-search.size.10-100KB.label");
|
||||
bucketDisplayName.put(sizeBuckets.get(2), "faceted-search.size.100KB-1MB.label");
|
||||
bucketDisplayName.put(sizeBuckets.get(3), "faceted-search.size.1-16MB.label");
|
||||
bucketDisplayName.put(sizeBuckets.get(4), "faceted-search.size.16-128MB.label");
|
||||
bucketDisplayName.put(sizeBuckets.get(5), "faceted-search.size.over128.label");
|
||||
|
||||
return bucketDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single value cache for date and size buckets.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private static class BucketsCache<K, V>
|
||||
{
|
||||
private final ConcurrentMap<K, Future<V>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Buckets<K, V> buckets;
|
||||
|
||||
public BucketsCache(Buckets<K, V> buckets)
|
||||
{
|
||||
this.buckets = buckets;
|
||||
}
|
||||
|
||||
public V getRangeBuckets(final K arg) throws Exception
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Future<V> future = cache.get(arg);
|
||||
// first checks to see if the buckets computation has been started
|
||||
if (future == null)
|
||||
{
|
||||
Callable<V> result = new Callable<V>()
|
||||
{
|
||||
public V call() throws Exception
|
||||
{
|
||||
// remove the previous entry
|
||||
Set<K> keys = cache.keySet();
|
||||
for (K key : keys)
|
||||
{
|
||||
if (!key.equals(arg))
|
||||
{
|
||||
cache.remove(key);
|
||||
}
|
||||
}
|
||||
return buckets.compute(arg);
|
||||
}
|
||||
};
|
||||
// If the calculation has been started, creates a
|
||||
// FutureTask, registers it in the Map, and starts the computation
|
||||
FutureTask<V> futureTask = new FutureTask<>(result);
|
||||
|
||||
future = cache.putIfAbsent(arg, futureTask);
|
||||
if (future == null)
|
||||
{
|
||||
future = futureTask;
|
||||
futureTask.run();
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
return future.get();
|
||||
}
|
||||
catch (CancellationException ce)
|
||||
{
|
||||
// Removes cache pollution. If the calculation is cancelled
|
||||
// or failed. As caching a Future instead of a value creates
|
||||
// the possibility of cache pollution
|
||||
cache.remove(arg, future);
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that wish to create buckets.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private static interface Buckets<K, V>
|
||||
{
|
||||
V compute(K arg) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple implementation which creates Date buckets for the facet query.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private static class FacetQueryParamDateBuckets implements Buckets<LocalDate, List<String>>
|
||||
{
|
||||
@Override
|
||||
public List<String> compute(LocalDate localDate) throws Exception
|
||||
{
|
||||
return makeDateBuckets(localDate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple implementation which creates display label for the Date buckets
|
||||
* from the facet query result.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private static class FacetQueryResultDateBuckets implements Buckets<LocalDate, Map<String, String>>
|
||||
{
|
||||
@Override
|
||||
public Map<String, String> compute(LocalDate localDate) throws Exception
|
||||
{
|
||||
return makeDateBucketsDisplayLabel(localDate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple implementation which creates display label for the Content size
|
||||
* buckets from the facet query result.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
private static class FacetQueryResultContentSizeBuckets implements Buckets<String, Map<String, String>>
|
||||
{
|
||||
@Override
|
||||
public Map<String, String> compute(String arg) throws Exception
|
||||
{
|
||||
return makeContentSizeBucketsDisplayLabel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solr facet value and facet query result display label handler
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public static interface FacetLabelDisplayHandler
|
||||
{
|
||||
Pair<String, String> getDisplayLabel(String value);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple handler to get the full user name from the userID
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public static class UserNameDisplayHandler implements FacetLabelDisplayHandler
|
||||
{
|
||||
private final PersonService personService;
|
||||
private final NodeService nodeService;
|
||||
|
||||
public UserNameDisplayHandler(ServiceRegistry services)
|
||||
{
|
||||
this.personService = services.getPersonService();
|
||||
this.nodeService = services.getNodeService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getDisplayLabel(String value)
|
||||
{
|
||||
String name = null;
|
||||
|
||||
final NodeRef personRef = personService.getPersonOrNull(value);
|
||||
if (personRef != null)
|
||||
{
|
||||
final String firstName = (String) nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
|
||||
final String lastName = (String) nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
|
||||
name = (firstName != null ? firstName + " " : "") + (lastName != null ? lastName : "");
|
||||
}
|
||||
return new Pair<String, String>(value, name == null ? value : name.trim());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple handler to get the Mimetype display label.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public static class MimetypeDisplayHandler implements FacetLabelDisplayHandler
|
||||
{
|
||||
private final MimetypeService mimetypeService;
|
||||
|
||||
public MimetypeDisplayHandler(ServiceRegistry services)
|
||||
{
|
||||
this.mimetypeService = services.getMimetypeService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getDisplayLabel(String value)
|
||||
{
|
||||
Map<String, String> mimetypes = mimetypeService.getDisplaysByMimetype();
|
||||
String displayName = mimetypes.get(value);
|
||||
return new Pair<String, String>(value, displayName == null ? value : displayName.trim());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple handler to get the appropriate display label for the date buckets.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public static class DateBucketsDisplayHandler implements FacetLabelDisplayHandler
|
||||
{
|
||||
private final BucketsCache<LocalDate, Map<String, String>> cache = new BucketsCache<>(
|
||||
new FacetQueryResultDateBuckets());
|
||||
private final String fq;
|
||||
|
||||
public DateBucketsDisplayHandler(String fq)
|
||||
{
|
||||
this.fq = fq;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getDisplayLabel(String value)
|
||||
{
|
||||
Map<String, String> dateBuckets = null;
|
||||
|
||||
String dateRange = value.substring(value.indexOf('[') + 1, value.length() - 1);
|
||||
String[] lowerUpperDates = dateRange.split("\\sTO\\s");
|
||||
LocalDate date = LocalDate.parse(lowerUpperDates[1]);
|
||||
try
|
||||
{
|
||||
dateBuckets = cache.getRangeBuckets(date);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.error(
|
||||
"Error occurred while trying to get the date buckets from the cache. Calculating the dates without the cache.", e);
|
||||
dateBuckets = makeDateBucketsDisplayLabel(date);
|
||||
}
|
||||
String newValue = fq + ":\"" + lowerUpperDates[0] + "\"..\"" + lowerUpperDates[1] + '"';
|
||||
String label = dateBuckets.get(dateRange);
|
||||
return new Pair<String, String>(newValue, label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple handler to get the appropriate display label for the content size buckets.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public static class ContentSizeBucketsDisplayHandler implements FacetLabelDisplayHandler
|
||||
{
|
||||
private final BucketsCache<String, Map<String, String>> cache = new BucketsCache<>(
|
||||
new FacetQueryResultContentSizeBuckets());
|
||||
private final String fq;
|
||||
|
||||
public ContentSizeBucketsDisplayHandler(String fq)
|
||||
{
|
||||
this.fq = fq;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getDisplayLabel(String value)
|
||||
{
|
||||
String sizeRange = value.substring(value.indexOf('[') + 1, value.length() - 1);
|
||||
String[] lowerUppperSize = sizeRange.split("\\sTO\\s");
|
||||
|
||||
Map<String, String> sizeBuckets;
|
||||
try
|
||||
{
|
||||
sizeBuckets = cache.getRangeBuckets(SIZE_BUCKETS_CACHE_KEY);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.error(
|
||||
"Error occurred while trying to get the content size buckets from the cache. Calculating the size without the cache.", e);
|
||||
sizeBuckets = makeContentSizeBucketsDisplayLabel();
|
||||
}
|
||||
|
||||
String newValue = fq + ":\"" + lowerUppperSize[0] + "\"..\"" + lowerUppperSize[1] + '"';
|
||||
String label = sizeBuckets.get(sizeRange);
|
||||
return new Pair<String, String>(newValue, label);
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ package org.alfresco.repo.search.results;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -156,6 +157,12 @@ public class ResultSetSPIWrapper<ROW extends ResultSetRow, MD extends ResultSetM
|
||||
return wrapped.getFieldFacet(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return wrapped.getFacetQueries();
|
||||
}
|
||||
|
||||
private static class WrappedIterator<ROW extends ResultSetRow> implements Iterator<ResultSetRow>
|
||||
{
|
||||
private Iterator<ROW> wrapped;
|
||||
|
@@ -26,6 +26,7 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.search.SearcherException;
|
||||
import org.alfresco.repo.search.impl.lucene.LuceneResultSetRow;
|
||||
@@ -932,5 +933,11 @@ public class SortedResultSet implements ResultSet
|
||||
{
|
||||
return resultSet.getNumberFound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return resultSet.getFacetQueries();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.search.ResultSetRowIterator;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
@@ -336,4 +337,10 @@ public class FilteringResultSet extends ACLEntryAfterInvocationProvider implemen
|
||||
{
|
||||
return inclusionMask.cardinality();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getFacetQueries()
|
||||
{
|
||||
return unfiltered.getFacetQueries();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user