X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/8bf39cf42648832dd33e2570c3b5f0f5f9a086ed..52f12e17f79a613e7010b06278d2a3a723cd63eb:/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java index 2f5bc272ce..c36c1b6b19 100644 --- a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java +++ b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java @@ -15,20 +15,23 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.log4j.Logger; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryParser.ParseException; -import org.apache.lucene.search.TopDocs; -import org.hibernate.criterion.Criterion; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator; +import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator; import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator; import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator; import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator; @@ -38,30 +41,46 @@ import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableExcepti import eu.etaxonomy.cdm.api.service.pager.Pager; import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl; import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder; +import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch; +import eu.etaxonomy.cdm.api.service.search.LuceneSearch; +import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore; +import eu.etaxonomy.cdm.api.service.search.QueryFactory; import eu.etaxonomy.cdm.api.service.search.SearchResult; +import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder; +import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge; import eu.etaxonomy.cdm.common.monitor.IProgressMonitor; import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper; +import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge; +import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge; +import eu.etaxonomy.cdm.model.CdmBaseType; import eu.etaxonomy.cdm.model.common.CdmBase; import eu.etaxonomy.cdm.model.common.IdentifiableEntity; import eu.etaxonomy.cdm.model.common.IdentifiableSource; +import eu.etaxonomy.cdm.model.common.Language; import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary; import eu.etaxonomy.cdm.model.common.RelationshipBase; import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction; import eu.etaxonomy.cdm.model.common.UuidAndTitleCache; +import eu.etaxonomy.cdm.model.description.DescriptionBase; import eu.etaxonomy.cdm.model.description.DescriptionElementBase; +import eu.etaxonomy.cdm.model.description.Feature; import eu.etaxonomy.cdm.model.description.IIdentificationKey; import eu.etaxonomy.cdm.model.description.PolytomousKeyNode; +import eu.etaxonomy.cdm.model.description.SpecimenDescription; import eu.etaxonomy.cdm.model.description.TaxonDescription; import eu.etaxonomy.cdm.model.description.TaxonInteraction; +import eu.etaxonomy.cdm.model.description.TaxonNameDescription; import eu.etaxonomy.cdm.model.media.Media; import eu.etaxonomy.cdm.model.media.MediaRepresentation; import eu.etaxonomy.cdm.model.media.MediaUtils; +import eu.etaxonomy.cdm.model.molecular.DnaSample; +import eu.etaxonomy.cdm.model.molecular.Sequence; import eu.etaxonomy.cdm.model.name.HomotypicalGroup; -import eu.etaxonomy.cdm.model.name.NonViralName; import eu.etaxonomy.cdm.model.name.Rank; import eu.etaxonomy.cdm.model.name.TaxonNameBase; import eu.etaxonomy.cdm.model.name.ZoologicalName; import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase; +import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase; import eu.etaxonomy.cdm.model.reference.Reference; import eu.etaxonomy.cdm.model.taxon.Classification; import eu.etaxonomy.cdm.model.taxon.Synonym; @@ -72,15 +91,16 @@ import eu.etaxonomy.cdm.model.taxon.TaxonBase; import eu.etaxonomy.cdm.model.taxon.TaxonNode; import eu.etaxonomy.cdm.model.taxon.TaxonRelationship; import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType; +import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer; import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao; import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao; import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao; +import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao; import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao; import eu.etaxonomy.cdm.persistence.fetch.CdmFetch; import eu.etaxonomy.cdm.persistence.query.MatchMode; import eu.etaxonomy.cdm.persistence.query.OrderHint; import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder; -import eu.etaxonomy.cdm.search.LuceneSearch; import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy; @@ -104,9 +124,6 @@ public class TaxonServiceImpl extends IdentifiableServiceBase searchTaxaByName(String name, Reference sec) { return dao.getTaxaByName(name, sec); } @@ -140,6 +164,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getAllSynonyms(int limit, int start) { return dao.getAllSynonyms(limit, start); } @@ -150,6 +175,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getAllTaxa(int limit, int start) { return dao.getAllTaxa(limit, start); } @@ -160,6 +186,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) { if (cdmFetch == null){ cdmFetch = CdmFetch.NO_FETCH(); @@ -171,6 +198,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List propertyPaths) { return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths); } @@ -178,6 +206,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getAllRelationships(int limit, int start){ return dao.getAllRelationships(limit, start); } @@ -186,6 +215,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getTaxonRelationshipTypeVocabulary() { @@ -202,6 +232,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase clazz, Integer stepSize, IIdentifiableEntityCacheStrategy cacheStrategy, IProgressMonitor monitor) { if (clazz == null){ clazz = TaxonBase.class; @@ -365,6 +398,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase findTaxaByName(Class clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) { Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank); @@ -387,6 +422,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listTaxaByName(Class clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) { Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank); @@ -401,6 +437,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths){ Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo); @@ -414,6 +451,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo); @@ -427,6 +465,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths){ Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom); @@ -440,6 +479,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom); @@ -450,9 +490,86 @@ public class TaxonServiceImpl extends IdentifiableServiceBase(pageNumber, numberOfResults, pageSize, results); } + /** + * @param taxon + * @param includeRelationships + * @param maxDepth + * @param limit + * @param starts + * @param propertyPaths + * @return an List which is not specifically ordered + */ + @Override + public Set listRelatedTaxa(Taxon taxon, Set includeRelationships, Integer maxDepth, + Integer limit, Integer start, List propertyPaths) { + + Set relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet(), maxDepth); + relatedTaxa.remove(taxon); + beanInitializer.initializeAll(relatedTaxa, propertyPaths); + return relatedTaxa; + } + + + /** + * recursively collect related taxa for the given taxon . The returned list will also include the + * taxon supplied as parameter. + * + * @param taxon + * @param includeRelationships + * @param taxa + * @param maxDepth can be null for infinite depth + * @return + */ + private Set collectRelatedTaxa(Taxon taxon, Set includeRelationships, Set taxa, Integer maxDepth) { + + if(taxa.isEmpty()) { + taxa.add(taxon); + } + + if(maxDepth != null) { + maxDepth--; + } + if(logger.isDebugEnabled()){ + logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth); + } + List taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null); + for (TaxonRelationship taxRel : taxonRelationships) { + + // skip invalid data + if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) { + continue; + } + // filter by includeRelationships + for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) { + if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) { + if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) { + if(logger.isDebugEnabled()){ + logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache()); + } + taxa.add(taxRel.getToTaxon()); + if(maxDepth == null || maxDepth > 0) { + taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth)); + } + } + if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) { + taxa.add(taxRel.getFromTaxon()); + if(logger.isDebugEnabled()){ + logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() ); + } + if(maxDepth == null || maxDepth > 0) { + taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth)); + } + } + } + } + } + return taxa; + } + /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List) */ + @Override public Pager getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { Integer numberOfResults = dao.countSynonyms(taxon, type); @@ -467,6 +584,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { Integer numberOfResults = dao.countSynonyms(synonym, type); @@ -481,6 +599,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List propertyPaths){ Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths); return t.getHomotypicSynonymsByHomotypicGroup(); @@ -489,6 +608,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase> getHeterotypicSynonymyGroups(Taxon taxon, List propertyPaths){ Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths); List homotypicalGroups = t.getHeterotypicSynonymyGroups(); @@ -499,7 +619,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){ + @Override + public List> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){ List> result = new ArrayList>(); // Class clazz = null; @@ -523,7 +644,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase findTaxaAndNames(ITaxonServiceConfigurator configurator) { + @Override + public Pager findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) { List results = new ArrayList(); int numberOfResults = 0; // overall number of results (as opposed to number of results per page) @@ -615,6 +737,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){ List medRep = new ArrayList(); taxon = (Taxon)dao.load(taxon.getUuid()); @@ -633,16 +756,129 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listTaxonDescriptionMedia(Taxon taxon, Set includeRelationships, boolean limitToGalleries, List propertyPath){ + return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath); + } + + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List) + */ + @Override + public List listMedia(Taxon taxon, Set includeRelationships, + Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences, + Boolean includeTaxonNameDescriptions, List propertyPath) { + + Set taxa = new HashSet(); + List taxonMedia = new ArrayList(); + + if (limitToGalleries == null) { + limitToGalleries = false; + } + + // --- resolve related taxa + if (includeRelationships != null) { + taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null); + } + + taxa.add((Taxon) dao.load(taxon.getUuid())); + + if(includeTaxonDescriptions != null && includeTaxonDescriptions){ + List taxonDescriptions = new ArrayList(); + // --- TaxonDescriptions + for (Taxon t : taxa) { + taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath)); + } + for (TaxonDescription taxonDescription : taxonDescriptions) { + if (!limitToGalleries || taxonDescription.isImageGallery()) { + for (DescriptionElementBase element : taxonDescription.getElements()) { + for (Media media : element.getMedia()) { + taxonMedia.add(media); + } + } + } + } + } + + if(includeOccurrences != null && includeOccurrences) { + Set specimensOrObservations = new HashSet(); + // --- Specimens + for (Taxon t : taxa) { + specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null)); + } + for (SpecimenOrObservationBase occurrence : specimensOrObservations) { + + taxonMedia.addAll(occurrence.getMedia()); + + // SpecimenDescriptions + Set specimenDescriptions = occurrence.getSpecimenDescriptions(); + for (DescriptionBase specimenDescription : specimenDescriptions) { + if (!limitToGalleries || specimenDescription.isImageGallery()) { + Set elements = specimenDescription.getElements(); + for (DescriptionElementBase element : elements) { + for (Media media : element.getMedia()) { + taxonMedia.add(media); + } + } + } + } + + // Collection + if (occurrence instanceof DerivedUnitBase) { + if (((DerivedUnitBase) occurrence).getCollection() != null){ + taxonMedia.addAll(((DerivedUnitBase) occurrence).getCollection().getMedia()); + } + } + + // Chromatograms + if (occurrence instanceof DnaSample) { + Set sequences = ((DnaSample) occurrence).getSequences(); + for (Sequence sequence : sequences) { + taxonMedia.addAll(sequence.getChromatograms()); + } + } + + } + } + + if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) { + // --- TaxonNameDescription + Set nameDescriptions = new HashSet(); + for (Taxon t : taxa) { + nameDescriptions .addAll(t.getName().getDescriptions()); + } + for(TaxonNameDescription nameDescription: nameDescriptions){ + if (!limitToGalleries || nameDescription.isImageGallery()) { + Set elements = nameDescription.getElements(); + for (DescriptionElementBase element : elements) { + for (Media media : element.getMedia()) { + taxonMedia.add(media); + } + } + } + } + } + + beanInitializer.initializeAll(taxonMedia, propertyPath); + return taxonMedia; + } + /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set) */ + @Override public List findTaxaByID(Set listOfIDs) { - return this.dao.findById(listOfIDs); + return this.dao.listByIds(listOfIDs, null, null, null, null); } /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List propertyPaths) */ + @Override public TaxonBase findTaxonByUuid(UUID uuid, List propertyPaths){ return this.dao.findByUuid(uuid, null ,propertyPaths); } @@ -650,6 +886,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase findIdenticalTaxonNames(List propertyPath) { return this.dao.findIdenticalTaxonNames(propertyPath); } @@ -800,7 +1038,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase findIdenticalTaxonNameIds(List propertyPath) { return this.dao.findIdenticalNamesNew(propertyPath); @@ -818,6 +1059,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listSynonymRelationships( TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths, Direction direction) { @@ -1086,21 +1331,241 @@ public class TaxonServiceImpl extends IdentifiableServiceBase> findByDescriptionElementFullText(Class clazz, String queryString, Integer pageSize, Integer pageNumber, List orderHints, - List propertyPaths) throws CorruptIndexException, IOException, ParseException { + public Pager> findByFullText( + Class clazz, String queryString, + Classification classification, List languages, + boolean highlightFragments, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) throws CorruptIndexException, IOException, ParseException { + + + LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments); + + // --- execute search + TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber); + + Map idFieldMap = new HashMap(); + idFieldMap.put(CdmBaseType.TAXON, "id"); + + // --- initialize taxa, thighlight matches .... + ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery()); + List> searchResults = searchResultBuilder.createResultSet( + topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths); + + int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0; + return new DefaultPagerImpl>(pageNumber, totalHits, pageSize, searchResults); + } - String luceneQueryTemplate = "titleCache:%1$s OR text.ALL:%1$s OR name:%1$s"; - String luceneQuery = String.format(luceneQueryTemplate, queryString); + /** + * @param clazz + * @param queryString + * @param classification + * @param languages + * @param highlightFragments + * @param directorySelectClass + * @return + */ + protected LuceneSearch prepareFindByFullTextSearch(Class clazz, String queryString, Classification classification, List languages, + boolean highlightFragments) { + BooleanQuery finalQuery = new BooleanQuery(); + BooleanQuery textQuery = new BooleanQuery(); + + LuceneSearch luceneSearch = new LuceneSearch(getSession(), TaxonBase.class); + QueryFactory queryFactory = new QueryFactory(luceneSearch); + + SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", false)}; + luceneSearch.setSortFields(sortFields); + + // ---- search criteria + luceneSearch.setClazz(clazz); + + textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD); + textQuery.add(queryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD); - LuceneSearch luceneSearch = new LuceneSearch(getSession(), clazz); - TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQuery); - List> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id"); + finalQuery.add(textQuery, Occur.MUST); - return new DefaultPagerImpl>(pageNumber, searchResults.size(), pageSize, searchResults); + if(classification != null){ + finalQuery.add(queryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST); + } + luceneSearch.setQuery(finalQuery); + if(highlightFragments){ + luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray()); + } + return luceneSearch; } + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List) + */ + @Override + public Pager> findByDescriptionElementFullText( + Class clazz, String queryString, + Classification classification, List features, List languages, + boolean highlightFragments, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) throws CorruptIndexException, IOException, ParseException { + + + LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments); + + // --- execute search + TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber); + + Map idFieldMap = new HashMap(); + idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id"); + + // --- initialize taxa, highlight matches .... + ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery()); + @SuppressWarnings("rawtypes") + List> searchResults = searchResultBuilder.createResultSet( + topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths); + + int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0; + return new DefaultPagerImpl>(pageNumber, totalHits, pageSize, searchResults); + + } + + + @Override + public Pager> findByEverythingFullText(String queryString, + Classification classification, List languages, boolean highlightFragments, + Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) throws CorruptIndexException, IOException, ParseException { + + LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments); + LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments); + + LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearchByDescriptionElement, luceneSearchByTaxonBase); + + // --- execute search + TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber); + + // --- initialize taxa, highlight matches .... + ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery()); + + Map idFieldMap = new HashMap(); + idFieldMap.put(CdmBaseType.TAXON, "id"); + idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id"); + + List> searchResults = searchResultBuilder.createResultSet( + topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths); + + int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0; + return new DefaultPagerImpl>(pageNumber, totalHits, pageSize, searchResults); + + } + + + /** + * @param clazz + * @param queryString + * @param classification + * @param features + * @param languages + * @param highlightFragments + * @param directorySelectClass + * @return + */ + protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class clazz, String queryString, Classification classification, List features, + List languages, boolean highlightFragments) { + BooleanQuery finalQuery = new BooleanQuery(); + BooleanQuery textQuery = new BooleanQuery(); + + LuceneSearch luceneSearch = new LuceneSearch(getSession(), DescriptionElementBase.class); + QueryFactory queryFactory = new QueryFactory(luceneSearch); + + SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", false)}; + luceneSearch.setSortFields(sortFields); + + // ---- search criteria + luceneSearch.setClazz(clazz); + textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD); + + // common name + Query nameQuery; + if(languages == null || languages.size() == 0){ + nameQuery = queryFactory.newTermQuery("name", queryString); + } else { + nameQuery = new BooleanQuery(); + BooleanQuery languageSubQuery = new BooleanQuery(); + for(Language lang : languages){ + languageSubQuery.add(queryFactory.newTermQuery("language.uuid", lang.getUuid().toString()), Occur.SHOULD); + } + ((BooleanQuery) nameQuery).add(queryFactory.newTermQuery("name", queryString), Occur.MUST); + ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST); + } + textQuery.add(nameQuery, Occur.SHOULD); + + + // text field from TextData + textQuery.add(queryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD); + + // --- TermBase fields - by representation ---- + // state field from CategoricalData + textQuery.add(queryFactory.newDefinedTermQuery("states.state", queryString, languages), Occur.SHOULD); + + // state field from CategoricalData + textQuery.add(queryFactory.newDefinedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD); + + // area field from Distribution + textQuery.add(queryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD); + + // status field from Distribution + textQuery.add(queryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD); + + finalQuery.add(textQuery, Occur.MUST); + // --- classification ---- + + if(classification != null){ + finalQuery.add(queryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST); + } + + // --- IdentifieableEntity fields - by uuid + if(features != null && features.size() > 0 ){ + finalQuery.add(queryFactory.newEntityUuidQuery("feature.uuid", features), Occur.MUST); + } + + // the description must be associated with a taxon + finalQuery.add(queryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST); + + luceneSearch.setQuery(finalQuery); + + if(highlightFragments){ + luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray()); + } + return luceneSearch; + } + + /** + * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge} + * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages. + * This method is a convenient means to retrieve a Lucene query string for such the fields. + * + * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge} + * or {@link MultilanguageTextFieldBridge } + * @param languages the languages to search for exclusively. Can be null to search in all languages + * @param stringBuilder a StringBuilder to be reused, if null a new StringBuilder will be instantiated and is returned + * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null. + * + * TODO move to utiliy class !!!!!!!! + */ + private StringBuilder appendLocalizedFieldQuery(String name, List languages, StringBuilder stringBuilder) { + + if(stringBuilder == null){ + stringBuilder = new StringBuilder(); + } + if(languages == null || languages.size() == 0){ + stringBuilder.append(name + ".ALL:(%1$s) "); + } else { + for(Language lang : languages){ + stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) "); + } + } + return stringBuilder; + } + + @Override public List createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){ List inferredSynonyms = new ArrayList(); List inferredSynonymsToBeRemoved = new ArrayList(); @@ -1130,7 +1595,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){ List inferredSynonyms = new ArrayList(); @@ -1699,6 +2193,39 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List propertyPaths) { + + // TODO quickly implemented, create according dao !!!! + Set nodes = new HashSet(); + Set classifications = new HashSet(); + List list = new ArrayList(); + + if (taxonBase == null) { + return list; + } + + taxonBase = load(taxonBase.getUuid()); + + if (taxonBase instanceof Taxon) { + nodes.addAll(((Taxon)taxonBase).getTaxonNodes()); + } else { + for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) { + nodes.addAll(taxon.getTaxonNodes()); + } + } + for (TaxonNode node : nodes) { + classifications.add(node.getClassification()); + } + list.addAll(classifications); + return list; + } + + +