remove harmonization candidates in TaxonDao and NameService
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / NameServiceImpl.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.api.service;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.UUID;
22
23 import org.apache.log4j.Logger;
24 import org.apache.lucene.index.Term;
25 import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
26 import org.apache.lucene.search.BooleanClause.Occur;
27 import org.apache.lucene.search.BooleanQuery;
28 import org.apache.lucene.search.BooleanQuery.Builder;
29 import org.apache.lucene.search.TopDocs;
30 import org.apache.lucene.search.WildcardQuery;
31 import org.hibernate.criterion.Criterion;
32 import org.springframework.beans.factory.annotation.Autowired;
33 import org.springframework.beans.factory.annotation.Qualifier;
34 import org.springframework.stereotype.Service;
35 import org.springframework.transaction.annotation.Transactional;
36
37 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
38 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
39 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
40 import eu.etaxonomy.cdm.api.service.pager.Pager;
41 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
42 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
43 import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
44 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
45 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
46 import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
47 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
48 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
49 import eu.etaxonomy.cdm.api.service.search.SearchResult;
50 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
51 import eu.etaxonomy.cdm.api.utility.TaxonNamePartsFilter;
52 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
53 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
54 import eu.etaxonomy.cdm.model.CdmBaseType;
55 import eu.etaxonomy.cdm.model.common.CdmBase;
56 import eu.etaxonomy.cdm.model.common.Language;
57 import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
58 import eu.etaxonomy.cdm.model.common.RelationshipBase;
59 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
60 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
61 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
62 import eu.etaxonomy.cdm.model.name.HybridRelationship;
63 import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
64 import eu.etaxonomy.cdm.model.name.INonViralName;
65 import eu.etaxonomy.cdm.model.name.NameRelationship;
66 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
67 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
68 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
69 import eu.etaxonomy.cdm.model.name.Rank;
70 import eu.etaxonomy.cdm.model.name.Registration;
71 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
72 import eu.etaxonomy.cdm.model.name.TaxonName;
73 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
74 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
75 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
76 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
77 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
78 import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
79 import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
80 import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
81 import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
82 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
83 import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
84 import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
85 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
86 import eu.etaxonomy.cdm.persistence.query.MatchMode;
87 import eu.etaxonomy.cdm.persistence.query.OrderHint;
88 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
89 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
90 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
91
92
93 @Service
94 @Transactional(readOnly = true)
95 public class NameServiceImpl
96 extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
97 implements INameService {
98 static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
99
100 @Autowired
101 protected ITermVocabularyDao vocabularyDao;
102 @Autowired
103 protected IOrderedTermVocabularyDao orderedVocabularyDao;
104 @Autowired
105 @Qualifier("refEntDao")
106 protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
107 @Autowired
108 private INomenclaturalStatusDao nomStatusDao;
109 @Autowired
110 private ITypeDesignationDao typeDesignationDao;
111 @Autowired
112 private IHomotypicalGroupDao homotypicalGroupDao;
113 @Autowired
114 private ICdmGenericDao genericDao;
115 @Autowired
116 private ILuceneIndexToolProvider luceneIndexToolProvider;
117
118 /**
119 * Constructor
120 */
121 public NameServiceImpl(){}
122
123 //********************* METHODS ****************************************************************//
124
125
126 @Override
127 @Transactional(readOnly = false)
128 public DeleteResult delete(UUID nameUUID){
129 NameDeletionConfigurator config = new NameDeletionConfigurator();
130 DeleteResult result = delete(nameUUID, config);
131 return result;
132 }
133
134 @Override
135 public DeleteResult delete(TaxonName name){
136 return delete(name.getUuid());
137 }
138
139 @Override
140 @Transactional(readOnly = false)
141 public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
142 DeleteResult result = new DeleteResult();
143
144 if (name == null){
145 result.setAbort();
146 return result;
147 }
148
149 try{
150 result = this.isDeletable(name, config);
151 }catch(Exception e){
152 result.addException(e);
153 result.setError();
154 return result;
155 }
156 if (result.isOk()){
157 //remove references to this name
158 removeNameRelationshipsByDeleteConfig(name, config);
159
160 //remove name from homotypical group
161 HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
162 if (homotypicalGroup != null){
163 homotypicalGroup.removeTypifiedName(name, false);
164 }
165
166 //all type designation relationships are removed as they belong to the name
167 deleteTypeDesignation(name, null);
168 // //type designations
169 // if (! name.getTypeDesignations().isEmpty()){
170 // String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
171 // throw new ReferrencedObjectUndeletableException(message);
172 // }
173
174 try{
175 dao.delete(name);
176 result.addDeletedObject(name);
177 }catch(Exception e){
178 result.addException(e);
179 result.setError();
180 }
181 return result;
182 }
183
184 return result;
185 }
186
187 @Override
188 @Transactional(readOnly = false)
189 public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
190
191 TaxonName name = dao.load(nameUUID);
192 return delete(name, config);
193 }
194
195 @Override
196 @Transactional
197 public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase typeDesignation){
198 if(typeDesignation != null && typeDesignation .isPersited()){
199 typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
200 }
201
202 DeleteResult result = new DeleteResult();
203 if (name == null && typeDesignation == null){
204 result.setError();
205 return result;
206 }else if (name != null && typeDesignation != null){
207 removeSingleDesignation(name, typeDesignation);
208 }else if (name != null){
209 Set<TypeDesignationBase<?>> designationSet = new HashSet<>(name.getTypeDesignations());
210 for (Object o : designationSet){
211 TypeDesignationBase<?> desig = CdmBase.deproxy(o, TypeDesignationBase.class);
212 removeSingleDesignation(name, desig);
213 }
214 }else if (typeDesignation != null){
215 @SuppressWarnings("unchecked")
216 Set<TaxonName> nameSet = new HashSet<>(typeDesignation.getTypifiedNames());
217 for (Object o : nameSet){
218 TaxonName singleName = CdmBase.deproxy(o, TaxonName.class);
219 removeSingleDesignation(singleName, typeDesignation);
220 }
221 }
222 result.addDeletedObject(typeDesignation);
223 result.addUpdatedObject(name);
224 return result;
225 }
226
227
228 @Override
229 @Transactional(readOnly = false)
230 public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
231 TaxonName nameBase = load(nameUuid);
232 TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
233 return deleteTypeDesignation(nameBase, typeDesignation);
234 }
235
236 /**
237 * @param name
238 * @param typeDesignation
239 */
240 @Transactional
241 private void removeSingleDesignation(TaxonName name, TypeDesignationBase typeDesignation) {
242 name.removeTypeDesignation(typeDesignation);
243 if (typeDesignation.getTypifiedNames().isEmpty()){
244 typeDesignation.removeType();
245 if (!typeDesignation.getRegistrations().isEmpty()){
246 for(Object reg: typeDesignation.getRegistrations()){
247 if (reg instanceof Registration){
248 ((Registration)reg).removeTypeDesignation(typeDesignation);
249 }
250 }
251 }
252 typeDesignationDao.delete(typeDesignation);
253
254 }
255 }
256
257
258
259 /**
260 * @param name
261 * @param config
262 */
263 private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
264 try {
265 if (config.isRemoveAllNameRelationships()){
266 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
267 for (NameRelationship rel : rels){
268 name.removeNameRelationship(rel);
269 }
270 }else{
271 //relations to this name
272 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
273 for (NameRelationship rel : rels){
274 if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
275 name.removeNameRelationship(rel);
276 }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
277 name.removeNameRelationship(rel);
278 }
279 }
280 //relations from this name
281 rels = getModifiableSet(name.getRelationsFromThisName());
282 for (NameRelationship rel : rels){
283 if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType()) ){
284 name.removeNameRelationship(rel);
285 }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
286 name.removeNameRelationship(rel);
287 }
288 }
289
290 }
291 } catch (Exception e) {
292 throw new RuntimeException(e);
293 }
294 }
295
296 /**
297 * @param name
298 * @return
299 */
300 private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
301 Set<NameRelationship> rels = new HashSet<NameRelationship>();
302 for (NameRelationship rel : relations){
303 rels.add(rel);
304 }
305 return rels;
306 }
307
308 //********************* METHODS ****************************************************************//
309
310
311 /**
312 * TODO candidate for harmonization
313 * new name findByName
314 */
315 @Override
316 @Deprecated
317 public List<TaxonName> getNamesByNameCache(String nameCache){
318 boolean includeAuthors = false;
319 List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
320 return result;
321 }
322
323
324 /**
325 * TODO candidate for harmonization
326 * new name saveHomotypicalGroups
327 *
328 * findByTitle
329 */
330 @Override
331 public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
332 List result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
333 return result;
334 }
335
336 /**
337 * TODO candidate for harmonization
338 * new name saveHomotypicalGroups
339 *
340 * findByTitle
341 */
342 @Override
343 public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
344 List result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
345 return result;
346 }
347
348 @Override
349 public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
350 Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
351 Optional<String> infraSpecificEpithet, Rank rank, Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
352
353
354 long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank);
355
356 List<TaxonNameParts> results;
357 if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
358 results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
359 rank,
360 pageSize, pageIndex, orderHints);
361 } else {
362 results = new ArrayList<>();
363 }
364
365 return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
366 }
367
368 /**
369 * {@inheritDoc}
370 */
371 @Override
372 public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
373 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
374
375 return findTaxonNameParts(
376 filter.uninomialQueryString(namePartQueryString),
377 filter.infraGenericEpithet(namePartQueryString),
378 filter.specificEpithet(namePartQueryString),
379 filter.infraspecificEpithet(namePartQueryString),
380 filter.getRank(),
381 pageSize, pageIndex, orderHints);
382 }
383
384 /**
385 * TODO candidate for harmonization
386 * new name saveHomotypicalGroups
387 */
388 @Override
389 @Transactional(readOnly = false)
390 public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
391 return homotypicalGroupDao.saveAll(homotypicalGroups);
392 }
393
394 /**
395 * TODO candidate for harmonization
396 * new name saveTypeDesignations
397 */
398 @Override
399 @Transactional(readOnly = false)
400 public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
401 return typeDesignationDao.saveAll(typeDesignationCollection);
402 }
403
404 /**
405 * TODO candidate for harmonization
406 * new name saveReferencedEntities
407 */
408 @Override
409 @Transactional(readOnly = false)
410 public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
411 return referencedEntityDao.saveAll(referencedEntityCollection);
412 }
413
414 /**
415 * TODO candidate for harmonization
416 * new name getNomenclaturalStatus
417 */
418 @Override
419 public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
420 return nomStatusDao.list(limit, start);
421 }
422
423 /**
424 * TODO candidate for harmonization
425 * new name getTypeDesignations
426 */
427 @Override
428 public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
429 return typeDesignationDao.getAllTypeDesignations(limit, start);
430 }
431
432 @Override
433 public TypeDesignationBase loadTypeDesignation(int id, List<String> propertyPaths){
434 return typeDesignationDao.load(id, propertyPaths);
435 }
436
437 @Override
438 public TypeDesignationBase loadTypeDesignation(UUID uuid, List<String> propertyPaths){
439 return typeDesignationDao.load(uuid, propertyPaths);
440 }
441
442 /**
443 * FIXME Candidate for harmonization
444 * homotypicalGroupService.list
445 */
446 @Override
447 public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
448 return homotypicalGroupDao.list(limit, start);
449 }
450
451 /**
452 * FIXME Candidate for harmonization
453 * remove
454 */
455 @Override
456 @Deprecated
457 public List<RelationshipBase> getAllRelationships(int limit, int start){
458 return dao.getAllRelationships(limit, start);
459 }
460
461
462
463 @Override
464 @Autowired
465 protected void setDao(ITaxonNameDao dao) {
466 this.dao = dao;
467 }
468
469 @Override
470 public Pager<HybridRelationship> getHybridNames(INonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
471 Integer numberOfResults = dao.countHybridNames(name, type);
472
473 List<HybridRelationship> results = new ArrayList<HybridRelationship>();
474 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
475 results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
476 }
477
478 return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
479 }
480
481 @Override
482 public List<NameRelationship> listNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
483 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
484
485 Integer numberOfResults = dao.countNameRelationships(name, direction, type);
486
487 List<NameRelationship> results = new ArrayList<NameRelationship>();
488 if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
489 results = dao.getNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
490 }
491 return results;
492 }
493
494
495 protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
496 INonViralName nvn,
497 float accuracy,
498 int maxNoOfResults,
499 List<Language> languages,
500 boolean highlightFragments) {
501 String similarity = Float.toString(accuracy);
502 String searchSuffix = "~" + similarity;
503
504
505 Builder finalQueryBuilder = new Builder();
506 finalQueryBuilder.setDisableCoord(false);
507 Builder textQueryBuilder = new Builder();
508 textQueryBuilder.setDisableCoord(false);
509
510 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
511 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
512
513 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
514 // luceneSearch.setSortFields(sortFields);
515
516 // ---- search criteria
517 luceneSearch.setCdmTypRestriction(clazz);
518
519 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
520 if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
521 fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
522 } else {
523 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
524 textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
525 }
526
527 if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
528 fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
529 } else {
530 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
531 textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
532 }
533
534 if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
535 fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
536 } else {
537 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
538 textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
539 }
540
541 if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
542 fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
543 } else {
544 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
545 textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
546 }
547
548 if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
549 fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
550 } else {
551 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
552 }
553
554 textQueryBuilder.add(fltq, Occur.MUST);
555
556 BooleanQuery textQuery = textQueryBuilder.build();
557 finalQueryBuilder.add(textQuery, Occur.MUST);
558
559 luceneSearch.setQuery(finalQueryBuilder.build());
560
561 if(highlightFragments){
562 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
563 }
564 return luceneSearch;
565 }
566
567 protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
568 String name,
569 float accuracy,
570 int maxNoOfResults,
571 List<Language> languages,
572 boolean highlightFragments) {
573
574 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
575 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
576
577 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
578 // luceneSearch.setSortFields(sortFields);
579
580 // ---- search criteria
581 luceneSearch.setCdmTypRestriction(clazz);
582 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
583
584 fltq.addTerms(name, "nameCache", accuracy, 3);
585
586 BooleanQuery finalQuery = new BooleanQuery(false);
587
588 finalQuery.add(fltq, Occur.MUST);
589
590 luceneSearch.setQuery(finalQuery);
591
592 if(highlightFragments){
593 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
594 }
595 return luceneSearch;
596 }
597
598 protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
599 String name,
600 boolean wildcard,
601 List<Language> languages,
602 boolean highlightFragments) {
603 Builder textQueryBuilder = new Builder();
604
605 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
606 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
607
608 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
609 // luceneSearch.setSortFields(sortFields);
610
611 // ---- search criteria
612 luceneSearch.setCdmTypRestriction(clazz);
613
614 if(name != null && !name.equals("")) {
615 if(wildcard) {
616 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
617 } else {
618 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
619 }
620 }
621
622 luceneSearch.setQuery(textQueryBuilder.build());
623
624 if(highlightFragments){
625 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
626 }
627 return luceneSearch;
628 }
629
630 @Override
631 public List<SearchResult<TaxonName>> findByNameFuzzySearch(
632 String name,
633 float accuracy,
634 List<Language> languages,
635 boolean highlightFragments,
636 List<String> propertyPaths,
637 int maxNoOfResults) throws IOException, LuceneParseException {
638
639 logger.info("Name to fuzzy search for : " + name);
640 // parse the input name
641 NonViralNameParserImpl parser = new NonViralNameParserImpl();
642 INonViralName nvn = parser.parseFullName(name);
643 if(name != null && !name.equals("") && nvn == null) {
644 throw new LuceneParseException("Could not parse name " + name);
645 }
646 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
647
648 // --- execute search
649 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
650
651
652 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
653 idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
654
655 // --- initialize taxa, highlight matches ....
656 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
657
658 @SuppressWarnings("rawtypes")
659 List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
660 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
661
662 return searchResults;
663
664 }
665
666 @Override
667 public List<DocumentSearchResult> findByNameFuzzySearch(
668 String name,
669 float accuracy,
670 List<Language> languages,
671 boolean highlightFragments,
672 int maxNoOfResults) throws IOException, LuceneParseException {
673
674 logger.info("Name to fuzzy search for : " + name);
675 // parse the input name
676 NonViralNameParserImpl parser = new NonViralNameParserImpl();
677 INonViralName nvn = parser.parseFullName(name);
678 if(name != null && !name.equals("") && nvn == null) {
679 throw new LuceneParseException("Could not parse name " + name);
680 }
681 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
682
683 // --- execute search
684 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
685
686 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
687
688 // --- initialize taxa, highlight matches ....
689 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
690
691 @SuppressWarnings("rawtypes")
692 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
693
694 return searchResults;
695 }
696
697 @Override
698 public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
699 String name,
700 float accuracy,
701 List<Language> languages,
702 boolean highlightFragments,
703 int maxNoOfResults) throws IOException, LuceneParseException {
704
705 logger.info("Name to fuzzy search for : " + name);
706
707 LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
708
709 // --- execute search
710 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
711 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
712
713 // --- initialize taxa, highlight matches ....
714 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
715
716 @SuppressWarnings("rawtypes")
717 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
718
719 return searchResults;
720 }
721
722 @Override
723 public List<DocumentSearchResult> findByNameExactSearch(
724 String name,
725 boolean wildcard,
726 List<Language> languages,
727 boolean highlightFragments,
728 int maxNoOfResults) throws IOException, LuceneParseException {
729
730 logger.info("Name to exact search for : " + name);
731
732 LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
733
734 // --- execute search
735
736
737 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
738
739 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
740
741 // --- initialize taxa, highlight matches ....
742 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
743
744 @SuppressWarnings("rawtypes")
745 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
746
747 return searchResults;
748 }
749
750 @Override
751 public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
752 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
753 List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
754 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
755 }
756
757 @Override
758 public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
759 return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
760 }
761
762 @Override
763 public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
764 List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
765 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
766 }
767
768 @Override
769 public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
770 return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
771 }
772
773 @Override
774 public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
775 List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
776 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
777 }
778
779 @Override
780 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
781 Integer pageSize, Integer pageNumber) {
782 return getTypeDesignations(name, status, pageSize, pageNumber, null);
783 }
784
785 @Override
786 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
787 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
788 long numberOfResults = dao.countTypeDesignations(name, status);
789
790 List<TypeDesignationBase> results = new ArrayList<>();
791 if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
792 results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
793 }
794
795 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
796 }
797
798 /**
799 * FIXME Candidate for harmonization
800 * rename search
801 */
802 @Override
803 public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
804 List<String> propertyPaths) {
805 long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
806
807 List<TaxonName> results = new ArrayList<>();
808 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
809 results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
810 }
811
812 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
813 }
814
815 @Override
816 public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
817 return dao.getUuidAndTitleCacheOfNames(limit, pattern);
818 }
819
820 @Override
821 public Pager<TaxonName> findByName(Class<? extends TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
822 Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
823
824 List<TaxonName> results = new ArrayList<>();
825 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
826 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
827 }
828
829 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
830 }
831
832 @Override
833 public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
834 return homotypicalGroupDao.findByUuid(uuid);
835 }
836
837 @Override
838 @Transactional(readOnly = false)
839 public void updateTitleCache(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
840 if (clazz == null){
841 clazz = TaxonName.class;
842 }
843 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
844 }
845
846
847 @Override
848 protected void setOtherCachesNull(TaxonName name) {
849 if (! name.isProtectedNameCache()){
850 name.setNameCache(null, false);
851 }
852 if (! name.isProtectedAuthorshipCache()){
853 name.setAuthorshipCache(null, false);
854 }
855 if (! name.isProtectedFullTitleCache()){
856 name.setFullTitleCache(null, false);
857 }
858
859 }
860
861 @Override
862 public List<TaggedText> getTaggedName(UUID uuid) {
863 TaxonName taxonName = dao.load(uuid);
864 List<TaggedText> taggedName = taxonName.getTaggedName();
865 return taggedName;
866 }
867
868
869 public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
870 DeleteResult result = new DeleteResult();
871
872 NameDeletionConfigurator nameConfig = null;
873 if (config instanceof NameDeletionConfigurator){
874 nameConfig = (NameDeletionConfigurator) config;
875 }else{
876 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
877 result.setError();
878 return result;
879 }
880
881 if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
882 HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
883
884 if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
885 result.addException(new Exception( "Name can't be deleted as it is a basionym."));
886 result.setAbort();
887 }
888 if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
889 result.addException(new Exception( "Name can't be deleted as it has a basionym."));
890 result.setAbort();
891 }
892 Set<NameRelationship> relationships = name.getNameRelations();
893 for (NameRelationship rel: relationships){
894 if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
895 result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
896 result.setAbort();
897 break;
898 }
899 }
900 }
901
902 //concepts
903 if (! name.getTaxonBases().isEmpty()){
904 result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
905 result.setAbort();
906 }
907
908 //hybrid relationships
909 if (name.isNonViral()){
910 INonViralName nvn = name;
911 Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
912 //Hibernate.initialize(parentHybridRelations);
913 if (! parentHybridRelations.isEmpty()){
914 result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));
915 result.setAbort();
916 }
917 }
918 Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
919 for (CdmBase referencingObject : referencingObjects){
920 //DerivedUnit?.storedUnder
921 if (referencingObject.isInstanceOf(DerivedUnit.class)){
922 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
923 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
924 result.addException(new ReferencedObjectUndeletableException(message));
925 result.addRelatedObject(referencingObject);
926 result.setAbort();
927 }
928 //DescriptionElementSource#nameUsedInSource
929 else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
930 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
931 result.addException(new ReferencedObjectUndeletableException(message));
932 result.addRelatedObject(referencingObject);
933 result.setAbort();
934 }
935 //NameTypeDesignation#typeName
936 else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
937 NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
938
939 if (typeDesignation.getTypeName().equals(name)){
940 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
941 result.addException(new ReferencedObjectUndeletableException(message));
942 result.addRelatedObject(referencingObject);
943 result.setAbort();
944 }
945 }
946 //DeterminationEvent#taxonName
947 else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
948 String message = "Name can't be deleted as it is used as a determination event";
949 result.addException(new ReferencedObjectUndeletableException(message));
950 result.addRelatedObject(referencingObject);
951 result.setAbort();
952 }
953 }
954
955 //TODO inline references
956
957
958 if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
959 String message = "Name can't be deleted as it is a replaced synonym.";
960 result.addException(new Exception(message));
961 result.setAbort();
962 }
963 if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
964 String message = "Name can't be deleted as it has a replaced synonym.";
965 result.addException(new Exception(message));
966 result.setAbort();
967 }
968 return result;
969
970 }
971
972
973 @Override
974 public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
975 TaxonName name = this.load(nameUUID);
976 return isDeletable(name, config);
977 }
978
979 @Override
980 @Transactional(readOnly = true)
981 public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
982 TaxonName name = dao.load(nameUuid);
983 UpdateResult result = new UpdateResult();
984 name.setAsGroupsBasionym();
985 result.addUpdatedObject(name);
986 return result;
987
988 }
989
990 @Override
991 public List<HashMap<String,String>> getNameRecords(){
992 return dao.getNameRecords();
993
994 }
995
996 }