deduplicate all results in vocabulary dao
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / term / TermVocabularyDaoImpl.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.term;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.UUID;
19
20 import org.hibernate.Criteria;
21 import org.hibernate.Query;
22 import org.hibernate.Session;
23 import org.hibernate.criterion.CriteriaSpecification;
24 import org.hibernate.criterion.Restrictions;
25 import org.hibernate.envers.query.AuditEntity;
26 import org.hibernate.envers.query.AuditQuery;
27 import org.springframework.stereotype.Repository;
28
29 import eu.etaxonomy.cdm.model.common.CdmClass;
30 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
31 import eu.etaxonomy.cdm.model.term.OrderedTermVocabulary;
32 import eu.etaxonomy.cdm.model.term.TermType;
33 import eu.etaxonomy.cdm.model.term.TermVocabulary;
34 import eu.etaxonomy.cdm.model.view.AuditEvent;
35 import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
36 import eu.etaxonomy.cdm.persistence.dao.term.ITermVocabularyDao;
37 import eu.etaxonomy.cdm.persistence.dto.FeatureDto;
38 import eu.etaxonomy.cdm.persistence.dto.TermCollectionDto;
39 import eu.etaxonomy.cdm.persistence.dto.TermDto;
40 import eu.etaxonomy.cdm.persistence.dto.TermVocabularyDto;
41 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
42 import eu.etaxonomy.cdm.persistence.query.OrderHint;
43
44 /**
45 * @author a.mueller
46 */
47 @Repository
48 public class TermVocabularyDaoImpl extends IdentifiableDaoBase<TermVocabulary> implements
49 ITermVocabularyDao {
50
51 @SuppressWarnings("unchecked")
52 public TermVocabularyDaoImpl() {
53 super(TermVocabulary.class);
54 indexedClasses = new Class[2];
55 indexedClasses[0] = TermVocabulary.class;
56 indexedClasses[1] = OrderedTermVocabulary.class;
57 }
58
59 @Override
60 public long countTerms(TermVocabulary termVocabulary) {
61 AuditEvent auditEvent = getAuditEventFromContext();
62 if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
63 Query query = getSession().createQuery("select count(term) from DefinedTermBase term where term.vocabulary = :vocabulary");
64 query.setParameter("vocabulary", termVocabulary);
65
66 return (Long)query.uniqueResult();
67 } else {
68 AuditQuery query = makeAuditQuery(null, auditEvent);
69 query.addProjection(AuditEntity.id().count());
70 query.add(AuditEntity.relatedId("vocabulary").eq(termVocabulary.getId()));
71 return (Long)query.getSingleResult();
72 }
73 }
74
75 @Override
76 public <T extends DefinedTermBase> List<T> getTerms(TermVocabulary<T> vocabulary,Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
77 AuditEvent auditEvent = getAuditEventFromContext();
78 if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
79 Criteria criteria = getCriteria(DefinedTermBase.class);
80 criteria.createCriteria("vocabulary").add(Restrictions.idEq(vocabulary.getId()));
81
82 addPageSizeAndNumber(criteria, pageSize, pageNumber);
83 this.addOrder(criteria, orderHints);
84
85 @SuppressWarnings("unchecked")
86 List<T> result = DefinedTermDaoImpl.deduplicateResult(criteria.list());
87 defaultBeanInitializer.initializeAll(result, propertyPaths);
88 return result;
89 } else {
90 AuditQuery query = makeAuditQuery(null, auditEvent);
91 query.add(AuditEntity.relatedId("vocabulary").eq(vocabulary.getId()));
92
93 addPageSizeAndNumber(query, pageSize, pageNumber);
94
95 @SuppressWarnings("unchecked")
96 List<T> result = DefinedTermDaoImpl.deduplicateResult(query.getResultList());
97 defaultBeanInitializer.initializeAll(result, propertyPaths);
98 return result;
99 }
100 }
101
102 @Override
103 public <T extends DefinedTermBase> TermVocabulary<T> findByUri(String termSourceUri, Class<T> clazz) {
104 AuditEvent auditEvent = getAuditEventFromContext();
105 if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
106 //TODO use clazz
107 Query query = getSession().createQuery("select vocabulary from TermVocabulary vocabulary where vocabulary.termSourceUri= :termSourceUri");
108 query.setParameter("termSourceUri", termSourceUri);
109
110 @SuppressWarnings("unchecked")
111 TermVocabulary<T> result = (TermVocabulary<T>)query.uniqueResult();
112 return result;
113 } else {
114 AuditQuery query = makeAuditQuery(clazz, auditEvent);
115 query.add(AuditEntity.property("termSourceUri").eq(termSourceUri));
116
117 @SuppressWarnings("unchecked")
118 TermVocabulary<T> result = (TermVocabulary<T>)query.getSingleResult();
119 return result;
120 }
121 }
122
123 @Override
124 public <T extends DefinedTermBase> List<T> getTerms(TermVocabulary<T> termVocabulary, Integer pageSize, Integer pageNumber) {
125 return getTerms(termVocabulary, pageSize, pageNumber, null, null);
126 }
127
128 @Override
129 public <T extends DefinedTermBase> List<TermVocabulary<T>> findByTermType(TermType termType, List<String> propertyPaths) {
130
131 Criteria criteria = getSession().createCriteria(type);
132 criteria.add(Restrictions.eq("termType", termType));
133 criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
134 //this.addOrder(criteria, orderHints);
135
136 @SuppressWarnings("unchecked")
137 List<TermVocabulary<T>> result = DefinedTermDaoImpl.deduplicateResult(criteria.list());
138 defaultBeanInitializer.initializeAll(result, propertyPaths);
139 return result;
140 }
141
142 @Override
143 public List<TermVocabulary> listByTermType(TermType termType, boolean includeSubTypes, Integer limit, Integer start,List<OrderHint> orderHints, List<String> propertyPaths) {
144 checkNotInPriorView("TermVocabularyDao.listByTermType(TermType termType, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths)");
145
146 Set<TermType> allTermTypes = new HashSet<TermType>();
147 allTermTypes.add(termType);
148 if (includeSubTypes){
149 allTermTypes.addAll(termType.getGeneralizationOf(true));
150 }
151
152 Criteria criteria = getSession().createCriteria(type);
153 criteria.add(Restrictions.in("termType", allTermTypes));
154
155 if(limit != null) {
156 criteria.setMaxResults(limit);
157 if(start != null) {
158 criteria.setFirstResult(start);
159 }
160 }
161
162 this.addOrder(criteria, orderHints);
163
164 @SuppressWarnings("unchecked")
165 List<TermVocabulary> result = DefinedTermDaoImpl.deduplicateResult(criteria.list());
166 defaultBeanInitializer.initializeAll(result, propertyPaths);
167 return result;
168 }
169
170 @Override
171 public void missingTermUuids(
172 Map<UUID, Set<UUID>> uuidsRequested,
173 Map<UUID, Set<UUID>> uuidMissingTermsRepsonse,
174 Map<UUID, TermVocabulary<?>> vocabularyResponse){
175
176 Set<UUID> missingTermCandidateUuids = new HashSet<>();
177
178 for (Set<UUID> uuidsPerVocSet : uuidsRequested.values()){
179 missingTermCandidateUuids.addAll(uuidsPerVocSet);
180 }
181
182 //search persisted subset of required (usually all)
183 String hql = " SELECT terms.uuid " +
184 " FROM TermVocabulary voc join voc.terms terms " +
185 " WHERE terms.uuid IN (:uuids) " +
186 " ORDER BY voc.uuid ";
187 Query query = getSession().createQuery(hql);
188
189 int splitSize = 2000;
190 List<Collection<UUID>> missingTermCandidates = splitCollection(missingTermCandidateUuids, splitSize);
191 List<UUID> persistedUuids = new ArrayList<>();
192
193 for (Collection<UUID> uuids : missingTermCandidates){
194 query.setParameterList("uuids", uuids);
195 @SuppressWarnings("unchecked")
196 List<UUID> list = query.list();
197 persistedUuids.addAll(list);
198 }
199
200
201 //fully load and initialize vocabularies if required
202 if (vocabularyResponse != null){
203 String hql2 = " SELECT DISTINCT voc " +
204 " FROM TermVocabulary voc " +
205 " LEFT JOIN FETCH voc.terms terms " +
206 " LEFT JOIN FETCH terms.representations representations " +
207 " LEFT JOIN FETCH voc.representations vocReps " +
208 " WHERE terms.uuid IN (:termUuids) OR ( voc.uuid IN (:vocUuids) ) " + //was: AND voc.terms is empty, but did not load originally empty vocabularies with user defined terms added
209 // " WHERE voc.uuid IN (:vocUuids) AND voc.terms is empty " +
210 " ORDER BY voc.uuid ";
211 query = getSession().createQuery(hql2);
212 query.setParameterList("termUuids", missingTermCandidateUuids);
213 query.setParameterList("vocUuids", uuidsRequested.keySet());
214
215 for (Collection<UUID> uuids : missingTermCandidates){
216 query.setParameterList("termUuids", uuids);
217 @SuppressWarnings("unchecked")
218 List<TermVocabulary<?>> o = query.list();
219 for (TermVocabulary<?> voc : o){
220 vocabularyResponse.put(voc.getUuid(), voc);
221 }
222 }
223 }
224
225 //compute missing terms
226 if (missingTermCandidateUuids.size() == persistedUuids.size()){
227 missingTermCandidateUuids.clear();
228 }else{
229 missingTermCandidateUuids.removeAll(persistedUuids);
230 //add missing terms to response
231 for (UUID vocUUID : uuidsRequested.keySet()){
232 for (UUID termUuid : uuidsRequested.get(vocUUID)){
233 if (missingTermCandidateUuids.contains(termUuid)){
234 Set<UUID> r = uuidMissingTermsRepsonse.get(vocUUID);
235 if (r == null){
236 r = new HashSet<>();
237 uuidMissingTermsRepsonse.put(vocUUID, r);
238 }
239 r.add(termUuid);
240 }
241 }
242 }
243 }
244
245 return;
246 }
247
248 @Override
249 public Collection<TermDto> getTerms(List<UUID> vocabularyUuids) {
250 String queryString = TermDto.getTermDtoSelect()
251 + "where v.uuid in :vocabularyUuids "
252 + "order by a.titleCache";
253 Query query = getSession().createQuery(queryString);
254 query.setParameterList("vocabularyUuids", vocabularyUuids);
255
256 @SuppressWarnings("unchecked")
257 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
258
259 List<TermDto> list = TermDto.termDtoListFrom(result);
260 return list;
261 }
262
263 @Override
264 public Collection<TermDto> getTerms(UUID vocabularyUuid) {
265 String queryString = TermDto.getTermDtoSelect()
266 + "where v.uuid = :vocabularyUuid "
267 + "order by a.titleCache";
268 Query query = getSession().createQuery(queryString);
269 query.setParameter("vocabularyUuid", vocabularyUuid);
270
271 @SuppressWarnings("unchecked")
272 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
273
274 List<TermDto> list = TermDto.termDtoListFrom(result);
275 return list;
276 }
277
278 @Override
279 public Collection<TermDto> getNamedAreaTerms(List<UUID> vocabularyUuids) {
280 String queryString = TermDto.getTermDtoSelectNamedArea()
281 + "where v.uuid in :vocabularyUuids "
282 + "order by a.titleCache";
283 Query query = getSession().createQuery(queryString);
284 query.setParameterList("vocabularyUuids", vocabularyUuids);
285
286 @SuppressWarnings("unchecked")
287 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
288
289 List<TermDto> list = TermDto.termDtoListFrom(result);
290 return list;
291 }
292
293 @Override
294 public Collection<TermDto> getTopLevelTerms(UUID vocabularyUuid) {
295 String queryString = TermDto.getTermDtoSelect()
296 + "where v.uuid = :vocabularyUuid "
297 + "and a.partOf is null "
298 + "and a.kindOf is null";
299 Query query = getSession().createQuery(queryString);
300 query.setParameter("vocabularyUuid", vocabularyUuid);
301
302 @SuppressWarnings("unchecked")
303 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
304
305 List<TermDto> list = TermDto.termDtoListFrom(result);
306 return list;
307 }
308
309 @Override
310 public Collection<TermDto> getTopLevelTerms(UUID vocabularyUuid, TermType type) {
311 String queryString;
312 if (type.equals(TermType.NamedArea)){
313 queryString = TermDto.getTermDtoSelectNamedArea();
314 }else if (type.equals(TermType.Feature) || type.isKindOf(TermType.Feature)){
315 queryString = FeatureDto.getTermDtoSelect();
316 }else{
317 queryString = TermDto.getTermDtoSelect();
318 }
319 queryString = queryString
320 + "where v.uuid = :vocabularyUuid "
321 + "and a.partOf is null "
322 + "and a.kindOf is null";
323 Query query = getSession().createQuery(queryString);
324 query.setParameter("vocabularyUuid", vocabularyUuid);
325
326 @SuppressWarnings("unchecked")
327 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
328 List<TermDto> list = null;
329 if (type.equals(TermType.Feature)|| type.isKindOf(TermType.Feature)){
330 list = FeatureDto.termDtoListFrom(result);
331 }else{
332 list = TermDto.termDtoListFrom(result);
333 }
334 return list;
335 }
336
337 @Override
338 public List<TermVocabularyDto> findVocabularyDtoByTermTypes(Set<TermType> termTypes) {
339 return findVocabularyDtoByTermTypes(termTypes, true);
340 }
341
342 @Override
343 public List<TermVocabularyDto> findVocabularyDtoByTermTypes(Set<TermType> termTypes, boolean includeSubtypes) {
344 return findVocabularyDtoByTermTypes(termTypes, null, includeSubtypes);
345 }
346
347 @Override
348 public List<TermVocabularyDto> findVocabularyDtoByAvailableFor(Set<CdmClass> availableForSet) {
349
350 String queryVocWithFittingTerms = "SELECT DISTINCT(v.uuid) FROM DefinedTermBase term JOIN term.vocabulary as v WHERE " ;
351 for (CdmClass availableFor: availableForSet){
352 queryVocWithFittingTerms += " term.availableFor like '%"+availableFor.getKey()+"%' AND term.termType = :feature";
353
354 }
355
356 // Query query1 = getSession().createQuery(queryVocWithFittingTerms);
357 // List<Object[]> result1 = query1.list();
358
359 String queryString = TermCollectionDto.getTermCollectionDtoSelect()
360 + " WHERE a.uuid in "
361 + " (" + queryVocWithFittingTerms + ")";
362
363
364
365
366 Query query = getSession().createQuery(queryString);
367 query.setParameter("feature", TermType.Feature);
368
369 @SuppressWarnings("unchecked")
370 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
371
372 // Map<UUID, TermVocabularyDto> dtoMap = new HashMap<>(result.size());
373 List<TermVocabularyDto> dtos = TermVocabularyDto.termVocabularyDtoListFrom(result);
374
375 return dtos;
376 }
377
378
379 @Override
380 public List<TermVocabularyDto> findVocabularyDtoByTermTypes(Set<TermType> termTypes, String pattern, boolean includeSubtypes) {
381 Set<TermType> termTypeWithSubType = new HashSet<>();
382 if (! (termTypes.isEmpty() || (termTypes.size() == 1 && termTypes.iterator().next() == null))){
383 termTypeWithSubType = new HashSet<>(termTypes);
384 }
385
386 if(includeSubtypes){
387 if (!termTypes.isEmpty()){
388 for (TermType termType : termTypes) {
389 if (termType != null){
390 termTypeWithSubType.addAll(termType.getGeneralizationOf(true));
391 }
392 }
393 }
394 }
395 String queryString = TermVocabularyDto.getTermCollectionDtoSelect();
396
397 if (!termTypeWithSubType.isEmpty()){
398 queryString += " where a.termType in (:termTypes) ";
399 if (pattern != null){
400 queryString += " AND a.titleCache like :pattern";
401 }
402 }else{
403 if (pattern != null){
404 queryString += " WHERE a.titleCache like :pattern";
405 }
406 }
407
408 Query query = getSession().createQuery(queryString);
409 if (!termTypeWithSubType.isEmpty()){
410 query.setParameterList("termTypes", termTypeWithSubType);
411 }
412 if (pattern != null){
413 pattern = pattern.replace("*", "%");
414 pattern = "%"+pattern+"%";
415 query.setParameter("pattern", pattern);
416 }
417 @SuppressWarnings("unchecked")
418 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
419 List<TermVocabularyDto> dtos = TermVocabularyDto.termVocabularyDtoListFrom(result);
420 return dtos;
421
422 // Map<UUID, TermVocabularyDto> dtoMap = new HashMap<>(result.size());
423 // for (Object[] elements : result) {
424 // UUID uuid = (UUID)elements[0];
425 // TermType termType = (TermType)elements[1];
426 // if(dtoMap.containsKey(uuid)){
427 // dtoMap.get(uuid).addRepresentation((Representation)elements[2]);
428 // } else {
429 // Set<Representation> representations = new HashSet<>();
430 // if(elements[2] instanceof Representation) {
431 // representations = new HashSet<Representation>();
432 // representations.add((Representation)elements[2]);
433 // } else {
434 // representations = (Set<Representation>)elements[2];
435 // }
436 // dtoMap.put(uuid, new TermVocabularyDto(uuid, representations, termType, (String)elements[3]));
437 // }
438 // }
439 // return new ArrayList<>(dtoMap.values());
440 }
441
442 @Override
443 public List<TermVocabularyDto> findVocabularyDtoByTermType(TermType termType) {
444 return findVocabularyDtoByTermTypes(Collections.singleton(termType));
445 }
446
447 @Override
448 public <S extends TermVocabulary> List<UuidAndTitleCache<S>> getUuidAndTitleCache(Class<S> clazz, TermType termType,
449 Integer limit, String pattern) {
450 if(termType==null){
451 return getUuidAndTitleCache(clazz, limit, pattern);
452 }
453 Session session = getSession();
454 Query query = null;
455 if (pattern != null){
456 query = session.createQuery(
457 " SELECT uuid, id, titleCache "
458 + " FROM " + clazz.getSimpleName()
459 + " WHERE titleCache LIKE :pattern "
460 + " AND termType = :termType");
461 pattern = pattern.replace("*", "%");
462 pattern = pattern.replace("?", "_");
463 pattern = pattern + "%";
464 query.setParameter("pattern", pattern);
465 } else {
466 query = session.createQuery(
467 " SELECT uuid, id, titleCache "
468 + " FROM " + clazz.getSimpleName()
469 + " WHERE termType = :termType");
470 }
471 query.setParameter("termType", termType);
472 if (limit != null){
473 query.setMaxResults(limit);
474 }
475 return getUuidAndTitleCache(query);
476 }
477
478 @Override
479 public TermVocabularyDto findVocabularyDtoByUuid(UUID vocUuid) {
480 if (vocUuid == null ){
481 return null;
482 }
483
484 String queryString = TermCollectionDto.getTermCollectionDtoSelect()
485 + " where a.uuid like :uuid ";
486 // + "order by a.titleCache";
487 Query query = getSession().createQuery(queryString);
488 query.setParameter("uuid", vocUuid);
489
490 @SuppressWarnings("unchecked")
491 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
492 if (result.size() == 1){
493 return TermVocabularyDto.termVocabularyDtoListFrom(result).get(0);
494 }
495 return null;
496 }
497
498 @Override
499 public List<TermVocabularyDto> findVocabularyDtoByUuids(List<UUID> vocUuids) {
500
501 if (vocUuids == null || vocUuids.isEmpty()){
502 return null;
503 }
504 List<TermVocabularyDto> list = new ArrayList<>();
505
506 String queryString = TermCollectionDto.getTermCollectionDtoSelect()
507 + "where a.uuid in :uuidList ";
508 // + "order by a.titleCache";
509 Query query = getSession().createQuery(queryString);
510 query.setParameterList("uuidList", vocUuids);
511
512 @SuppressWarnings("unchecked")
513 List<Object[]> result = DefinedTermDaoImpl.deduplicateResult(query.list());
514
515 list = TermVocabularyDto.termVocabularyDtoListFrom(result);
516 return list;
517
518 }
519 }