From 0b19ebb616164188d053c4c454a13eae00f9cfa2 Mon Sep 17 00:00:00 2001 From: Andrew Hind Date: Tue, 27 Nov 2007 16:20:01 +0000 Subject: [PATCH] Tag cloud support git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7449 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/core-services-context.xml | 5 +- config/alfresco/model/contentModel.xml | 2 +- .../public-services-security-context.xml | 13 +- .../impl/lucene/ADMLuceneCategoryTest.java | 58 +++++- .../impl/lucene/ADMLuceneSearcherImpl.java | 165 +++++++++++++----- .../lucene/LuceneCategoryServiceImpl.java | 120 +++++++++++-- .../impl/lucene/LuceneIndexerAndSearcher.java | 6 + .../search/impl/lucene/LuceneSearcher.java | 13 ++ .../service/cmr/search/CategoryService.java | 12 ++ 9 files changed, 317 insertions(+), 77 deletions(-) diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index e1e96ac459..ac3c832015 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -318,7 +318,7 @@ - org.alfresco.repo.search.IndexerAndSearcher + org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcher @@ -502,6 +502,9 @@ + + + diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index 1e1240b49b..b394441108 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -668,7 +668,7 @@ true true - true + false diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index b4808de92f..dba1cbbc19 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -456,6 +456,8 @@ + + @@ -468,11 +470,12 @@ org.alfresco.service.cmr.search.CategoryService.getClassifications=AFTER_ACL_NODE.sys:base.ReadProperties org.alfresco.service.cmr.search.CategoryService.getRootCategories=AFTER_ACL_NODE.sys:base.ReadProperties org.alfresco.service.cmr.search.CategoryService.getClassificationAspects=ACL_ALLOW - org.alfresco.service.cmr.search.CategoryService.createClassifiction=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.search.CategoryService.createRootCategory=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.search.CategoryService.createCategory=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.search.CategoryService.deleteClassification=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.search.CategoryService.deleteCategory=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.search.CategoryService.createClassifiction=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.createRootCategory=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.createCategory=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.deleteClassification=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.deleteCategory=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.getTopCategories=ACL_ALLOW diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java index 3f850c7e90..c3328e9594 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java @@ -28,8 +28,11 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Random; +import javax.transaction.NotSupportedException; +import javax.transaction.SystemException; import javax.transaction.UserTransaction; import junit.framework.TestCase; @@ -58,6 +61,7 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.Pair; import org.springframework.context.ApplicationContext; /** @@ -282,7 +286,7 @@ public class ADMLuceneCategoryTest extends TestCase genCatProp.setMandatory(true); genCatProp.setMultiValued(true); genCatProp.setStoredInIndex(true); - genCatProp.setTokenisedInIndex(true); + genCatProp.setTokenisedInIndex(false); genCatProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName()); assetClassCategorisationQName = QName.createQName(TEST_NAMESPACE, "AssetClass"); @@ -294,7 +298,7 @@ public class ADMLuceneCategoryTest extends TestCase acProp.setMandatory(true); acProp.setMultiValued(true); acProp.setStoredInIndex(true); - acProp.setTokenisedInIndex(true); + acProp.setTokenisedInIndex(false); acProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName()); investmentRegionCategorisationQName = QName.createQName(TEST_NAMESPACE, "InvestmentRegion"); @@ -306,7 +310,7 @@ public class ADMLuceneCategoryTest extends TestCase irProp.setMandatory(true); irProp.setMultiValued(true); irProp.setStoredInIndex(true); - irProp.setTokenisedInIndex(true); + irProp.setTokenisedInIndex(false); irProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName()); marketingRegionCategorisationQName = QName.createQName(TEST_NAMESPACE, "MarketingRegion"); @@ -318,7 +322,7 @@ public class ADMLuceneCategoryTest extends TestCase mrProp.setMandatory(true); mrProp.setMultiValued(true); mrProp.setStoredInIndex(true); - mrProp.setTokenisedInIndex(true); + mrProp.setTokenisedInIndex(false); mrProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName()); dictionaryDAO.putModel(model); @@ -798,6 +802,52 @@ public class ADMLuceneCategoryTest extends TestCase } + public void testCatCount() throws Exception + { + TransactionService transactionService = serviceRegistry.getTransactionService(); + UserTransaction tx = transactionService.getUserTransaction(); + tx.begin(); + + + assertEquals(1, categoryService.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.IMMEDIATE).size()); + assertEquals(2, categoryService.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE).size()); + assertEquals(3, categoryService.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.IMMEDIATE).size()); + assertEquals(14, categoryService.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.ANY).size()); + assertEquals(3, categoryService.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.ANY).size()); + assertEquals(17, categoryService.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.ANY).size()); + assertEquals(2, categoryService.getClassifications(rootNodeRef.getStoreRef()).size()); + assertEquals(2, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size()); + assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size()); + assertEquals(6, categoryService.getClassificationAspects().size()); + assertEquals(2, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size()); + + List> top = categoryService.getTopCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), 10); + for(Pair current : top) + { + System.out.println(""+nodeService.getPaths(current.getFirst(), true) + " "+current.getSecond()); + } + + top = categoryService.getTopCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "InvestmentRegion"), 10); + for(Pair current : top) + { + System.out.println(""+nodeService.getPaths(current.getFirst(), true) + " "+current.getSecond()); + } + + top = categoryService.getTopCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "MarketingRegion"), 10); + for(Pair current : top) + { + System.out.println(""+nodeService.getPaths(current.getFirst(), true) + " "+current.getSecond()); + } + + top = categoryService.getTopCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "Region"), 10); + for(Pair current : top) + { + System.out.println(""+nodeService.getPaths(current.getFirst(), true) + " "+current.getSecond()); + } + + tx.commit(); + } + @SuppressWarnings("unused") private int getTotalScore(ResultSet results) { diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java index ec2da9a7e6..8d6ef78f84 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java @@ -27,8 +27,10 @@ package org.alfresco.repo.search.impl.lucene; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -60,11 +62,13 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO9075; +import org.alfresco.util.Pair; import org.alfresco.util.SearchLanguageConversion; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermEnum; import org.apache.lucene.search.Hits; import org.apache.lucene.search.Query; @@ -93,7 +97,7 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS private NamespacePrefixResolver namespacePrefixResolver; private NodeService nodeService; - + private TenantService tenantService; private QueryRegisterComponent queryRegister; @@ -156,13 +160,12 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS { this.nodeService = nodeService; } - + public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } - /** * Set the query register * @@ -175,8 +178,8 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS public ResultSet query(StoreRef store, String language, String queryString, Path[] queryOptions, QueryParameterDefinition[] queryParameterDefinitions) throws SearcherException { - store = tenantService.getName(store); - + store = tenantService.getName(store); + SearchParameters sp = new SearchParameters(); sp.addStore(store); sp.setLanguage(language); @@ -206,9 +209,9 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS { throw new IllegalStateException("Only one store can be searched at present"); } - + ArrayList stores = searchParameters.getStores(); - stores.set(0, tenantService.getName(searchParameters.getStores().get(0))); + stores.set(0, tenantService.getName(searchParameters.getStores().get(0))); String parameterisedQueryString; if (searchParameters.getQueryParameterDefinitions().size() > 0) @@ -243,18 +246,9 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS } ClosingIndexSearcher searcher = getSearcher(indexer); - Query query = LuceneQueryParser.parse( - parameterisedQueryString, DEFAULT_FIELD, - new LuceneAnalyser( - getDictionaryService(), - searchParameters.getMlAnalaysisMode() == null ? getLuceneConfig().getDefaultMLSearchAnalysisMode() : searchParameters.getMlAnalaysisMode()), - namespacePrefixResolver, - getDictionaryService(), - tenantService, - defaultOperator, - searchParameters, - getLuceneConfig(), - searcher.getIndexReader()); + Query query = LuceneQueryParser.parse(parameterisedQueryString, DEFAULT_FIELD, new LuceneAnalyser(getDictionaryService(), + searchParameters.getMlAnalaysisMode() == null ? getLuceneConfig().getDefaultMLSearchAnalysisMode() : searchParameters.getMlAnalaysisMode()), + namespacePrefixResolver, getDictionaryService(), tenantService, defaultOperator, searchParameters, getLuceneConfig(), searcher.getIndexReader()); if (s_logger.isDebugEnabled()) { s_logger.debug("Query is " + query.toString()); @@ -277,24 +271,24 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS { case FIELD: String field = sd.getField(); - if(field.startsWith("@")) + if (field.startsWith("@")) { - field = expandAttributeFieldName(field); - PropertyDefinition propertyDef = getDictionaryService().getProperty(QName.createQName(field.substring(1))); + field = expandAttributeFieldName(field); + PropertyDefinition propertyDef = getDictionaryService().getProperty(QName.createQName(field.substring(1))); - if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME)) - { - DataTypeDefinition dataType = propertyDef.getDataType(); - String analyserClassName = dataType.getAnalyserClassName(); - if(analyserClassName.equals(DateTimeAnalyser.class.getCanonicalName())) - { - field = field + ".sort"; - } - } + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME)) + { + DataTypeDefinition dataType = propertyDef.getDataType(); + String analyserClassName = dataType.getAnalyserClassName(); + if (analyserClassName.equals(DateTimeAnalyser.class.getCanonicalName())) + { + field = field + ".sort"; + } + } } if (fieldHasTerm(searcher.getReader(), field)) - { + { fields[index++] = new SortField(field, !sd.isAscending()); } else @@ -319,13 +313,7 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS } Path[] paths = searchParameters.getAttributePaths().toArray(new Path[0]); - return new LuceneResultSet( - hits, - searcher, - nodeService, - tenantService, - paths, - searchParameters); + return new LuceneResultSet(hits, searcher, nodeService, tenantService, paths, searchParameters); } catch (ParseException e) { @@ -358,13 +346,7 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS return new EmptyResultSet(); } Hits hits = searcher.search(query); - return new LuceneResultSet( - hits, - searcher, - nodeService, - tenantService, - searchParameters.getAttributePaths().toArray(new Path[0]), - searchParameters); + return new LuceneResultSet(hits, searcher, nodeService, tenantService, searchParameters.getAttributePaths().toArray(new Path[0]), searchParameters); } catch (SAXPathException e) { @@ -584,9 +566,9 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS boolean followAllParentLinks, String language) throws InvalidNodeRefException, XPathException { NodeSearcher nodeSearcher = new NodeSearcher(nodeService, getDictionaryService(), this); - + contextNodeRef = tenantService.getName(contextNodeRef); - + return nodeSearcher.selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, language); } @@ -750,4 +732,91 @@ public class ADMLuceneSearcherImpl extends AbstractLuceneBase implements LuceneS } return fieldName; } + + public List> getTopTerms(String field, int count) + { + ClosingIndexSearcher searcher = null; + try + { + LinkedList> answer = new LinkedList>(); + searcher = getSearcher(indexer); + IndexReader reader = searcher.getIndexReader(); + TermEnum terms = reader.terms(new Term(field, "")); + while (terms.next()) + { + Term term = terms.term(); + if(!term.field().equals(field)) + { + break; + } + int freq = terms.docFreq(); + Pair pair = new Pair(term.text(), Integer.valueOf(freq)); + if (answer.size() < count) + { + if (answer.size() == 0) + { + answer.add(pair); + } + else if (answer.get(answer.size() - 1).getSecond().compareTo(pair.getSecond()) >= 0) + { + answer.add(pair); + } + else + { + for (ListIterator> it = answer.listIterator(); it.hasNext(); /**/) + { + Pair test = it.next(); + if (test.getSecond().compareTo(pair.getSecond()) < 0) + { + it.previous(); + it.add(pair); + break; + } + } + } + } + else if (answer.get(count - 1).getSecond().compareTo(pair.getSecond()) < 0) + { + for (ListIterator> it = answer.listIterator(); it.hasNext(); /**/) + { + Pair test = it.next(); + if (test.getSecond().compareTo(pair.getSecond()) < 0) + { + it.previous(); + it.add(pair); + break; + } + } + answer.removeLast(); + } + else + { + // off the end + } + } + terms.close(); + return answer; + + } + catch (IOException e) + { + throw new SearcherException(e); + } + finally + { + if (searcher != null) + { + try + { + searcher.close(); + } + catch (IOException e) + { + throw new SearcherException(e); + } + } + } + + } + } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneCategoryServiceImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneCategoryServiceImpl.java index 32c93b990f..b057c19c55 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneCategoryServiceImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneCategoryServiceImpl.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -38,7 +39,10 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.search.IndexerAndSearcher; import org.alfresco.repo.search.IndexerException; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +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; @@ -47,22 +51,25 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO9075; +import org.alfresco.util.Pair; /** * Category service implementation * * @author andyh - * */ public class LuceneCategoryServiceImpl implements CategoryService { private NodeService nodeService; + + private NodeService publicNodeService; private TenantService tenantService; - + private NamespacePrefixResolver namespacePrefixResolver; private DictionaryService dictionaryService; @@ -81,6 +88,7 @@ public class LuceneCategoryServiceImpl implements CategoryService /** * Set the node service + * * @param nodeService */ public void setNodeService(NodeService nodeService) @@ -88,8 +96,19 @@ public class LuceneCategoryServiceImpl implements CategoryService this.nodeService = nodeService; } + /** + * Set the public node service + * + * @param nodeService + */ + public void setPublicNodeService(NodeService publicNodeService) + { + this.publicNodeService = publicNodeService; + } + /** * Set the tenant service + * * @param tenantService */ public void setTenantService(TenantService tenantService) @@ -99,6 +118,7 @@ public class LuceneCategoryServiceImpl implements CategoryService /** * Set the service to map prefixes to uris + * * @param namespacePrefixResolver */ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) @@ -108,6 +128,7 @@ public class LuceneCategoryServiceImpl implements CategoryService /** * Set the dictionary service + * * @param dictionaryService */ public void setDictionaryService(DictionaryService dictionaryService) @@ -117,6 +138,7 @@ public class LuceneCategoryServiceImpl implements CategoryService /** * Set the indexer and searcher + * * @param indexerAndSearcher */ public void setIndexerAndSearcher(IndexerAndSearcher indexerAndSearcher) @@ -132,13 +154,13 @@ public class LuceneCategoryServiceImpl implements CategoryService } categoryRef = tenantService.getName(categoryRef); - + ResultSet resultSet = null; try { StringBuilder luceneQuery = new StringBuilder(64); - switch(mode) + switch (mode) { case ALL: luceneQuery.append("PATH:\""); @@ -264,15 +286,15 @@ public class LuceneCategoryServiceImpl implements CategoryService ResultSet resultSet = null; try { - resultSet = indexerAndSearcher.getSearcher(storeRef, false).query(storeRef, "lucene", "PATH:\"/" + getPrefix(qname.getNamespaceURI()) + ISO9075.encode(qname.getLocalName()) + "\"", - null, null); + resultSet = indexerAndSearcher.getSearcher(storeRef, false).query(storeRef, "lucene", + "PATH:\"/" + getPrefix(qname.getNamespaceURI()) + ISO9075.encode(qname.getLocalName()) + "\"", null, null); Set nodeRefs = new HashSet(resultSet.length()); for (ResultSetRow row : resultSet) { nodeRefs.add(row.getNodeRef()); } - + return nodeRefs; } finally @@ -305,7 +327,7 @@ public class LuceneCategoryServiceImpl implements CategoryService public Collection getClassificationAspects() { - return dictionaryService.getSubAspects(ContentModel.ASPECT_CLASSIFIABLE, true); + return dictionaryService.getSubAspects(ContentModel.ASPECT_CLASSIFIABLE, true); } public NodeRef createClassifiction(StoreRef storeRef, QName typeName, String attributeName) @@ -326,23 +348,23 @@ public class LuceneCategoryServiceImpl implements CategoryService public NodeRef createCategory(NodeRef parent, String name) { - if(!nodeService.exists(parent)) + if (!nodeService.exists(parent)) { throw new AlfrescoRuntimeException("Missing category?"); } String uri = nodeService.getPrimaryParent(parent).getQName().getNamespaceURI(); String validLocalName = QName.createValidLocalName(name); - NodeRef newCategory = nodeService.createNode(parent, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(uri, validLocalName), ContentModel.TYPE_CATEGORY).getChildRef(); - nodeService.setProperty(newCategory, ContentModel.PROP_NAME, name); + NodeRef newCategory = publicNodeService.createNode(parent, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(uri, validLocalName), ContentModel.TYPE_CATEGORY).getChildRef(); + publicNodeService.setProperty(newCategory, ContentModel.PROP_NAME, name); return newCategory; } public NodeRef createRootCategory(StoreRef storeRef, QName aspectName, String name) { Set nodeRefs = getClassificationNodes(storeRef, aspectName); - if(nodeRefs.size() == 0) + if (nodeRefs.size() == 0) { - throw new AlfrescoRuntimeException("Missing classification: "+aspectName); + throw new AlfrescoRuntimeException("Missing classification: " + aspectName); } NodeRef parent = nodeRefs.iterator().next(); return createCategory(parent, name); @@ -350,16 +372,78 @@ public class LuceneCategoryServiceImpl implements CategoryService public void deleteCategory(NodeRef nodeRef) { - nodeService.deleteNode(nodeRef); + publicNodeService.deleteNode(nodeRef); } public void deleteClassification(StoreRef storeRef, QName aspectName) { throw new UnsupportedOperationException(); } - - - - + public List> getTopCategories(StoreRef storeRef, QName aspectName, int count) + { + if (indexerAndSearcher instanceof LuceneIndexerAndSearcher) + { + AspectDefinition definition = dictionaryService.getAspect(aspectName); + if(definition == null) + { + throw new IllegalStateException("Unknown aspect"); + } + QName catProperty = null; + Map properties = definition.getProperties(); + for(QName pName : properties.keySet()) + { + if(pName.getNamespaceURI().equals(aspectName.getNamespaceURI())) + { + if(pName.getLocalName().equalsIgnoreCase(aspectName.getLocalName())) + { + PropertyDefinition def = properties.get(pName); + if(def.getDataType().getName().equals(DataTypeDefinition.CATEGORY)) + { + catProperty = pName; + } + } + } + } + if(catProperty == null) + { + throw new IllegalStateException("Aspect does not have category property mirroring the aspect name"); + } + + + LuceneIndexerAndSearcher lias = (LuceneIndexerAndSearcher) indexerAndSearcher; + String field = "@" + catProperty; + SearchService searchService = lias.getSearcher(storeRef, true); + if (searchService instanceof LuceneSearcher) + { + LuceneSearcher luceneSearcher = (LuceneSearcher)searchService; + List> topTerms = luceneSearcher.getTopTerms(field, count); + List> answer = new ArrayList>(); + for (Pair term : topTerms) + { + Pair toAdd; + NodeRef nodeRef = new NodeRef(term.getFirst()); + if (nodeService.exists(nodeRef)) + { + toAdd = new Pair(nodeRef, term.getSecond()); + } + else + { + toAdd = new Pair(null, term.getSecond()); + } + answer.add(toAdd); + } + return answer; + } + else + { + throw new UnsupportedOperationException("getPolularCategories is only supported for lucene indexes"); + } + } + else + { + throw new UnsupportedOperationException("getPolularCategories is only supported for lucene indexes"); + } + } + } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerAndSearcher.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerAndSearcher.java index dcd46679f1..156eacba8f 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerAndSearcher.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerAndSearcher.java @@ -24,8 +24,13 @@ */ package org.alfresco.repo.search.impl.lucene; +import java.util.List; + import org.alfresco.repo.search.IndexerAndSearcher; import org.alfresco.repo.search.IndexerException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.Pair; public interface LuceneIndexerAndSearcher extends IndexerAndSearcher, LuceneConfig { @@ -40,4 +45,5 @@ public interface LuceneIndexerAndSearcher extends IndexerAndSearcher, LuceneConf } public R doWithAllWriteLocks(WithAllWriteLocksWork lockWork); + } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcher.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcher.java index 406ab6a4ab..c18dfbae8e 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcher.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcher.java @@ -24,9 +24,13 @@ */ package org.alfresco.repo.search.impl.lucene; +import java.util.List; + import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.util.Pair; /** * Lucene implementation specific entension to the seracher API @@ -50,4 +54,13 @@ public interface LuceneSearcher extends SearchService * @param namespacePrefixResolver */ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver); + + /** + * Get top terms + * + * @param field + * @param count + * @return + */ + public List> getTopTerms(String field, int count); } diff --git a/source/java/org/alfresco/service/cmr/search/CategoryService.java b/source/java/org/alfresco/service/cmr/search/CategoryService.java index 23686266b3..18f74adc55 100644 --- a/source/java/org/alfresco/service/cmr/search/CategoryService.java +++ b/source/java/org/alfresco/service/cmr/search/CategoryService.java @@ -25,6 +25,7 @@ package org.alfresco.service.cmr.search; import java.util.Collection; +import java.util.List; import org.alfresco.service.Auditable; import org.alfresco.service.PublicService; @@ -32,6 +33,7 @@ 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.namespace.QName; +import org.alfresco.util.Pair; /** * Category Service @@ -163,4 +165,14 @@ public interface CategoryService */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"}) public void deleteCategory(NodeRef nodeRef); + + /** + * Get the most polular categories + * + * @param storeRef + * @param aspectName + * @param count + * @return + */ + public List> getTopCategories(StoreRef storeRef, QName aspectName, int count); }