\r
import java.io.IOException;\r
import java.util.ArrayList;\r
+import java.util.EnumSet;\r
import java.util.HashMap;\r
import java.util.HashSet;\r
+import java.util.Iterator;\r
import java.util.List;\r
+import java.util.Map;\r
import java.util.Set;\r
import java.util.UUID;\r
\r
import org.apache.log4j.Logger;\r
import org.apache.lucene.index.CorruptIndexException;\r
import org.apache.lucene.queryParser.ParseException;\r
-import org.apache.lucene.search.TopDocs;\r
-import org.hibernate.criterion.Criterion;\r
+import org.apache.lucene.search.BooleanClause.Occur;\r
+import org.apache.lucene.search.BooleanFilter;\r
+import org.apache.lucene.search.BooleanQuery;\r
+import org.apache.lucene.search.DocIdSet;\r
+import org.apache.lucene.search.Query;\r
+import org.apache.lucene.search.QueryWrapperFilter;\r
+import org.apache.lucene.search.SortField;\r
import org.springframework.beans.factory.annotation.Autowired;\r
import org.springframework.stereotype.Service;\r
-import org.springframework.transaction.annotation.Propagation;\r
import org.springframework.transaction.annotation.Transactional;\r
\r
-import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;\r
import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;\r
-import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;\r
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator.ChildHandling;\r
import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;\r
import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;\r
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;\r
import eu.etaxonomy.cdm.api.service.pager.Pager;\r
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;\r
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneSearch;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;\r
+import eu.etaxonomy.cdm.api.service.search.QueryFactory;\r
import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
+import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;\r
+import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;\r
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;\r
+import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;\r
+import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;\r
+import eu.etaxonomy.cdm.model.CdmBaseType;\r
import eu.etaxonomy.cdm.model.common.CdmBase;\r
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
+import eu.etaxonomy.cdm.model.common.Language;\r
import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;\r
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;\r
import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
+import eu.etaxonomy.cdm.model.description.CommonTaxonName;\r
+import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.Distribution;\r
+import eu.etaxonomy.cdm.model.description.Feature;\r
import eu.etaxonomy.cdm.model.description.IIdentificationKey;\r
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
import eu.etaxonomy.cdm.model.description.TaxonInteraction;\r
+import eu.etaxonomy.cdm.model.description.TaxonNameDescription;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\r
import eu.etaxonomy.cdm.model.media.Media;\r
import eu.etaxonomy.cdm.model.media.MediaRepresentation;\r
import eu.etaxonomy.cdm.model.media.MediaUtils;\r
+import eu.etaxonomy.cdm.model.molecular.Amplification;\r
+import eu.etaxonomy.cdm.model.molecular.DnaSample;\r
+import eu.etaxonomy.cdm.model.molecular.Sequence;\r
+import eu.etaxonomy.cdm.model.molecular.SingleRead;\r
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
-import eu.etaxonomy.cdm.model.name.NonViralName;\r
import eu.etaxonomy.cdm.model.name.Rank;\r
import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
import eu.etaxonomy.cdm.model.name.ZoologicalName;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;\r
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
import eu.etaxonomy.cdm.model.reference.Reference;\r
import eu.etaxonomy.cdm.model.taxon.Classification;\r
import eu.etaxonomy.cdm.model.taxon.Synonym;\r
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\r
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;\r
import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;\r
+import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;\r
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;\r
+import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;\r
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;\r
import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;\r
import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;\r
-import eu.etaxonomy.cdm.search.LuceneSearch;\r
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;\r
\r
\r
*\r
*/\r
@Service\r
-@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\r
+@Transactional(readOnly = true)\r
public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{\r
private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
\r
@Autowired\r
private ITaxonNameDao nameDao;\r
\r
- @Autowired\r
- private ISearchResultBuilder searchResultBuilder;\r
-\r
@Autowired\r
private INameService nameService;\r
\r
@Autowired\r
private IOrderedTermVocabularyDao orderedVocabularyDao;\r
\r
+ @Autowired\r
+ private IOccurrenceDao occurrenceDao;\r
+\r
+ @Autowired\r
+ private AbstractBeanInitializer beanInitializer;\r
+\r
+ @Autowired\r
+ private ILuceneIndexToolProvider luceneIndexToolProvider;\r
+\r
/**\r
* Constructor\r
*/\r
* FIXME Candidate for harmonization\r
* rename searchByName ?\r
*/\r
+ @Override\r
public List<TaxonBase> searchTaxaByName(String name, Reference sec) {\r
return dao.getTaxaByName(name, sec);\r
}\r
* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)\r
*/\r
+ @Override\r
public List<Synonym> getAllSynonyms(int limit, int start) {\r
return dao.getAllSynonyms(limit, start);\r
}\r
* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)\r
*/\r
+ @Override\r
public List<Taxon> getAllTaxa(int limit, int start) {\r
return dao.getAllTaxa(limit, start);\r
}\r
* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)\r
*/\r
+ @Override\r
public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {\r
if (cdmFetch == null){\r
cdmFetch = CdmFetch.NO_FETCH();\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)\r
*/\r
+ @Override\r
public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {\r
return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)\r
*/\r
+ @Override\r
public List<RelationshipBase> getAllRelationships(int limit, int start){\r
return dao.getAllRelationships(limit, start);\r
}\r
* FIXME Candidate for harmonization\r
* is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?\r
*/\r
+ @Override\r
@Deprecated\r
public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {\r
\r
* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)\r
*/\r
+ @Override\r
@Transactional(readOnly = false)\r
public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\r
*/\r
- //TODO correct delete handling still needs to be implemented / checked\r
+ \r
@Override\r
@Transactional(readOnly = false)\r
public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{\r
-\r
+ \r
TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();\r
TaxonNameBase<?,?> synonymName = synonym.getName();\r
HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();\r
}\r
\r
//synonym.getName().removeTaxonBase(synonym);\r
- //TODO correct delete handling still needs to be implemented / checked\r
+ \r
if (deleteSynonym){\r
// deleteSynonym(synonym, taxon, false);\r
try {\r
this.dao.flush();\r
- this.delete(synonym);\r
+ this.deleteSynonym(synonym, acceptedTaxon, new SynonymDeletionConfigurator());\r
\r
} catch (Exception e) {\r
logger.info("Can't delete old synonym from database");\r
}\r
\r
\r
+ @Override\r
public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){\r
\r
// Get name from synonym\r
* @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)\r
*/\r
@Override\r
+ @Transactional(readOnly = false)\r
public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {\r
if (clazz == null){\r
clazz = TaxonBase.class;\r
super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);\r
}\r
\r
+ @Override\r
@Autowired\r
protected void setDao(ITaxonDao dao) {\r
this.dao = dao;\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)\r
*/\r
+ @Override\r
public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {\r
Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)\r
*/\r
+ @Override\r
public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {\r
Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
*/\r
+ @Override\r
public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
*/\r
+ @Override\r
public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
*/\r
+ @Override\r
public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
*/\r
+ @Override\r
public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
\r
return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
}\r
\r
+ /**\r
+ * @param taxon\r
+ * @param includeRelationships\r
+ * @param maxDepth\r
+ * @param limit\r
+ * @param starts\r
+ * @param propertyPaths\r
+ * @return an List which is not specifically ordered\r
+ */\r
+ @Override\r
+ public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,\r
+ Integer limit, Integer start, List<String> propertyPaths) {\r
+\r
+ Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<Taxon>(), maxDepth);\r
+ relatedTaxa.remove(taxon);\r
+ beanInitializer.initializeAll(relatedTaxa, propertyPaths);\r
+ return relatedTaxa;\r
+ }\r
+\r
+\r
+ /**\r
+ * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the\r
+ * <code>taxon</code> supplied as parameter.\r
+ *\r
+ * @param taxon\r
+ * @param includeRelationships\r
+ * @param taxa\r
+ * @param maxDepth can be <code>null</code> for infinite depth\r
+ * @return\r
+ */\r
+ private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa, Integer maxDepth) {\r
+\r
+ if(taxa.isEmpty()) {\r
+ taxa.add(taxon);\r
+ }\r
+\r
+ if(maxDepth != null) {\r
+ maxDepth--;\r
+ }\r
+ if(logger.isDebugEnabled()){\r
+ logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);\r
+ }\r
+ List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null);\r
+ for (TaxonRelationship taxRel : taxonRelationships) {\r
+\r
+ // skip invalid data\r
+ if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {\r
+ continue;\r
+ }\r
+ // filter by includeRelationships\r
+ for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {\r
+ if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) {\r
+ if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {\r
+ if(logger.isDebugEnabled()){\r
+ logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());\r
+ }\r
+ taxa.add(taxRel.getToTaxon());\r
+ if(maxDepth == null || maxDepth > 0) {\r
+ taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth));\r
+ }\r
+ }\r
+ if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {\r
+ taxa.add(taxRel.getFromTaxon());\r
+ if(logger.isDebugEnabled()){\r
+ logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );\r
+ }\r
+ if(maxDepth == null || maxDepth > 0) {\r
+ taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return taxa;\r
+ }\r
+\r
/* (non-Javadoc)\r
* @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)\r
*/\r
+ @Override\r
public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
Integer numberOfResults = dao.countSynonyms(taxon, type);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
*/\r
+ @Override\r
public Pager<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
Integer numberOfResults = dao.countSynonyms(synonym, type);\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
*/\r
+ @Override\r
public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){\r
Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
return t.getHomotypicSynonymsByHomotypicGroup();\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
*/\r
+ @Override\r
public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){\r
Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();\r
return heterotypicSynonymyGroups;\r
}\r
\r
- public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){\r
+ @Override\r
+ public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){\r
\r
List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();\r
// Class<? extends TaxonBase> clazz = null;\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)\r
*/\r
- public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {\r
+ @Override\r
+ public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {\r
\r
List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();\r
int numberOfResults = 0; // overall number of results (as opposed to number of results per page)\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])\r
*/\r
+ @Override\r
public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){\r
List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();\r
taxon = (Taxon)dao.load(taxon.getUuid());\r
return medRep;\r
}\r
\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)\r
+ */\r
+ @Override\r
+ public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){\r
+ return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);\r
+ }\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)\r
+ */\r
+ @Override\r
+ public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,\r
+ Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,\r
+ Boolean includeTaxonNameDescriptions, List<String> propertyPath) {\r
+\r
+ Set<Taxon> taxa = new HashSet<Taxon>();\r
+ List<Media> taxonMedia = new ArrayList<Media>();\r
+\r
+ if (limitToGalleries == null) {\r
+ limitToGalleries = false;\r
+ }\r
+\r
+ // --- resolve related taxa\r
+ if (includeRelationships != null) {\r
+ taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null);\r
+ }\r
+\r
+ taxa.add((Taxon) dao.load(taxon.getUuid()));\r
+\r
+ if(includeTaxonDescriptions != null && includeTaxonDescriptions){\r
+ List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();\r
+ // --- TaxonDescriptions\r
+ for (Taxon t : taxa) {\r
+ taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));\r
+ }\r
+ for (TaxonDescription taxonDescription : taxonDescriptions) {\r
+ if (!limitToGalleries || taxonDescription.isImageGallery()) {\r
+ for (DescriptionElementBase element : taxonDescription.getElements()) {\r
+ for (Media media : element.getMedia()) {\r
+ taxonMedia.add(media);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if(includeOccurrences != null && includeOccurrences) {\r
+ Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<SpecimenOrObservationBase>();\r
+ // --- Specimens\r
+ for (Taxon t : taxa) {\r
+ specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));\r
+ }\r
+ for (SpecimenOrObservationBase occurrence : specimensOrObservations) {\r
+\r
+// direct media removed from specimen #3597\r
+// taxonMedia.addAll(occurrence.getMedia());\r
+\r
+ // SpecimenDescriptions\r
+ Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();\r
+ for (DescriptionBase specimenDescription : specimenDescriptions) {\r
+ if (!limitToGalleries || specimenDescription.isImageGallery()) {\r
+ Set<DescriptionElementBase> elements = specimenDescription.getElements();\r
+ for (DescriptionElementBase element : elements) {\r
+ for (Media media : element.getMedia()) {\r
+ taxonMedia.add(media);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Collection\r
+ //TODO why may collections have media attached? #\r
+ if (occurrence.isInstanceOf(DerivedUnit.class)) {\r
+ DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);\r
+ if (derivedUnit.getCollection() != null){\r
+ taxonMedia.addAll(derivedUnit.getCollection().getMedia());\r
+ }\r
+ }\r
+\r
+ // pherograms & gelPhotos\r
+ if (occurrence.isInstanceOf(DnaSample.class)) {\r
+ DnaSample dnaSample = CdmBase.deproxy(occurrence, DnaSample.class);\r
+ Set<Sequence> sequences = dnaSample.getSequences();\r
+ //we do show only those gelPhotos which lead to a consensus sequence\r
+ for (Sequence sequence : sequences) {\r
+ Set<Media> dnaRelatedMedia = new HashSet<Media>();\r
+ for (SingleRead singleRead : sequence.getSingleReads()){\r
+ Amplification amplification = singleRead.getAmplification();\r
+ dnaRelatedMedia.add(amplification.getGelPhoto());\r
+ dnaRelatedMedia.add(singleRead.getPherogram());\r
+ dnaRelatedMedia.remove(null);\r
+ }\r
+ taxonMedia.addAll(dnaRelatedMedia);\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {\r
+ // --- TaxonNameDescription\r
+ Set<TaxonNameDescription> nameDescriptions = new HashSet<TaxonNameDescription>();\r
+ for (Taxon t : taxa) {\r
+ nameDescriptions .addAll(t.getName().getDescriptions());\r
+ }\r
+ for(TaxonNameDescription nameDescription: nameDescriptions){\r
+ if (!limitToGalleries || nameDescription.isImageGallery()) {\r
+ Set<DescriptionElementBase> elements = nameDescription.getElements();\r
+ for (DescriptionElementBase element : elements) {\r
+ for (Media media : element.getMedia()) {\r
+ taxonMedia.add(media);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ beanInitializer.initializeAll(taxonMedia, propertyPath);\r
+ return taxonMedia;\r
+ }\r
+\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)\r
*/\r
+ @Override\r
public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
- return this.dao.findById(listOfIDs);\r
+ return this.dao.listByIds(listOfIDs, null, null, null, null);\r
}\r
\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)\r
*/\r
+ @Override\r
public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){\r
return this.dao.findByUuid(uuid, null ,propertyPaths);\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()\r
*/\r
+ @Override\r
public int countAllRelationships() {\r
return this.dao.countAllRelationships();\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)\r
*/\r
+ @Override\r
public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {\r
return this.dao.findIdenticalTaxonNames(propertyPath);\r
}\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)\r
*/\r
@Override\r
- public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config) throws ReferencedObjectUndeletableException {\r
+ public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config, Classification classification) throws DataChangeNoRollbackException {\r
if (config == null){\r
config = new TaxonDeletionConfigurator();\r
}\r
\r
// TaxonNode\r
- if (! config.isDeleteTaxonNodes()){\r
+ if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null )){\r
if (taxon.getTaxonNodes().size() > 0){\r
- String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";\r
+ String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";\r
throw new ReferencedObjectUndeletableException(message);\r
}\r
+ }else{\r
+ if (taxon.getTaxonNodes().size() != 0){\r
+ Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
+ Iterator<TaxonNode> iterator = nodes.iterator();\r
+ TaxonNode node = null;\r
+ boolean deleteChildren;\r
+ if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){\r
+ deleteChildren = true;\r
+ }else {\r
+ deleteChildren = false;\r
+ }\r
+ boolean success = true;\r
+ if (!config.isDeleteInAllClassifications() && !(classification == null)){\r
+ while (iterator.hasNext()){\r
+ node = iterator.next();\r
+ if (node.getClassification().equals(classification)){\r
+ break;\r
+ }\r
+ node = null;\r
+ }\r
+ if (node != null){\r
+ success =taxon.removeTaxonNode(node, deleteChildren);\r
+ } else {\r
+ String message = "Taxon is not used in defined classification";\r
+ throw new DataChangeNoRollbackException(message);\r
+ }\r
+ } else if (config.isDeleteInAllClassifications()){\r
+ success = taxon.removeTaxonNodes(deleteChildren);\r
+ }\r
+ if (!success){\r
+ String message = "The taxon node could not be deleted.";\r
+ throw new DataChangeNoRollbackException(message);\r
+ }\r
+ }\r
}\r
\r
\r
if (config.isDeleteSynonymsIfPossible()){\r
//TODO which value\r
boolean newHomotypicGroupIfNeeded = true;\r
- deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);\r
+ SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();\r
+ \r
+ deleteSynonym(synonym, taxon, synConfig);\r
}else{\r
deleteSynonymRelationships(synonym, taxon);\r
}\r
String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";\r
throw new ReferencedObjectUndeletableException(message);\r
}\r
+ } else{\r
+ for (TaxonRelationship taxRel: taxon.getTaxonRelations()){\r
+ \r
+ \r
+ \r
+ if (config.isDeleteMisappliedNamesAndInvalidDesignations()){\r
+ if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){\r
+ if (taxon.equals(taxRel.getToTaxon())){\r
+ this.deleteTaxon(taxRel.getFromTaxon(), config, classification);\r
+ }\r
+ }\r
+ }\r
+ taxon.removeTaxonRelation(taxRel);\r
+ /*if (taxFrom.equals(taxon)){\r
+ try{\r
+ this.deleteTaxon(taxTo, taxConf, classification);\r
+ } catch(DataChangeNoRollbackException e){\r
+ logger.debug("A related taxon will not be deleted." + e.getMessage());\r
+ }\r
+ } else {\r
+ try{\r
+ this.deleteTaxon(taxFrom, taxConf, classification);\r
+ } catch(DataChangeNoRollbackException e){\r
+ logger.debug("A related taxon will not be deleted." + e.getMessage());\r
+ }\r
+ \r
+ }*/\r
+ }\r
}\r
\r
\r
+ \r
+ \r
// TaxonDescription\r
Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
\r
if (config.isDeleteDescriptions()){\r
//TODO use description delete configurator ?\r
//FIXME check if description is ALWAYS deletable\r
+ taxon.removeDescription(desc);\r
descriptionService.delete(desc);\r
}else{\r
- if (desc.getDescribedSpecimenOrObservations().size()>0){\r
+ if (desc.getDescribedSpecimenOrObservation() != null){\r
String message = "Taxon can't be deleted as it is used in a TaxonDescription" +\r
" which also describes specimens or abservations";\r
throw new ReferencedObjectUndeletableException(message);\r
\r
\r
//check references with only reverse mapping\r
- Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);\r
- for (CdmBase referencingObject : referencingObjects){\r
- //IIdentificationKeys (Media, Polytomous, MultiAccess)\r
- if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){\r
- String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
- message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnitBase.class).getTitleCache());\r
- throw new ReferencedObjectUndeletableException(message);\r
- }\r
-\r
-\r
- //PolytomousKeyNode\r
- if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
- String message = "Taxon can't be deleted as it is used in polytomous key node";\r
- throw new ReferencedObjectUndeletableException(message);\r
- }\r
-\r
- //TaxonInteraction\r
- if (referencingObject.isInstanceOf(TaxonInteraction.class)){\r
- String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
- throw new ReferencedObjectUndeletableException(message);\r
- }\r
- }\r
+ String message = checkForReferences(taxon);\r
+ if (message != null){\r
+ throw new ReferencedObjectUndeletableException(message.toString());\r
+ }\r
\r
\r
//TaxonNameBase\r
if (config.isDeleteNameIfPossible()){\r
try {\r
- nameService.delete(taxon.getName(), config.getNameDeletionConfig());\r
+ \r
+ //TaxonNameBase name = nameService.find(taxon.getName().getUuid());\r
+ TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName());\r
+ //check whether taxon will be deleted or not\r
+ if (taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0){\r
+ taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);\r
+ name.removeTaxonBase(taxon);\r
+ nameService.save(name);\r
+ nameService.delete(name, config.getNameDeletionConfig());\r
+ }\r
} catch (ReferencedObjectUndeletableException e) {\r
//do nothing\r
if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}\r
+ \r
}\r
}\r
+ \r
+// TaxonDescription\r
+ /* Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
+\r
+ for (TaxonDescription desc: descriptions){\r
+ if (config.isDeleteDescriptions()){\r
+ //TODO use description delete configurator ?\r
+ //FIXME check if description is ALWAYS deletable\r
+ taxon.removeDescription(desc);\r
+ descriptionService.delete(desc);\r
+ }else{\r
+ if (desc.getDescribedSpecimenOrObservations().size()>0){\r
+ String message = "Taxon can't be deleted as it is used in a TaxonDescription" +\r
+ " which also describes specimens or observations";\r
+ throw new ReferencedObjectUndeletableException(message);\r
+ }\r
+ }\r
+ }*/\r
+ if (taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0){\r
+ dao.delete(taxon);\r
+ } else{\r
+ message = "Taxon can't be deleted as it is used in another Taxonnode";\r
+ throw new ReferencedObjectUndeletableException(message);\r
+ }\r
+ \r
\r
}\r
+ \r
+ private String checkForReferences(Taxon taxon){\r
+ Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);\r
+ for (CdmBase referencingObject : referencingObjects){\r
+ //IIdentificationKeys (Media, Polytomous, MultiAccess)\r
+ if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){\r
+ String message = "Taxon" + taxon.getTitleCache() + "can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+ \r
+ return message;\r
+ }\r
+\r
+\r
+ //PolytomousKeyNode\r
+ if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+ String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";\r
+ return message;\r
+ }\r
\r
+ //TaxonInteraction\r
+ if (referencingObject.isInstanceOf(TaxonInteraction.class)){\r
+ String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+ return message;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ @Transactional(readOnly = false)\r
+ public UUID delete(Synonym syn){\r
+ UUID result = syn.getUuid();\r
+ this.deleteSynonym(syn, null);\r
+ return result;\r
+ }\r
+ \r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)\r
*/\r
@Transactional(readOnly = false)\r
@Override\r
- public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {\r
+ public void deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {\r
+ deleteSynonym(synonym, null, config);\r
+ \r
+ }\r
+ \r
+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)\r
+ */\r
+ @Transactional(readOnly = false)\r
+ @Override\r
+ public void deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) {\r
if (synonym == null){\r
return;\r
}\r
+ if (config == null){\r
+ config = new SynonymDeletionConfigurator();\r
+ }\r
synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\r
\r
//remove synonymRelationship\r
}\r
for (Taxon relatedTaxon : taxonSet){\r
// dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
- relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);\r
+ relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded());\r
}\r
this.saveOrUpdate(synonym);\r
\r
//TODO remove name from homotypical group?\r
\r
//remove synonym (if necessary)\r
+ \r
+ \r
if (synonym.getSynonymRelations().isEmpty()){\r
TaxonNameBase<?,?> name = synonym.getName();\r
synonym.setName(null);\r
dao.delete(synonym);\r
\r
//remove name if possible (and required)\r
- if (name != null && removeNameIfPossible){\r
+ if (name != null && config.isDeleteNameIfPossible()){\r
try{\r
- nameService.delete(name, new NameDeletionConfigurator());\r
- }catch (DataChangeNoRollbackException ex){\r
- if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");\r
+ nameService.delete(name, config.getNameDeletionConfig());\r
+ }catch (ReferencedObjectUndeletableException ex){\r
+ System.err.println("Name wasn't deleted as it is referenced");\r
+ if (logger.isDebugEnabled()) {\r
+ logger.debug("Name wasn't deleted as it is referenced");\r
+ }\r
}\r
}\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)\r
*/\r
+ @Override\r
public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {\r
\r
return this.dao.findIdenticalNamesNew(propertyPath);\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
*/\r
+ @Override\r
public String getPhylumName(TaxonNameBase name){\r
return this.dao.getPhylumName(name);\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\r
*/\r
+ @Override\r
public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {\r
return dao.deleteSynonymRelationships(syn, taxon);\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)\r
*/\r
+ @Override\r
public long deleteSynonymRelationships(Synonym syn) {\r
return dao.deleteSynonymRelationships(syn, null);\r
}\r
/* (non-Javadoc)\r
* @see eu.etaxonomy.cdm.api.service.ITaxonService#listSynonymRelationships(eu.etaxonomy.cdm.model.taxon.TaxonBase, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List, eu.etaxonomy.cdm.model.common.RelationshipBase.Direction)\r
*/\r
+ @Override\r
public List<SynonymRelationship> listSynonymRelationships(\r
TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,\r
List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {\r
\r
} catch (Exception e){\r
logger.error(e);\r
+ e.printStackTrace();\r
}\r
\r
return bestCandidate;\r
return dao.getUuidAndTitleCacheSynonym();\r
}\r
\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+ */\r
@Override\r
- public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(Class<? extends DescriptionElementBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
- List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+ public Pager<SearchResult<TaxonBase>> findByFullText(\r
+ Class<? extends TaxonBase> clazz, String queryString,\r
+ Classification classification, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+\r
+ LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments);\r
\r
- String luceneQueryTemplate = "titleCache:%1$s OR text.ALL:%1$s OR name:%1$s";\r
- String luceneQuery = String.format(luceneQueryTemplate, queryString);\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
\r
- LuceneSearch luceneSearch = new LuceneSearch(getSession(), clazz);\r
- TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQuery);\r
- List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id");\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
\r
- return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);\r
+ // --- initialize taxa, thighlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
}\r
\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTermBase<?>> statusFilter,\r
+ Classification classification,\r
+ Integer pageSize, Integer pageNumber,\r
+ List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, ParseException {\r
+\r
+ LuceneSearch luceneSearch = prepareByDistributionSearch(areaFilter, statusFilter, classification);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+ // --- initialize taxa, thighlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+ }\r
+\r
+ /**\r
+ * @param clazz\r
+ * @param queryString\r
+ * @param classification\r
+ * @param languages\r
+ * @param highlightFragments\r
+ * @param directorySelectClass\r
+ * @return\r
+ */\r
+ protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,\r
+ boolean highlightFragments) {\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+ BooleanQuery textQuery = new BooleanQuery();\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
+ QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
+ luceneSearch.setSortFields(sortFields);\r
+\r
+ // ---- search criteria\r
+ luceneSearch.setCdmTypRestriction(clazz);\r
+\r
+ textQuery.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+ textQuery.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);\r
+\r
+ finalQuery.add(textQuery, Occur.MUST);\r
+\r
+ if(classification != null){\r
+ finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+ luceneSearch.setQuery(finalQuery);\r
+\r
+ if(highlightFragments){\r
+ luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
+ }\r
+ return luceneSearch;\r
+ }\r
+\r
+ /**\r
+ * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively\r
+ * the BlockJoinQuery could be used. The latter might be more memory save but has the\r
+ * drawback of requiring to do the join an indexing time.\r
+ * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.\r
+ *\r
+ * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:\r
+ * <ul>\r
+ * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>\r
+ * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>\r
+ * <ul>\r
+ *\r
+ * @param queryString\r
+ * @param classification\r
+ * @param languages\r
+ * @param highlightFragments\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List<Language> languages,\r
+ boolean highlightFragments) throws IOException {\r
+\r
+ String fromField;\r
+ String queryTermField;\r
+ String toField = "id"; // TaxonBase.uuid\r
+\r
+ if(edge.isBidirectional()){\r
+ throw new RuntimeException("Bidirectional joining not supported!");\r
+ }\r
+ if(edge.isEvers()){\r
+ fromField = "relatedFrom.id";\r
+ queryTermField = "relatedFrom.titleCache";\r
+ } else if(edge.isInvers()) {\r
+ fromField = "relatedTo.id";\r
+ queryTermField = "relatedTo.titleCache";\r
+ } else {\r
+ throw new RuntimeException("Invalid direction: " + edge.getDirections());\r
+ }\r
+\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
+ QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);\r
+\r
+ BooleanQuery joinFromQuery = new BooleanQuery();\r
+ joinFromQuery.add(taxonBaseQueryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);\r
+ joinFromQuery.add(taxonBaseQueryFactory.newEntityIdQuery("type.id", edge.getTaxonRelationshipType()), Occur.MUST);\r
+ Query joinQuery = taxonBaseQueryFactory.newJoinQuery(fromField, toField, joinFromQuery, TaxonRelationship.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
+ luceneSearch.setSortFields(sortFields);\r
+\r
+ finalQuery.add(joinQuery, Occur.MUST);\r
+\r
+ if(classification != null){\r
+ finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+ luceneSearch.setQuery(finalQuery);\r
+\r
+ if(highlightFragments){\r
+ luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
+ }\r
+ return luceneSearch;\r
+ }\r
+\r
+\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNamesByFullText(java.util.EnumSet, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.Set, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.Map)\r
+ */\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(\r
+ EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,\r
+ Set<NamedArea> namedAreas, Set<PresenceAbsenceTermBase<?>> distributionStatus, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize,\r
+ Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)\r
+ throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
+\r
+ // FIXME: allow taxonomic ordering\r
+ // hql equivalent: order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";\r
+ // this require building a special sort column by a special classBridge\r
+ if(highlightFragments){\r
+ logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +\r
+ "currently not fully supported by this method and thus " +\r
+ "may not work with common names and misapplied names.");\r
+ }\r
+\r
+ // convert sets to lists\r
+ List<NamedArea> namedAreaList = null;\r
+ List<PresenceAbsenceTermBase<?>>distributionStatusList = null;\r
+ if(namedAreas != null){\r
+ namedAreaList = new ArrayList<NamedArea>(namedAreas.size());\r
+ namedAreaList.addAll(namedAreas);\r
+ }\r
+ if(distributionStatus != null){\r
+ distributionStatusList = new ArrayList<PresenceAbsenceTermBase<?>>(distributionStatus.size());\r
+ distributionStatusList.addAll(distributionStatus);\r
+ }\r
+\r
+ // set default if parameter is null\r
+ if(searchModes == null){\r
+ searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);\r
+ }\r
+\r
+ boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;\r
+\r
+ List<LuceneSearch> luceneSearches = new ArrayList<LuceneSearch>();\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+\r
+ /*\r
+ ======== filtering by distribution , HOWTO ========\r
+\r
+ - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html\r
+ - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter\r
+ add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html\r
+ which will be put into a FilteredQuersy in the end ?\r
+\r
+\r
+ 3. how does it work in spatial?\r
+ see\r
+ - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html\r
+ - http://www.infoq.com/articles/LuceneSpatialSupport\r
+ - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html\r
+ ------------------------------------------------------------------------\r
+\r
+ filter strategies:\r
+ A) use a separate distribution filter per index sub-query/search:\r
+ - byTaxonSyonym (query TaxaonBase):\r
+ use a join area filter (Distribution -> TaxonBase)\r
+ - byCommonName (query DescriptionElementBase): use an area filter on\r
+ DescriptionElementBase !!! PROBLEM !!!\r
+ This cannot work since the distributions are different entities than the\r
+ common names and thus these are different lucene documents.\r
+ - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):\r
+ use a join area filter (Distribution -> TaxonBase)\r
+\r
+ B) use a common distribution filter for all index sub-query/searches:\r
+ - use a common join area filter (Distribution -> TaxonBase)\r
+ - also implement the byCommonName as join query (CommonName -> TaxonBase)\r
+ PROBLEM in this case: we are losing the fragment highlighting for the\r
+ common names, since the returned documents are always TaxonBases\r
+ */\r
+\r
+ /* The QueryFactory for creating filter queries on Distributions should\r
+ * The query factory used for the common names query cannot be reused\r
+ * for this case, since we want to only record the text fields which are\r
+ * actually used in the primary query\r
+ */\r
+ QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);\r
+\r
+ BooleanFilter multiIndexByAreaFilter = new BooleanFilter();\r
+\r
+\r
+ // search for taxa or synonyms\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+ Class taxonBaseSubclass = TaxonBase.class;\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+ taxonBaseSubclass = Taxon.class;\r
+ } else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+ taxonBaseSubclass = Synonym.class;\r
+ }\r
+ luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments));\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+ /* A) does not work!!!!\r
+ if(addDistributionFilter){\r
+ // in this case we need a filter which uses a join query\r
+ // to get the TaxonBase documents for the DescriptionElementBase documents\r
+ // which are matching the areas in question\r
+ Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+ }\r
+ */\r
+ if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+ // add additional area filter for synonyms\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+ String toField = "accTaxon.id"; // id in TaxonBase index\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+\r
+ Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+\r
+ }\r
+ }\r
+\r
+ // search by CommonTaxonName\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {\r
+ // B)\r
+ QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+ Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(\r
+ "inDescription.taxon.id",\r
+ "id",\r
+ QueryFactory.addTypeRestriction(\r
+ createByDescriptionElementFullTextQuery(queryString, classification, null, languages, descriptionElementQueryFactory)\r
+ , CommonTaxonName.class\r
+ ),\r
+ CommonTaxonName.class);\r
+ logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());\r
+ LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+ byCommonNameSearch.setCdmTypRestriction(Taxon.class);\r
+ byCommonNameSearch.setQuery(byCommonNameJoinQuery);\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+ luceneSearches.add(byCommonNameSearch);\r
+\r
+ /* A) does not work!!!!\r
+ luceneSearches.add(\r
+ prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,\r
+ queryString, classification, null, languages, highlightFragments)\r
+ );\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+ if(addDistributionFilter){\r
+ // in this case we are able to use DescriptionElementBase documents\r
+ // which are matching the areas in question directly\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);\r
+ } */\r
+ }\r
+\r
+ // search by misapplied names\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)) {\r
+ // NOTE:\r
+ // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()\r
+ // which allows doing query time joins\r
+ // finds the misapplied name (Taxon B) which is an misapplication for\r
+ // a related Taxon A.\r
+ //\r
+ luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(\r
+ new TaxonRelationshipEdge(TaxonRelationshipType.MISAPPLIED_NAME_FOR(), Direction.relatedTo),\r
+ queryString, classification, languages, highlightFragments));\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+ if(addDistributionFilter){\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+\r
+ /*\r
+ * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.\r
+ * Maybe this is a but in java itself java.\r
+ *\r
+ * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()\r
+ * directly:\r
+ *\r
+ * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";\r
+ *\r
+ * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query\r
+ * will execute as expected:\r
+ *\r
+ * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+ * String toField = "relation." + misappliedNameForUuid +".to.id";\r
+ *\r
+ * Comparing both strings by the String.equals method returns true, so both String are identical.\r
+ *\r
+ * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be\r
+ * dependent from a specific jvm (openjdk6 6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2, oracle jdk1.7.0_25 tested)\r
+ * The bug is persistent after a reboot of the development computer.\r
+ */\r
+// String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+// String toField = "relation." + misappliedNameForUuid +".to.id";\r
+ String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";\r
+// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");\r
+// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+ Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+ QueryWrapperFilter filter = new QueryWrapperFilter(taxonAreaJoinQuery);\r
+\r
+// debug code for bug described above\r
+ DocIdSet filterMatchSet = filter.getDocIdSet(luceneIndexToolProvider.getIndexReaderFor(Taxon.class));\r
+// System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));\r
+\r
+ multiIndexByAreaFilter.add(filter, Occur.SHOULD);\r
+ }\r
+ }\r
+\r
+ LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,\r
+ luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));\r
+\r
+\r
+ if(addDistributionFilter){\r
+\r
+ // B)\r
+ // in this case we need a filter which uses a join query\r
+ // to get the TaxonBase documents for the DescriptionElementBase documents\r
+ // which are matching the areas in question\r
+ //\r
+ // for toTaxa, doByCommonName\r
+ Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+ }\r
+\r
+ if (addDistributionFilter){\r
+ multiSearch.setFilter(multiIndexByAreaFilter);\r
+ }\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+ }\r
+\r
+ /**\r
+ * @param namedAreaList at least one area must be in the list\r
+ * @param distributionStatusList optional\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected Query createByDistributionJoinQuery(\r
+ List<NamedArea> namedAreaList,\r
+ List<PresenceAbsenceTermBase<?>> distributionStatusList,\r
+ QueryFactory queryFactory\r
+ ) throws IOException {\r
+\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+ String toField = "id"; // id in TaxonBase index\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);\r
+\r
+ Query taxonAreaJoinQuery = queryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+\r
+ return taxonAreaJoinQuery;\r
+ }\r
+\r
+ /**\r
+ * @param namedAreaList\r
+ * @param distributionStatusList\r
+ * @param queryFactory\r
+ * @return\r
+ */\r
+ private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,\r
+ List<PresenceAbsenceTermBase<?>> distributionStatusList, QueryFactory queryFactory) {\r
+ BooleanQuery areaQuery = new BooleanQuery();\r
+ // area field from Distribution\r
+ areaQuery.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);\r
+\r
+ // status field from Distribution\r
+ if(distributionStatusList != null && distributionStatusList.size() > 0){\r
+ areaQuery.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);\r
+ }\r
+\r
+ logger.debug("createByDistributionQuery() query: " + areaQuery.toString());\r
+ return areaQuery;\r
+ }\r
+\r
+ /**\r
+ * This method has been primarily created for testing the area join query but might\r
+ * also be useful in other situations\r
+ *\r
+ * @param namedAreaList\r
+ * @param distributionStatusList\r
+ * @param classification\r
+ * @param highlightFragments\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected LuceneSearch prepareByDistributionSearch(\r
+ List<NamedArea> namedAreaList, List<PresenceAbsenceTermBase<?>> distributionStatusList,\r
+ Classification classification) throws IOException {\r
+\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+\r
+ // FIXME is this query factory using the wrong type?\r
+ QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
+ luceneSearch.setSortFields(sortFields);\r
+\r
+\r
+ Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory);\r
+\r
+ finalQuery.add(byAreaQuery, Occur.MUST);\r
+\r
+ if(classification != null){\r
+ finalQuery.add(taxonQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+\r
+ logger.info("prepareByAreaSearch() query: " + finalQuery.toString());\r
+ luceneSearch.setQuery(finalQuery);\r
+\r
+ return luceneSearch;\r
+ }\r
+\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @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)\r
+ */\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(\r
+ Class<? extends DescriptionElementBase> clazz, String queryString,\r
+ Classification classification, List<Feature> features, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+\r
+ LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+ @SuppressWarnings("rawtypes")\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,\r
+ Classification classification, List<Language> languages, boolean highlightFragments,\r
+ Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
+\r
+ LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
+ LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);\r
+\r
+ LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * @param clazz\r
+ * @param queryString\r
+ * @param classification\r
+ * @param features\r
+ * @param languages\r
+ * @param highlightFragments\r
+ * @param directorySelectClass\r
+ * @return\r
+ */\r
+ protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,\r
+ String queryString, Classification classification, List<Feature> features,\r
+ List<Language> languages, boolean highlightFragments) {\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);\r
+ QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.STRING, false)};\r
+\r
+ BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, features,\r
+ languages, descriptionElementQueryFactory);\r
+\r
+ luceneSearch.setSortFields(sortFields);\r
+ luceneSearch.setCdmTypRestriction(clazz);\r
+ luceneSearch.setQuery(finalQuery);\r
+ if(highlightFragments){\r
+ luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());\r
+ }\r
+\r
+ return luceneSearch;\r
+ }\r
+\r
+ /**\r
+ * @param queryString\r
+ * @param classification\r
+ * @param features\r
+ * @param languages\r
+ * @param descriptionElementQueryFactory\r
+ * @return\r
+ */\r
+ private BooleanQuery createByDescriptionElementFullTextQuery(String queryString, Classification classification,\r
+ List<Feature> features, List<Language> languages, QueryFactory descriptionElementQueryFactory) {\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+ BooleanQuery textQuery = new BooleanQuery();\r
+ textQuery.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+\r
+ // common name\r
+ Query nameQuery;\r
+ if(languages == null || languages.size() == 0){\r
+ nameQuery = descriptionElementQueryFactory.newTermQuery("name", queryString);\r
+ } else {\r
+ nameQuery = new BooleanQuery();\r
+ BooleanQuery languageSubQuery = new BooleanQuery();\r
+ for(Language lang : languages){\r
+ languageSubQuery.add(descriptionElementQueryFactory.newTermQuery("language.uuid", lang.getUuid().toString(), false), Occur.SHOULD);\r
+ }\r
+ ((BooleanQuery) nameQuery).add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);\r
+ ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);\r
+ }\r
+ textQuery.add(nameQuery, Occur.SHOULD);\r
+\r
+\r
+ // text field from TextData\r
+ textQuery.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
+\r
+ // --- TermBase fields - by representation ----\r
+ // state field from CategoricalData\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);\r
+\r
+ // state field from CategoricalData\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);\r
+\r
+ // area field from Distribution\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
+\r
+ // status field from Distribution\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);\r
+\r
+ finalQuery.add(textQuery, Occur.MUST);\r
+ // --- classification ----\r
+\r
+ if(classification != null){\r
+ finalQuery.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+\r
+ // --- IdentifieableEntity fields - by uuid\r
+ if(features != null && features.size() > 0 ){\r
+ finalQuery.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);\r
+ }\r
+\r
+ // the description must be associated with a taxon\r
+ finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
+\r
+ logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());\r
+ return finalQuery;\r
+ }\r
+\r
+ /**\r
+ * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}\r
+ * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.\r
+ * This method is a convenient means to retrieve a Lucene query string for such the fields.\r
+ *\r
+ * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}\r
+ * or {@link MultilanguageTextFieldBridge }\r
+ * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages\r
+ * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned\r
+ * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.\r
+ *\r
+ * TODO move to utiliy class !!!!!!!!\r
+ */\r
+ private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {\r
+\r
+ if(stringBuilder == null){\r
+ stringBuilder = new StringBuilder();\r
+ }\r
+ if(languages == null || languages.size() == 0){\r
+ stringBuilder.append(name + ".ALL:(%1$s) ");\r
+ } else {\r
+ for(Language lang : languages){\r
+ stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");\r
+ }\r
+ }\r
+ return stringBuilder;\r
+ }\r
+\r
+ @Override\r
public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){\r
List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();\r
HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();\r
\r
\r
- UUID uuid= taxon.getName().getUuid();\r
- ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);\r
+ UUID nameUuid= taxon.getName().getUuid();\r
+ ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);\r
String epithetOfTaxon = null;\r
String infragenericEpithetOfTaxon = null;\r
String infraspecificEpithetOfTaxon = null;\r
\r
if (node.getClassification().equals(classification)){\r
if (!node.isTopmostNode()){\r
- TaxonNode parent = (TaxonNode)node.getParent();\r
+ TaxonNode parent = node.getParent();\r
parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);\r
- TaxonNameBase parentName = parent.getTaxon().getName();\r
+ TaxonNameBase<?,?> parentName = parent.getTaxon().getName();\r
ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);\r
Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());\r
Rank rankOfTaxon = taxonName.getRank();\r
\r
\r
if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){\r
- Set<String> genusNames = new HashSet<String>();\r
+\r
\r
for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
Synonym syn = synonymRelationOfParent.getSynonym();\r
\r
// Set sourceReference\r
sourceReference = syn.getSec();\r
+ if (sourceReference == null){\r
+ logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");\r
+ //TODO:Remove\r
+ if (!parentSynZooName.getTaxa().isEmpty()){\r
+ TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();\r
\r
-\r
+ sourceReference = taxon.getSec();\r
+ }\r
+ }\r
String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();\r
\r
String synTaxonInfraSpecificName= null;\r
String idInSourceSyn= getIdInSource(syn);\r
\r
if (idInSourceParent != null && idInSourceSyn != null) {\r
- IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
inferredSynName.addSource(originalSource);\r
- originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
potentialCombination.addSource(originalSource);\r
}\r
\r
// Determine the sourceReference\r
Reference sourceReference = syn.getSec();\r
\r
+ //logger.warn(sourceReference.getTitleCache());\r
+\r
synName = syn.getName();\r
ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
String synSpeciesEpithetName = synZooName.getSpecificEpithet();\r
\r
// Add the original source\r
if (idInSourceSyn != null && idInSourceTaxon != null) {\r
- IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
inferredGenus.addSource(originalSource);\r
\r
- originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
inferredSynName.addSource(originalSource);\r
+ originalSource = null;\r
\r
+ }else{\r
+ logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredGenus.addSource(originalSource);\r
+\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredSynName.addSource(originalSource);\r
+ originalSource = null;\r
}\r
\r
taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());\r
TaxonNameBase parentName, TaxonBase syn) {\r
\r
Synonym inferredEpithet;\r
- TaxonNameBase synName;\r
+ TaxonNameBase<?,?> synName;\r
ZoologicalName inferredSynName;\r
HibernateProxyHelper.deproxy(syn);\r
\r
String idInSourceSyn = getIdInSource(syn);\r
String idInSourceTaxon = getIdInSource(taxon);\r
// Determine the sourceReference\r
- Reference sourceReference = syn.getSec();\r
+ Reference<?> sourceReference = syn.getSec();\r
+\r
+ if (sourceReference == null){\r
+ logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());\r
+ sourceReference = taxon.getSec();\r
+ }\r
\r
synName = syn.getName();\r
ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);\r
}*/\r
String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
\r
- IdentifiableSource originalSource;\r
- originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
\r
inferredEpithet.addSource(originalSource);\r
\r
- originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
\r
inferredSynName.addSource(originalSource);\r
\r
}\r
count++;\r
}\r
+ } else if (sources.size() == 0){\r
+ logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());\r
}\r
\r
+\r
return idInSource;\r
}\r
\r
return citation;\r
}\r
\r
+ @Override\r
public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){\r
List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
\r
return inferredSynonyms;\r
}\r
\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)\r
+ */\r
+ @Override\r
+ public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {\r
+\r
+ // TODO quickly implemented, create according dao !!!!\r
+ Set<TaxonNode> nodes = new HashSet<TaxonNode>();\r
+ Set<Classification> classifications = new HashSet<Classification>();\r
+ List<Classification> list = new ArrayList<Classification>();\r
+\r
+ if (taxonBase == null) {\r
+ return list;\r
+ }\r
+\r
+ taxonBase = load(taxonBase.getUuid());\r
+\r
+ if (taxonBase instanceof Taxon) {\r
+ nodes.addAll(((Taxon)taxonBase).getTaxonNodes());\r
+ } else {\r
+ for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {\r
+ nodes.addAll(taxon.getTaxonNodes());\r
+ }\r
+ }\r
+ for (TaxonNode node : nodes) {\r
+ classifications.add(node.getClassification());\r
+ }\r
+ list.addAll(classifications);\r
+ return list;\r
+ }\r
+\r
+\r
+\r
\r
\r
\r