Name search functionality for taxonomic editor
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / taxon / TaxonDaoHibernateImpl.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 package eu.etaxonomy.cdm.persistence.dao.hibernate.taxon;
10
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.UUID;
15
16 import org.apache.log4j.Logger;
17 import org.apache.lucene.analysis.SimpleAnalyzer;
18 import org.apache.lucene.queryParser.ParseException;
19 import org.apache.lucene.queryParser.QueryParser;
20 import org.apache.lucene.search.Sort;
21 import org.apache.lucene.search.SortField;
22 import org.hibernate.Criteria;
23 import org.hibernate.FetchMode;
24 import org.hibernate.Hibernate;
25 import org.hibernate.LazyInitializationException;
26 import org.hibernate.Query;
27 import org.hibernate.Transaction;
28 import org.hibernate.criterion.Criterion;
29 import org.hibernate.criterion.Projections;
30 import org.hibernate.criterion.Restrictions;
31 import org.hibernate.search.FullTextQuery;
32 import org.hibernate.search.FullTextSession;
33 import org.hibernate.search.Search;
34 import org.hibernate.search.SearchFactory;
35 import org.springframework.beans.factory.annotation.Autowired;
36 import org.springframework.beans.factory.annotation.Qualifier;
37 import org.springframework.dao.DataAccessException;
38 import org.springframework.stereotype.Repository;
39
40 import eu.etaxonomy.cdm.model.common.Annotation;
41 import eu.etaxonomy.cdm.model.common.Marker;
42 import eu.etaxonomy.cdm.model.common.OriginalSource;
43 import eu.etaxonomy.cdm.model.common.RelationshipBase;
44 import eu.etaxonomy.cdm.model.name.Rank;
45 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
46 import eu.etaxonomy.cdm.model.taxon.Synonym;
47 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
48 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
49 import eu.etaxonomy.cdm.model.taxon.Taxon;
50 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
51 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
52 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
53 import eu.etaxonomy.cdm.persistence.dao.QueryParseException;
54 import eu.etaxonomy.cdm.persistence.dao.common.ITitledDao;
55 import eu.etaxonomy.cdm.persistence.dao.hibernate.AlternativeSpellingSuggestionParser;
56 import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
57 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
58 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
59
60 /**
61 * @author a.mueller
62 * @created 24.11.2008
63 * @version 1.0
64 */
65 @Repository
66 @Qualifier("taxonDaoHibernateImpl")
67 public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implements ITaxonDao {
68 private AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser;
69
70
71 @SuppressWarnings("unused")
72 private static final Logger logger = Logger.getLogger(TaxonDaoHibernateImpl.class);
73
74 public TaxonDaoHibernateImpl() {
75 super(TaxonBase.class);
76 }
77
78 @Autowired
79 public void setAlternativeSpellingSuggestionParser(AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser) {
80 this.alternativeSpellingSuggestionParser = alternativeSpellingSuggestionParser;
81 }
82
83 /* (non-Javadoc)
84 * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase)
85 */
86 public List<Taxon> getRootTaxa(ReferenceBase sec) {
87 return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true, false);
88 }
89
90
91
92 /* (non-Javadoc)
93 * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, eu.etaxonomy.cdm.persistence.fetch.CdmFetch, java.lang.Boolean, java.lang.Boolean)
94 */
95 public List<Taxon> getRootTaxa(ReferenceBase sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications) {
96 if (onlyWithChildren == null){
97 onlyWithChildren = true;
98 }
99 if (withMisapplications == null){
100 withMisapplications = true;
101 }
102 if (cdmFetch == null){
103 cdmFetch = CdmFetch.NO_FETCH();
104 }
105
106
107 // String query = "from Taxon root ";
108 // query += " where root.taxonomicParentCache is NULL ";
109 // if (sec != null){
110 // query += " AND root.sec.id = :sec ";
111 // }
112 // Query q = getSession().createQuery(query);
113 // if (sec != null){
114 // q.setInteger("sec", sec.getId());
115 // }
116
117
118 Criteria crit = getSession().createCriteria(Taxon.class);
119 crit.add(Restrictions.isNull("taxonomicParentCache"));
120 if (sec != null){
121 crit.add(Restrictions.eq("sec", sec) );
122 }
123
124
125 if (! cdmFetch.includes(CdmFetch.FETCH_CHILDTAXA())){
126 logger.warn("no child taxa fetch");
127 //TODO overwrite LAZY (SELECT) does not work (bug in hibernate?)
128 crit.setFetchMode("relationsToThisTaxon.fromTaxon", FetchMode.LAZY);
129 }
130
131 List<Taxon> results = new ArrayList<Taxon>();
132 for(Taxon taxon : (List<Taxon>) crit.list()){
133 //childTaxa
134 //TODO create restriction instead
135 if (onlyWithChildren == false || taxon.hasTaxonomicChildren()){
136 if (withMisapplications == true || ! taxon.isMisappliedName()){
137 results.add(taxon);
138 }
139 }
140 }
141 return results;
142 }
143
144 public List<TaxonBase> getTaxaByName(String queryString, ReferenceBase sec) {
145
146 return getTaxaByName(queryString, true, sec);
147 }
148
149 public List<TaxonBase> getTaxaByName(String queryString, Boolean accepted, ReferenceBase sec) {
150
151 Criteria criteria = null;
152 if (accepted == true) {
153 criteria = getSession().createCriteria(Taxon.class);
154 } else {
155 criteria = getSession().createCriteria(Synonym.class);
156 }
157
158 criteria.setFetchMode( "name", FetchMode.JOIN );
159 criteria.createAlias("name", "name");
160
161 if (sec != null){
162 if(sec.getId() == 0){
163 getSession().save(sec);
164 }
165 criteria.add(Restrictions.eq("sec", sec ) );
166 }
167 if (queryString != null) {
168 criteria.add(Restrictions.ilike("name.nameCache", queryString));
169 }
170 List<TaxonBase> results = criteria.list();
171 return results;
172 }
173
174 public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {
175 Criteria crit = getSession().createCriteria(TaxonBase.class);
176 List<TaxonBase> results = crit.list();
177 // TODO add page & pagesize criteria
178 return results;
179 }
180
181 public List<Synonym> getAllSynonyms(Integer limit, Integer start) {
182 Criteria crit = getSession().createCriteria(Synonym.class);
183 List<Synonym> results = crit.list();
184 return results;
185 }
186
187 public List<Taxon> getAllTaxa(Integer limit, Integer start) {
188 Criteria crit = getSession().createCriteria(Taxon.class);
189 List<Taxon> results = crit.list();
190 return results;
191 }
192
193 public List<RelationshipBase> getAllRelationships(Integer limit, Integer start) {
194 Criteria crit = getSession().createCriteria(RelationshipBase.class);
195 List<RelationshipBase> results = crit.list();
196 return results;
197 }
198
199 @Override
200 public UUID delete(TaxonBase taxonBase) throws DataAccessException{
201 //getSession().update(taxonBase); doesn't work with lazy collections
202 //annotations
203 try {
204 Set<Annotation> annotations = taxonBase.getAnnotations();
205 for (Annotation annotation: annotations){
206 taxonBase.removeAnnotation(annotation);
207 }
208 } catch (LazyInitializationException e) {
209 logger.warn("LazyInitializationException: " + e);
210 }
211 //markers
212 try {
213 Set<Marker> markers = taxonBase.getMarkers();
214 for (Marker marker: markers){
215 taxonBase.removeMarker(marker);
216 }
217 } catch (LazyInitializationException e) {
218 logger.warn("LazyInitializationException: " + e);
219 }
220 //originalSource
221 try {
222 Set<OriginalSource> origSources = taxonBase.getSources();
223 for (OriginalSource source: origSources){
224 taxonBase.removeSource(source);
225 }
226 } catch (LazyInitializationException e) {
227 logger.warn("LazyInitializationException: " + e);
228 }
229 //is Taxon
230 taxonBase.getName().removeTaxonBase(taxonBase);
231 if (taxonBase instanceof Taxon){
232 //taxonRelationships
233 Taxon taxon = (Taxon)taxonBase;
234 Set<TaxonRelationship> taxRels = taxon.getTaxonRelations();
235 for (TaxonRelationship taxRel: taxRels){
236 taxon.removeTaxonRelation(taxRel);
237 } ;
238 //SynonymRelationships
239 Set<SynonymRelationship> synRels = taxon.getSynonymRelations();
240 for (SynonymRelationship synRel: synRels){
241 taxon.removeSynonymRelation(synRel);
242 } ;
243 }//is Synonym
244 else if (taxonBase instanceof Synonym){
245 Synonym synonym = (Synonym)taxonBase;
246 Set<SynonymRelationship> synRels = synonym.getSynonymRelations();
247 for (SynonymRelationship synRel: synRels){
248 synonym.removeSynonymRelation(synRel);
249 } ;
250 }
251 return super.delete(taxonBase);
252 }
253
254
255 // TODO add generic return type !!
256 public List findByName(String queryString, ITitledDao.MATCH_MODE matchMode, int page, int pagesize, boolean onlyAcccepted) {
257 ArrayList<Criterion> criteria = new ArrayList<Criterion>();
258 //TODO ... Restrictions.eq(propertyName, value)
259 return super.findByTitle(queryString, matchMode, page, pagesize, criteria);
260
261 }
262
263 public int countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted) {
264
265 Criteria crit = getSession().createCriteria(type);
266 crit.add(Restrictions.ilike("persistentTitleCache", matchMode.queryStringFrom(queryString)));
267 crit.setProjection(Projections.rowCount());
268 int result = ((Integer)crit.list().get(0)).intValue();
269 return result;
270 }
271
272
273 public int countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
274
275 Criteria crit = getSession().createCriteria(type);
276 crit.add(Restrictions.ilike("persistentTitleCache", matchMode.queryStringFrom(queryString)));
277 if(criteria != null){
278 for (Criterion criterion : criteria) {
279 crit.add(criterion);
280 }
281 }
282 crit.setProjection(Projections.rowCount());
283 int result = ((Integer)crit.list().get(0)).intValue();
284 return result;
285 }
286
287 public int countRelatedTaxa(Taxon taxon, TaxonRelationshipType type) {
288 Query query = null;
289
290 if(type == null) {
291 query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship.relatedTo = :relatedTo");
292 } else {
293 query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship.relatedTo = :relatedTo and taxonRelationship.type = :type");
294 query.setParameter("type",type);
295 }
296
297 query.setParameter("relatedTo", taxon);
298
299 return ((Long)query.uniqueResult()).intValue();
300 }
301
302 public int countSynonyms(Taxon taxon, SynonymRelationshipType type) {
303 Query query = null;
304
305 if(type == null) {
306 query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo");
307 } else {
308 query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo and synonymRelationship.type = :type");
309 query.setParameter("type",type);
310 }
311
312 query.setParameter("relatedTo", taxon);
313
314 return ((Long)query.uniqueResult()).intValue();
315 }
316
317 public int countTaxa(String queryString, Boolean accepted) {
318 QueryParser queryParser = new QueryParser("name.persistentTitleCache", new SimpleAnalyzer());
319
320 try {
321 org.apache.lucene.search.Query query = queryParser.parse(queryString);
322
323 FullTextSession fullTextSession = Search.createFullTextSession(this.getSession());
324 org.hibernate.search.FullTextQuery fullTextQuery = null;
325
326 if(accepted == null) {
327 fullTextQuery = fullTextSession.createFullTextQuery(query, TaxonBase.class);
328 } else {
329 if(accepted) {
330 fullTextQuery = fullTextSession.createFullTextQuery(query, Taxon.class);
331 } else {
332 fullTextQuery = fullTextSession.createFullTextQuery(query, Synonym.class);
333 }
334 }
335
336 Integer result = fullTextQuery.getResultSize();
337 return result;
338
339 } catch (ParseException e) {
340 throw new QueryParseException(e, queryString);
341 }
342 }
343
344 public int countTaxaByName(String queryString, Boolean accepted, ReferenceBase sec) {
345
346 Criteria criteria = null;
347
348 if (accepted == true) {
349 criteria = getSession().createCriteria(Taxon.class);
350 } else {
351 criteria = getSession().createCriteria(Synonym.class);
352 }
353
354 criteria.setFetchMode( "name", FetchMode.JOIN );
355 criteria.createAlias("name", "name");
356
357 if (sec != null){
358 if(sec.getId() == 0){
359 getSession().save(sec);
360 }
361 criteria.add(Restrictions.eq("sec", sec ) );
362 }
363 if (queryString != null) {
364 criteria.add(Restrictions.ilike("name.nameCache", queryString));
365 }
366 criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
367
368 return (Integer)criteria.uniqueResult();
369 }
370
371 public int countTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank) {
372 Criteria criteria = null;
373
374 if(accepted == null) {
375 criteria = getSession().createCriteria(TaxonBase.class);
376 } else {
377 if(accepted) {
378 criteria = getSession().createCriteria(Taxon.class);
379 } else {
380 criteria = getSession().createCriteria(Synonym.class);
381 }
382 }
383
384 criteria.setFetchMode( "name", FetchMode.JOIN );
385 criteria.createAlias("name", "name");
386
387 if(genusOrUninomial != null) {
388 criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
389 }
390
391 if(infraGenericEpithet != null) {
392 criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
393 }
394
395 if(specificEpithet != null) {
396 criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
397 }
398
399 if(infraSpecificEpithet != null) {
400 criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
401 }
402
403 if(rank != null) {
404 criteria.add(Restrictions.eq("name.rank", rank));
405 }
406
407 criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
408
409 return (Integer)criteria.uniqueResult();
410 }
411
412 public List<TaxonBase> findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize, Integer pageNumber) {
413 Criteria criteria = null;
414
415 if(accepted == null) {
416 criteria = getSession().createCriteria(TaxonBase.class);
417 } else {
418 if(accepted) {
419 criteria = getSession().createCriteria(Taxon.class);
420 } else {
421 criteria = getSession().createCriteria(Synonym.class);
422 }
423 }
424
425 criteria.setFetchMode( "name", FetchMode.JOIN );
426 criteria.createAlias("name", "name");
427
428 if(genusOrUninomial != null) {
429 criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
430 }
431
432 if(infraGenericEpithet != null) {
433 criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
434 } else {
435 criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
436 }
437
438 if(specificEpithet != null) {
439 criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
440 }
441
442 if(infraSpecificEpithet != null) {
443 criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
444 }
445
446 if(rank != null) {
447 criteria.add(Restrictions.eq("name.rank", rank));
448 }
449
450 if(pageSize != null) {
451 criteria.setMaxResults(pageSize);
452 if(pageNumber != null) {
453 criteria.setFirstResult(pageNumber * pageSize);
454 } else {
455 criteria.setFirstResult(0);
456 }
457 }
458
459 return (List<TaxonBase>)criteria.list();
460 }
461
462 public List<TaxonRelationship> getRelatedTaxa(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber) {
463 Query query = null;
464
465 if(type == null) {
466 query = getSession().createQuery("select taxonRelationship from TaxonRelationship taxonRelationship join fetch taxonRelationship.relatedFrom where taxonRelationship.relatedTo = :relatedTo");
467 } else {
468 query = getSession().createQuery("select taxonRelationship from TaxonRelationship taxonRelationship join fetch taxonRelationship.relatedFrom where taxonRelationship.relatedTo = :relatedTo and taxonRelationship.type = :type");
469 query.setParameter("type",type);
470 }
471
472 query.setParameter("relatedTo", taxon);
473
474 if(pageSize != null) {
475 query.setMaxResults(pageSize);
476 if(pageNumber != null) {
477 query.setFirstResult(pageNumber * pageSize);
478 } else {
479 query.setFirstResult(0);
480 }
481 }
482
483 return (List<TaxonRelationship>)query.list();
484 }
485
486 public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber) {
487 Query query = null;
488
489 if(type == null) {
490 query = getSession().createQuery("select synonymRelationship from SynonymRelationship synonymRelationship join fetch synonymRelationship.relatedFrom where synonymRelationship.relatedTo = :relatedTo");
491 } else {
492 query = getSession().createQuery("select synonymRelationship from SynonymRelationship synonymRelationship join fetch synonymRelationship.relatedFrom where synonymRelationship.relatedTo = :relatedTo and synonymRelationship.type = :type");
493 query.setParameter("type",type);
494 }
495
496 query.setParameter("relatedTo", taxon);
497
498 if(pageSize != null) {
499 query.setMaxResults(pageSize);
500 if(pageNumber != null) {
501 query.setFirstResult(pageNumber * pageSize);
502 } else {
503 query.setFirstResult(0);
504 }
505 }
506
507 return (List<SynonymRelationship>)query.list();
508 }
509
510 public List<TaxonBase> searchTaxa(String queryString, Boolean accepted, Integer pageSize, Integer pageNumber) {
511 QueryParser queryParser = new QueryParser("name.persistentTitleCache", new SimpleAnalyzer());
512 List<TaxonBase> results = new ArrayList<TaxonBase>();
513
514 try {
515 org.apache.lucene.search.Query query = queryParser.parse(queryString);
516
517 FullTextSession fullTextSession = Search.createFullTextSession(getSession());
518 org.hibernate.search.FullTextQuery fullTextQuery = null;
519 Criteria criteria = null;
520
521 if(accepted == null) {
522 fullTextQuery = fullTextSession.createFullTextQuery(query, TaxonBase.class);
523 criteria = getSession().createCriteria( TaxonBase.class );
524 } else {
525 if(accepted) {
526 fullTextQuery = fullTextSession.createFullTextQuery(query, Taxon.class);
527 criteria = getSession().createCriteria( Taxon.class );
528 } else {
529 fullTextQuery = fullTextSession.createFullTextQuery(query, Synonym.class);
530 criteria = getSession().createCriteria( Synonym.class );
531 }
532 }
533
534 org.apache.lucene.search.Sort sort = new Sort(new SortField("name.titleCache_forSort"));
535 fullTextQuery.setSort(sort);
536
537 criteria.setFetchMode( "name", FetchMode.JOIN );
538 fullTextQuery.setCriteriaQuery(criteria);
539
540 if(pageSize != null) {
541 fullTextQuery.setMaxResults(pageSize);
542 if(pageNumber != null) {
543 fullTextQuery.setFirstResult(pageNumber * pageSize);
544 } else {
545 fullTextQuery.setFirstResult(0);
546 }
547 }
548
549 return (List<TaxonBase>)fullTextQuery.list();
550
551 } catch (ParseException e) {
552 throw new QueryParseException(e, queryString);
553 }
554 }
555
556 public void purgeIndex() {
557 FullTextSession fullTextSession = Search.createFullTextSession(getSession());
558
559 fullTextSession.purgeAll(type); // remove all taxon base from indexes
560 // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA
561 }
562
563 public void rebuildIndex() {
564 FullTextSession fullTextSession = Search.createFullTextSession(getSession());
565
566 for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
567 Hibernate.initialize(taxonBase.getName());
568 fullTextSession.index(taxonBase);
569 }
570 // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA
571 }
572
573 public void optimizeIndex() {
574 FullTextSession fullTextSession = Search.createFullTextSession(getSession());
575 SearchFactory searchFactory = fullTextSession.getSearchFactory();
576 searchFactory.optimize(type); // optimize the indices ()
577 // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA
578 }
579
580 public String suggestQuery(String queryString) {
581 try {
582 String alternativeQueryString = null;
583 alternativeSpellingSuggestionParser.parse(queryString);
584 org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
585 if(alternativeQuery != null) {
586 alternativeQueryString = alternativeQuery.toString("name.persistentTitleCache");
587 }
588 return alternativeQueryString;
589 } catch (ParseException e) {
590 throw new QueryParseException(e, queryString);
591 }
592 }
593 }