mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
ACS-4025: Support include count and orderBy count for GET /tags (#1806)
* ACS-4025: Support include count and orderBy count for GET /tags * ACS-4025: add E2Es
This commit is contained in:
committed by
GitHub
parent
05df8a7582
commit
d39401a7ec
@@ -23,225 +23,225 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
/**
|
||||
* Support class for finding categories, finding root nodes for categories and creating root categories.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public final class Classification extends BaseScopableProcessorExtension
|
||||
{
|
||||
private ServiceRegistry services;
|
||||
|
||||
private StoreRef storeRef;
|
||||
|
||||
/**
|
||||
* Set the default store reference
|
||||
*
|
||||
* @param storeRef the default store reference
|
||||
*/
|
||||
public void setStoreUrl(String storeRef)
|
||||
{
|
||||
this.storeRef = new StoreRef(storeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service registry
|
||||
*
|
||||
* @param services the service registry
|
||||
*/
|
||||
public void setServiceRegistry(ServiceRegistry services)
|
||||
{
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the category nodes in a given classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getAllCategoryNodes(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories(
|
||||
storeRef, createQName(aspect), CategoryService.Depth.ANY));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the aspects that define a classification.
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getAllClassificationAspects()
|
||||
{
|
||||
Collection<QName> aspects = services.getCategoryService().getClassificationAspects();
|
||||
String[] answer = new String[aspects.size()];
|
||||
int i = 0;
|
||||
for (QName qname : aspects)
|
||||
{
|
||||
answer[i++] = qname.toPrefixString(this.services.getNamespaceService());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a root category in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param name String
|
||||
*/
|
||||
public CategoryNode createRootCategory(String aspect, String name)
|
||||
{
|
||||
NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name);
|
||||
CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category node from the category node reference.
|
||||
*
|
||||
* @param categoryRef category node reference
|
||||
* @return {@link CategoryNode} category node
|
||||
*/
|
||||
public CategoryNode getCategory(String categoryRef)
|
||||
{
|
||||
CategoryNode result = null;
|
||||
NodeRef categoryNodeRef = new NodeRef(categoryRef);
|
||||
if (services.getNodeService().exists(categoryNodeRef) == true &&
|
||||
services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true)
|
||||
{
|
||||
result = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root categories in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories(
|
||||
storeRef, createQName(aspect)));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ordered, filtered and paged root categories in a classification.
|
||||
*
|
||||
* @param aspect
|
||||
* @param filter
|
||||
* @param maxItems
|
||||
* @param skipCount (offset)
|
||||
* @return
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount)
|
||||
{
|
||||
PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems);
|
||||
List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage();
|
||||
Object[] cats = buildCategoryNodes(rootCategories);
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category usage count.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param maxCount int
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getCategoryUsage(String aspect, int maxCount)
|
||||
{
|
||||
List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount);
|
||||
Object[] tags = new Object[topCats.size()];
|
||||
int i = 0;
|
||||
for (Pair<NodeRef, Integer> topCat : topCats)
|
||||
{
|
||||
tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond());
|
||||
}
|
||||
|
||||
return Context.getCurrentContext().newArray(getScope(), tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build category nodes.
|
||||
*
|
||||
* @param cars list of associations to category nodes
|
||||
* @return {@link Object}[] array of category nodes
|
||||
*/
|
||||
private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars)
|
||||
{
|
||||
Object[] categoryNodes = new Object[cars.size()];
|
||||
int i = 0;
|
||||
for (ChildAssociationRef car : cars)
|
||||
{
|
||||
categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope());
|
||||
}
|
||||
return categoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QName from string
|
||||
*
|
||||
* @param s QName string value
|
||||
* @return {@link QName} qualified name object
|
||||
*/
|
||||
private QName createQName(String s)
|
||||
{
|
||||
QName qname;
|
||||
if (s.indexOf(QName.NAMESPACE_BEGIN) != -1)
|
||||
{
|
||||
qname = QName.createQName(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
qname = QName.createQName(s, this.services.getNamespaceService());
|
||||
}
|
||||
return qname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag class returned from getCategoryUsage().
|
||||
*/
|
||||
public final class Tag
|
||||
{
|
||||
private CategoryNode categoryNode;
|
||||
private int frequency = 0;
|
||||
|
||||
public Tag(CategoryNode categoryNode, int frequency)
|
||||
{
|
||||
this.categoryNode = categoryNode;
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public CategoryNode getCategory()
|
||||
{
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
public int getFrequency()
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
/**
|
||||
* Support class for finding categories, finding root nodes for categories and creating root categories.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public final class Classification extends BaseScopableProcessorExtension
|
||||
{
|
||||
private ServiceRegistry services;
|
||||
|
||||
private StoreRef storeRef;
|
||||
|
||||
/**
|
||||
* Set the default store reference
|
||||
*
|
||||
* @param storeRef the default store reference
|
||||
*/
|
||||
public void setStoreUrl(String storeRef)
|
||||
{
|
||||
this.storeRef = new StoreRef(storeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service registry
|
||||
*
|
||||
* @param services the service registry
|
||||
*/
|
||||
public void setServiceRegistry(ServiceRegistry services)
|
||||
{
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the category nodes in a given classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getAllCategoryNodes(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories(
|
||||
storeRef, createQName(aspect), CategoryService.Depth.ANY));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the aspects that define a classification.
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getAllClassificationAspects()
|
||||
{
|
||||
Collection<QName> aspects = services.getCategoryService().getClassificationAspects();
|
||||
String[] answer = new String[aspects.size()];
|
||||
int i = 0;
|
||||
for (QName qname : aspects)
|
||||
{
|
||||
answer[i++] = qname.toPrefixString(this.services.getNamespaceService());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a root category in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param name String
|
||||
*/
|
||||
public CategoryNode createRootCategory(String aspect, String name)
|
||||
{
|
||||
NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name);
|
||||
CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category node from the category node reference.
|
||||
*
|
||||
* @param categoryRef category node reference
|
||||
* @return {@link CategoryNode} category node
|
||||
*/
|
||||
public CategoryNode getCategory(String categoryRef)
|
||||
{
|
||||
CategoryNode result = null;
|
||||
NodeRef categoryNodeRef = new NodeRef(categoryRef);
|
||||
if (services.getNodeService().exists(categoryNodeRef) == true &&
|
||||
services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true)
|
||||
{
|
||||
result = new CategoryNode(categoryNodeRef, this.services, getScope());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root categories in a classification.
|
||||
*
|
||||
* @param aspect String
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect)
|
||||
{
|
||||
Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories(
|
||||
storeRef, createQName(aspect)));
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ordered, filtered and paged root categories in a classification.
|
||||
*
|
||||
* @param aspect
|
||||
* @param filter
|
||||
* @param maxItems
|
||||
* @param skipCount (offset)
|
||||
* @return
|
||||
*/
|
||||
public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount)
|
||||
{
|
||||
PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems);
|
||||
List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage();
|
||||
Object[] cats = buildCategoryNodes(rootCategories);
|
||||
return Context.getCurrentContext().newArray(getScope(), cats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category usage count.
|
||||
*
|
||||
* @param aspect String
|
||||
* @param maxCount int
|
||||
* @return Scriptable
|
||||
*/
|
||||
public Scriptable getCategoryUsage(String aspect, int maxCount)
|
||||
{
|
||||
List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount);
|
||||
Object[] tags = new Object[topCats.size()];
|
||||
int i = 0;
|
||||
for (Pair<NodeRef, Integer> topCat : topCats)
|
||||
{
|
||||
tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond());
|
||||
}
|
||||
|
||||
return Context.getCurrentContext().newArray(getScope(), tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build category nodes.
|
||||
*
|
||||
* @param cars list of associations to category nodes
|
||||
* @return {@link Object}[] array of category nodes
|
||||
*/
|
||||
private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars)
|
||||
{
|
||||
Object[] categoryNodes = new Object[cars.size()];
|
||||
int i = 0;
|
||||
for (ChildAssociationRef car : cars)
|
||||
{
|
||||
categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope());
|
||||
}
|
||||
return categoryNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QName from string
|
||||
*
|
||||
* @param s QName string value
|
||||
* @return {@link QName} qualified name object
|
||||
*/
|
||||
private QName createQName(String s)
|
||||
{
|
||||
QName qname;
|
||||
if (s.indexOf(QName.NAMESPACE_BEGIN) != -1)
|
||||
{
|
||||
qname = QName.createQName(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
qname = QName.createQName(s, this.services.getNamespaceService());
|
||||
}
|
||||
return qname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag class returned from getCategoryUsage().
|
||||
*/
|
||||
public final class Tag
|
||||
{
|
||||
private CategoryNode categoryNode;
|
||||
private int frequency = 0;
|
||||
|
||||
public Tag(CategoryNode categoryNode, int frequency)
|
||||
{
|
||||
this.categoryNode = categoryNode;
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public CategoryNode getCategory()
|
||||
{
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
public int getFrequency()
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -414,34 +414,7 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService
|
||||
int count = 0;
|
||||
boolean moreItems = false;
|
||||
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> {
|
||||
final Set<ChildAssociationRef> childNodes = new HashSet<>();
|
||||
if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup in DB without filtering
|
||||
childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CollectionUtils.isNotEmpty(exactNamesFilter))
|
||||
{
|
||||
// lookup in DB filtering by name
|
||||
childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup using search engin filtering by name
|
||||
childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1));
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ChildAssociationRef> childNodesStream = childNodes.stream();
|
||||
if (sortByName)
|
||||
{
|
||||
childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName()));
|
||||
}
|
||||
return childNodesStream.collect(Collectors.toList());
|
||||
};
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(sortByName, exactNamesFilter, alikeNamesFilter, skipCount, maxItems);
|
||||
|
||||
OUTER_LOOP: for(NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
@@ -468,6 +441,55 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService
|
||||
return new ListBackedPagingResults<>(associations, moreItems);
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
final Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectName);
|
||||
final List<ChildAssociationRef> associations = new LinkedList<>();
|
||||
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(false, exactNamesFilter, alikeNamesFilter, 0, 10000);
|
||||
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
Collection<ChildAssociationRef> children = childNodesSupplier.apply(nodeRef);
|
||||
associations.addAll(children);
|
||||
}
|
||||
|
||||
return associations;
|
||||
}
|
||||
|
||||
private Function<NodeRef, Collection<ChildAssociationRef>> getNodeRefCollectionFunction(boolean sortByName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter, int skipCount, int maxItems)
|
||||
{
|
||||
final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> {
|
||||
final Set<ChildAssociationRef> childNodes = new HashSet<>();
|
||||
if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup in DB without filtering
|
||||
childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CollectionUtils.isNotEmpty(exactNamesFilter))
|
||||
{
|
||||
// lookup in DB filtering by name
|
||||
childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(alikeNamesFilter))
|
||||
{
|
||||
// lookup using search engine filtering by name
|
||||
childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1));
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ChildAssociationRef> childNodesStream = childNodes.stream();
|
||||
if (sortByName)
|
||||
{
|
||||
childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName()));
|
||||
}
|
||||
return childNodesStream.collect(Collectors.toList());
|
||||
};
|
||||
return childNodesSupplier;
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName)
|
||||
{
|
||||
return getRootCategories(storeRef, aspectName, null);
|
||||
|
@@ -46,12 +46,16 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.EmptyPagingResults;
|
||||
@@ -61,6 +65,7 @@ import org.alfresco.repo.audit.AuditComponent;
|
||||
import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies.BeforeCopyPolicy;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies.OnCopyCompletePolicy;
|
||||
import org.alfresco.repo.domain.query.QueryException;
|
||||
import org.alfresco.repo.event2.EventGenerator;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy;
|
||||
@@ -138,6 +143,9 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
private static final String TAG_DETAILS_DELIMITER = "|";
|
||||
/** Next tag delimiter */
|
||||
private static final String NEXT_TAG_DELIMITER = "\n";
|
||||
/** Parameters Include count */
|
||||
private static final String PARAM_INCLUDE_COUNT = "count";
|
||||
|
||||
|
||||
private static Set<String> FORBIDDEN_TAGS_SEQUENCES = new HashSet<String>(Arrays.asList(new String[] {NEXT_TAG_DELIMITER, TAG_DETAILS_DELIMITER}));
|
||||
|
||||
@@ -680,6 +688,21 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Long> calculateCount(StoreRef storeRef)
|
||||
{
|
||||
List<Pair<String, Integer>> tagsByCount = findTaggedNodesAndCountByTagName(storeRef);
|
||||
Map<String, Long> tagsByCountMap = new HashMap<>();
|
||||
if (tagsByCount != null)
|
||||
{
|
||||
for (Pair<String, Integer> tagByCountElem : tagsByCount)
|
||||
{
|
||||
tagsByCountMap.put(tagByCountElem.getFirst(), Long.valueOf(tagByCountElem.getSecond()));
|
||||
}
|
||||
}
|
||||
return tagsByCountMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see TaggingService#hasTag(NodeRef, String)
|
||||
*/
|
||||
@@ -956,7 +979,83 @@ public class TaggingServiceImpl implements TaggingService,
|
||||
exactNamesFilter, alikeNamesFilter);
|
||||
|
||||
return mapPagingResult(rootCategories,
|
||||
(childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName()));
|
||||
(childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName()));
|
||||
}
|
||||
|
||||
public Map<NodeRef, Long> getTags(StoreRef storeRef, List<String> parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
ParameterCheck.mandatory("storeRef", storeRef);
|
||||
Collection<ChildAssociationRef> rootCategories = categoryService.getRootCategories(storeRef, ContentModel.ASPECT_TAGGABLE, exactNamesFilter, alikeNamesFilter);
|
||||
|
||||
Map<String, Long> tagsMap = new TreeMap<>();
|
||||
for (ChildAssociationRef childAssociation : rootCategories)
|
||||
{
|
||||
tagsMap.put(childAssociation.getQName().getLocalName(), 0L);
|
||||
}
|
||||
|
||||
Map<String, Long> tagsByCountMap = new HashMap<>();
|
||||
|
||||
if(parameterIncludes.contains(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
tagsByCountMap = calculateCount(storeRef);
|
||||
|
||||
for (Map.Entry<String, Long> entry : tagsMap.entrySet()) {
|
||||
entry.setValue(Optional.ofNullable(tagsByCountMap.get(entry.getKey())).orElse(0L));
|
||||
}
|
||||
}
|
||||
|
||||
//check if we should sort results. Can only sort by one parameter, default order is ascending
|
||||
if (sorting != null)
|
||||
{
|
||||
if (sorting.getFirst().equals("tag"))
|
||||
{
|
||||
if (!sorting.getSecond())
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByKey()));
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey());
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
}
|
||||
else if (sorting.getFirst().equals(PARAM_INCLUDE_COUNT))
|
||||
{
|
||||
if (tagsByCountMap.isEmpty())
|
||||
{
|
||||
throw new QueryException("Tag count should be included when ordering by count");
|
||||
}
|
||||
|
||||
if (!sorting.getSecond())
|
||||
{
|
||||
Stream<Map.Entry<String, Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream<Map.Entry<String,Long>> sortedTags =
|
||||
tagsMap.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue());
|
||||
tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<NodeRef, Long> tagNodeRefMap = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, Long> entry : tagsMap.entrySet())
|
||||
{
|
||||
tagNodeRefMap.put(getTagNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
return tagNodeRefMap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,6 +26,7 @@
|
||||
package org.alfresco.service.cmr.search;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -150,11 +151,21 @@ public interface CategoryService
|
||||
*/
|
||||
@Auditable(parameters = {"storeRef", "aspectName", "pagingRequest", "sortByName", "exactNamesFilter", "alikeNamesFilter"})
|
||||
default PagingResults<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, PagingRequest pagingRequest, boolean sortByName,
|
||||
Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
return new EmptyPagingResults<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of the root categories for an aspect/classification supporting multiple name filters.
|
||||
*/
|
||||
@Auditable(parameters = {"storeRef", "aspectName", "exactNamesFilter", "alikeNamesFilter"})
|
||||
default Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the root categories for an aspect/classification with names that start with filter
|
||||
*
|
||||
|
@@ -28,6 +28,7 @@ package org.alfresco.service.cmr.tagging;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.api.AlfrescoPublicApi;
|
||||
import org.alfresco.query.EmptyPagingResults;
|
||||
@@ -94,6 +95,18 @@ public interface TaggingService
|
||||
{
|
||||
return new EmptyPagingResults<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of tag NodeRefs and their respective usage count filtered by name and sorted by tag name or count
|
||||
*
|
||||
* @param storeRef
|
||||
* @param parameterIncludes
|
||||
* @param sorting
|
||||
* @param exactNamesFilter
|
||||
* @param alikeNamesFilter
|
||||
* @return
|
||||
*/
|
||||
Map<NodeRef, Long> getTags(StoreRef storeRef, List<String>parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter);
|
||||
|
||||
/**
|
||||
* Get all the tags currently available that match the provided filter.
|
||||
@@ -327,8 +340,7 @@ public interface TaggingService
|
||||
*/
|
||||
@NotAuditable
|
||||
Pair<List<String>, Integer> getPagedTags(StoreRef storeRef, String filter, int fromTag, int pageSize);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get tagged nodes and count of nodes group by tag name
|
||||
*
|
||||
@@ -362,6 +374,13 @@ public interface TaggingService
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param storeRef
|
||||
* @return a map with each tag name and its usage count
|
||||
*/
|
||||
Map<String, Long> calculateCount(StoreRef storeRef);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user