ref #8030 rename updateTitleCache to updateCaches
[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 @Override
126 @Transactional(readOnly = false)
127 public DeleteResult delete(UUID nameUUID){
128 NameDeletionConfigurator config = new NameDeletionConfigurator();
129 DeleteResult result = delete(nameUUID, config);
130 return result;
131 }
132
133 @Override
134 public DeleteResult delete(TaxonName name){
135 return delete(name.getUuid());
136 }
137
138 @Override
139 @Transactional(readOnly = false)
140 public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
141 DeleteResult result = new DeleteResult();
142
143 if (name == null){
144 result.setAbort();
145 return result;
146 }
147
148 try{
149 result = this.isDeletable(name, config);
150 }catch(Exception e){
151 result.addException(e);
152 result.setError();
153 return result;
154 }
155 if (result.isOk()){
156 //remove references to this name
157 removeNameRelationshipsByDeleteConfig(name, config);
158
159 //remove name from homotypical group
160 HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
161 if (homotypicalGroup != null){
162 homotypicalGroup.removeTypifiedName(name, false);
163 }
164
165 //all type designation relationships are removed as they belong to the name
166 deleteTypeDesignation(name, null);
167 // //type designations
168 // if (! name.getTypeDesignations().isEmpty()){
169 // String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
170 // throw new ReferrencedObjectUndeletableException(message);
171 // }
172
173 try{
174 dao.delete(name);
175 result.addDeletedObject(name);
176 }catch(Exception e){
177 result.addException(e);
178 result.setError();
179 }
180 return result;
181 }
182
183 return result;
184 }
185
186 @Override
187 @Transactional(readOnly = false)
188 public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
189
190 TaxonName name = dao.load(nameUUID);
191 return delete(name, config);
192 }
193
194 @Override
195 @Transactional
196 public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase typeDesignation){
197 if(typeDesignation != null && typeDesignation .isPersited()){
198 typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
199 }
200
201 DeleteResult result = new DeleteResult();
202 if (name == null && typeDesignation == null){
203 result.setError();
204 return result;
205 }else if (name != null && typeDesignation != null){
206 removeSingleDesignation(name, typeDesignation);
207 }else if (name != null){
208 @SuppressWarnings("rawtypes")
209 Set<TypeDesignationBase> designationSet = new HashSet<>(name.getTypeDesignations());
210 for (TypeDesignationBase<?> desig : designationSet){
211 desig = CdmBase.deproxy(desig);
212 removeSingleDesignation(name, desig);
213 }
214 }else if (typeDesignation != null){
215 @SuppressWarnings("unchecked")
216 Set<TaxonName> nameSet = new HashSet<>(typeDesignation.getTypifiedNames());
217 for (TaxonName singleName : nameSet){
218 singleName = CdmBase.deproxy(singleName);
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 * TODO candidate for harmonization
325 * new name saveHomotypicalGroups
326 *
327 * findByTitle
328 */
329 @Override
330 public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
331 List result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
332 return result;
333 }
334
335 /**
336 * TODO candidate for harmonization
337 * new name saveHomotypicalGroups
338 *
339 * findByTitle
340 */
341 @Override
342 public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
343 List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
344 return result;
345 }
346
347 @Override
348 public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
349 Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
350 Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
351 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
352
353
354 long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
355
356 List<TaxonNameParts> results;
357 if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
358 results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
359 rank, excludedNamesUuids,
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 filter.getExludedNamesUuids(),
382 pageSize, pageIndex, orderHints);
383 }
384
385 /**
386 * TODO candidate for harmonization
387 * new name saveHomotypicalGroups
388 */
389 @Override
390 @Transactional(readOnly = false)
391 public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
392 return homotypicalGroupDao.saveAll(homotypicalGroups);
393 }
394
395 /**
396 * TODO candidate for harmonization
397 * new name saveTypeDesignations
398 */
399 @Override
400 @Transactional(readOnly = false)
401 public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
402 return typeDesignationDao.saveAll(typeDesignationCollection);
403 }
404
405 /**
406 * TODO candidate for harmonization
407 * new name saveReferencedEntities
408 */
409 @Override
410 @Transactional(readOnly = false)
411 public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
412 return referencedEntityDao.saveAll(referencedEntityCollection);
413 }
414
415 /**
416 * TODO candidate for harmonization
417 * new name getNomenclaturalStatus
418 */
419 @Override
420 public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
421 return nomStatusDao.list(limit, start);
422 }
423
424 /**
425 * TODO candidate for harmonization
426 * new name getTypeDesignations
427 */
428 @Override
429 public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
430 return typeDesignationDao.getAllTypeDesignations(limit, start);
431 }
432
433 @Override
434 public TypeDesignationBase loadTypeDesignation(int id, List<String> propertyPaths){
435 return typeDesignationDao.load(id, propertyPaths);
436 }
437
438 @Override
439 public TypeDesignationBase loadTypeDesignation(UUID uuid, List<String> propertyPaths){
440 return typeDesignationDao.load(uuid, propertyPaths);
441 }
442
443 /**
444 * FIXME Candidate for harmonization
445 * homotypicalGroupService.list
446 */
447 @Override
448 public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
449 return homotypicalGroupDao.list(limit, start);
450 }
451
452 /**
453 * FIXME Candidate for harmonization
454 * remove
455 */
456 @Override
457 @Deprecated
458 public List<RelationshipBase> getAllRelationships(int limit, int start){
459 return dao.getAllRelationships(limit, start);
460 }
461
462
463
464 @Override
465 @Autowired
466 protected void setDao(ITaxonNameDao dao) {
467 this.dao = dao;
468 }
469
470 @Override
471 public Pager<HybridRelationship> getHybridNames(INonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
472 Integer numberOfResults = dao.countHybridNames(name, type);
473
474 List<HybridRelationship> results = new ArrayList<HybridRelationship>();
475 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
476 results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
477 }
478
479 return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
480 }
481
482 @Override
483 public List<NameRelationship> listNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
484 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
485
486 Integer numberOfResults = dao.countNameRelationships(name, direction, type);
487
488 List<NameRelationship> results = new ArrayList<NameRelationship>();
489 if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
490 results = dao.getNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
491 }
492 return results;
493 }
494
495
496 protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
497 INonViralName nvn,
498 float accuracy,
499 int maxNoOfResults,
500 List<Language> languages,
501 boolean highlightFragments) {
502 String similarity = Float.toString(accuracy);
503 String searchSuffix = "~" + similarity;
504
505
506 Builder finalQueryBuilder = new Builder();
507 finalQueryBuilder.setDisableCoord(false);
508 Builder textQueryBuilder = new Builder();
509 textQueryBuilder.setDisableCoord(false);
510
511 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
512 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
513
514 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
515 // luceneSearch.setSortFields(sortFields);
516
517 // ---- search criteria
518 luceneSearch.setCdmTypRestriction(clazz);
519
520 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
521 if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
522 fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
523 } else {
524 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
525 textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
526 }
527
528 if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
529 fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
530 } else {
531 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
532 textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
533 }
534
535 if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
536 fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
537 } else {
538 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
539 textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
540 }
541
542 if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
543 fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
544 } else {
545 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
546 textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
547 }
548
549 if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
550 fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
551 } else {
552 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
553 }
554
555 textQueryBuilder.add(fltq, Occur.MUST);
556
557 BooleanQuery textQuery = textQueryBuilder.build();
558 finalQueryBuilder.add(textQuery, Occur.MUST);
559
560 luceneSearch.setQuery(finalQueryBuilder.build());
561
562 if(highlightFragments){
563 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
564 }
565 return luceneSearch;
566 }
567
568 protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
569 String name,
570 float accuracy,
571 int maxNoOfResults,
572 List<Language> languages,
573 boolean highlightFragments) {
574
575 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
576 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
577
578 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
579 // luceneSearch.setSortFields(sortFields);
580
581 // ---- search criteria
582 luceneSearch.setCdmTypRestriction(clazz);
583 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
584
585 fltq.addTerms(name, "nameCache", accuracy, 3);
586
587 BooleanQuery finalQuery = new BooleanQuery(false);
588
589 finalQuery.add(fltq, Occur.MUST);
590
591 luceneSearch.setQuery(finalQuery);
592
593 if(highlightFragments){
594 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
595 }
596 return luceneSearch;
597 }
598
599 protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
600 String name,
601 boolean wildcard,
602 List<Language> languages,
603 boolean highlightFragments) {
604 Builder textQueryBuilder = new Builder();
605
606 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
607 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
608
609 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
610 // luceneSearch.setSortFields(sortFields);
611
612 // ---- search criteria
613 luceneSearch.setCdmTypRestriction(clazz);
614
615 if(name != null && !name.equals("")) {
616 if(wildcard) {
617 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
618 } else {
619 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
620 }
621 }
622
623 luceneSearch.setQuery(textQueryBuilder.build());
624
625 if(highlightFragments){
626 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
627 }
628 return luceneSearch;
629 }
630
631 @Override
632 public List<SearchResult<TaxonName>> findByNameFuzzySearch(
633 String name,
634 float accuracy,
635 List<Language> languages,
636 boolean highlightFragments,
637 List<String> propertyPaths,
638 int maxNoOfResults) throws IOException, LuceneParseException {
639
640 logger.info("Name to fuzzy search for : " + name);
641 // parse the input name
642 NonViralNameParserImpl parser = new NonViralNameParserImpl();
643 INonViralName nvn = parser.parseFullName(name);
644 if(name != null && !name.equals("") && nvn == null) {
645 throw new LuceneParseException("Could not parse name " + name);
646 }
647 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
648
649 // --- execute search
650 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
651
652
653 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
654 idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
655
656 // --- initialize taxa, highlight matches ....
657 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
658
659 @SuppressWarnings("rawtypes")
660 List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
661 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
662
663 return searchResults;
664
665 }
666
667 @Override
668 public List<DocumentSearchResult> findByNameFuzzySearch(
669 String name,
670 float accuracy,
671 List<Language> languages,
672 boolean highlightFragments,
673 int maxNoOfResults) throws IOException, LuceneParseException {
674
675 logger.info("Name to fuzzy search for : " + name);
676 // parse the input name
677 NonViralNameParserImpl parser = new NonViralNameParserImpl();
678 INonViralName nvn = parser.parseFullName(name);
679 if(name != null && !name.equals("") && nvn == null) {
680 throw new LuceneParseException("Could not parse name " + name);
681 }
682 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
683
684 // --- execute search
685 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
686
687 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
688
689 // --- initialize taxa, highlight matches ....
690 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
691
692 @SuppressWarnings("rawtypes")
693 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
694
695 return searchResults;
696 }
697
698 @Override
699 public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
700 String name,
701 float accuracy,
702 List<Language> languages,
703 boolean highlightFragments,
704 int maxNoOfResults) throws IOException, LuceneParseException {
705
706 logger.info("Name to fuzzy search for : " + name);
707
708 LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
709
710 // --- execute search
711 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
712 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
713
714 // --- initialize taxa, highlight matches ....
715 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
716
717 @SuppressWarnings("rawtypes")
718 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
719
720 return searchResults;
721 }
722
723 @Override
724 public List<DocumentSearchResult> findByNameExactSearch(
725 String name,
726 boolean wildcard,
727 List<Language> languages,
728 boolean highlightFragments,
729 int maxNoOfResults) throws IOException, LuceneParseException {
730
731 logger.info("Name to exact search for : " + name);
732
733 LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
734
735 // --- execute search
736
737
738 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
739
740 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
741
742 // --- initialize taxa, highlight matches ....
743 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
744
745 @SuppressWarnings("rawtypes")
746 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
747
748 return searchResults;
749 }
750
751 @Override
752 public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
753 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
754 List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
755 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
756 }
757
758 @Override
759 public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
760 return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
761 }
762
763 @Override
764 public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
765 List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
766 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
767 }
768
769 @Override
770 public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
771 return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
772 }
773
774 @Override
775 public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
776 List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
777 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
778 }
779
780 @Override
781 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
782 Integer pageSize, Integer pageNumber) {
783 return getTypeDesignations(name, status, pageSize, pageNumber, null);
784 }
785
786 @Override
787 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
788 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
789 long numberOfResults = dao.countTypeDesignations(name, status);
790
791 List<TypeDesignationBase> results = new ArrayList<>();
792 if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
793 results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
794 }
795
796 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
797 }
798
799 /**
800 * FIXME Candidate for harmonization
801 * rename search
802 */
803 @Override
804 public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
805 List<String> propertyPaths) {
806 long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
807
808 List<TaxonName> results = new ArrayList<>();
809 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
810 results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
811 }
812
813 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
814 }
815
816 @Override
817 public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
818 return dao.getUuidAndTitleCacheOfNames(limit, pattern);
819 }
820
821 @Override
822 public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
823 Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
824 Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
825
826 List<TaxonName> results = new ArrayList<>();
827 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
828 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
829 }
830
831 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
832 }
833
834 @Override
835 public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
836 return homotypicalGroupDao.findByUuid(uuid);
837 }
838
839 @Override
840 @Transactional(readOnly = false)
841 public void updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
842 if (clazz == null){
843 clazz = TaxonName.class;
844 }
845 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
846 }
847
848
849 @Override
850 public List<TaggedText> getTaggedName(UUID uuid) {
851 TaxonName taxonName = dao.load(uuid);
852 List<TaggedText> taggedName = taxonName.getTaggedName();
853 return taggedName;
854 }
855
856
857 public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
858 DeleteResult result = new DeleteResult();
859
860 NameDeletionConfigurator nameConfig = null;
861 if (config instanceof NameDeletionConfigurator){
862 nameConfig = (NameDeletionConfigurator) config;
863 }else{
864 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
865 result.setError();
866 return result;
867 }
868
869 if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
870 HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
871
872 if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
873 result.addException(new Exception( "Name can't be deleted as it is a basionym."));
874 result.setAbort();
875 }
876 if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
877 result.addException(new Exception( "Name can't be deleted as it has a basionym."));
878 result.setAbort();
879 }
880 Set<NameRelationship> relationships = name.getNameRelations();
881 for (NameRelationship rel: relationships){
882 if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
883 result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
884 result.setAbort();
885 break;
886 }
887 }
888 }
889
890 //concepts
891 if (! name.getTaxonBases().isEmpty()){
892 result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
893 result.setAbort();
894 }
895
896 //hybrid relationships
897 if (name.isNonViral()){
898 INonViralName nvn = name;
899 Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
900 //Hibernate.initialize(parentHybridRelations);
901 if (! parentHybridRelations.isEmpty()){
902 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."));
903 result.setAbort();
904 }
905 }
906 Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
907 for (CdmBase referencingObject : referencingObjects){
908 //DerivedUnit?.storedUnder
909 if (referencingObject.isInstanceOf(DerivedUnit.class)){
910 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
911 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
912 result.addException(new ReferencedObjectUndeletableException(message));
913 result.addRelatedObject(referencingObject);
914 result.setAbort();
915 }
916 //DescriptionElementSource#nameUsedInSource
917 else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
918 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
919 result.addException(new ReferencedObjectUndeletableException(message));
920 result.addRelatedObject(referencingObject);
921 result.setAbort();
922 }
923 //NameTypeDesignation#typeName
924 else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
925 NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
926
927 if (typeDesignation.getTypeName().equals(name)){
928 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
929 result.addException(new ReferencedObjectUndeletableException(message));
930 result.addRelatedObject(referencingObject);
931 result.setAbort();
932 }
933 }
934 //DeterminationEvent#taxonName
935 else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
936 String message = "Name can't be deleted as it is used as a determination event";
937 result.addException(new ReferencedObjectUndeletableException(message));
938 result.addRelatedObject(referencingObject);
939 result.setAbort();
940 }
941 }
942
943 //TODO inline references
944
945
946 if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
947 String message = "Name can't be deleted as it is a replaced synonym.";
948 result.addException(new Exception(message));
949 result.setAbort();
950 }
951 if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
952 String message = "Name can't be deleted as it has a replaced synonym.";
953 result.addException(new Exception(message));
954 result.setAbort();
955 }
956 return result;
957
958 }
959
960
961 @Override
962 public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
963 TaxonName name = this.load(nameUUID);
964 return isDeletable(name, config);
965 }
966
967 @Override
968 @Transactional(readOnly = true)
969 public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
970 TaxonName name = dao.load(nameUuid);
971 UpdateResult result = new UpdateResult();
972 name.setAsGroupsBasionym();
973 result.addUpdatedObject(name);
974 return result;
975
976 }
977
978 @Override
979 public List<HashMap<String,String>> getNameRecords(){
980 return dao.getNameRecords();
981
982 }
983
984 }