2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.taxon
;
11 import java
.util
.ArrayList
;
12 import java
.util
.List
;
13 import java
.util
.UUID
;
15 import org
.hibernate
.query
.Query
;
16 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
17 import org
.springframework
.stereotype
.Repository
;
18 import org
.springframework
.util
.StringUtils
;
20 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
21 import eu
.etaxonomy
.cdm
.filter
.LogicFilter
;
22 import eu
.etaxonomy
.cdm
.filter
.LogicFilter
.Op
;
23 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
24 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
25 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
26 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
27 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
28 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
29 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
30 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
.CdmEntityDaoBase
;
31 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
32 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeFilterDao
;
33 import eu
.etaxonomy
.cdm
.persistence
.dao
.term
.IDefinedTermDao
;
36 * DAO to retrieve taxon node uuids according to a {@link TaxonNodeFilter}.
41 public class TaxonNodeFilterDaoHibernateImpl
42 extends CdmEntityDaoBase
<TaxonNode
>
43 implements ITaxonNodeFilterDao
{
45 private static final String DESCRIPTION_ELEMENTS
= "descriptionElements";
47 private static final String FEATURE_UUID
= "9fc9d10c-ba50-49ee-b174-ce83fc3f80c6";
49 private static final String HQL_TRUE
= " 1 "; //maybe we can/should use 'true' instead, needs to be checked for all DB types
50 private static final String HQL_FALSE
= " 0 "; //maybe we can/should use 'false' instead, needs to be checked for all DB types
53 private ITaxonNodeDao taxonNodeDao
;
56 private IDefinedTermDao termDao
;
58 public TaxonNodeFilterDaoHibernateImpl() {
59 super(TaxonNode
.class);
63 public long count(TaxonNodeFilter filter
){
64 String queryStr
= query(filter
, "count(*) as n ");
65 Query
<Long
> query
= getSession().createQuery(queryStr
, Long
.class);
66 long result
= query
.uniqueResult();
72 public List
<UUID
> listUuids(TaxonNodeFilter filter
){
73 String queryStr
= query(filter
, "tn.uuid");
74 Query
<UUID
> query
= getSession().createQuery(queryStr
, UUID
.class);
75 List
<UUID
> list
= query
.list();
77 list
= deduplicate(list
);
82 public List
<Integer
> idList(TaxonNodeFilter filter
){
83 String queryStr
= query(filter
, "tn.id");
84 Query
<Integer
> query
= getSession().createQuery(queryStr
, Integer
.class);
85 List
<Integer
> list
= query
.list();
86 list
= deduplicate(list
);
90 //maybe we will later want to have ordering included
91 private String
query(TaxonNodeFilter filter
, String selectPart
){
92 String select
= " SELECT " + selectPart
;
93 String from
= getFrom(filter
);
94 String subtreeFilter
= getSubtreeFilter(filter
);
95 String taxonNodeFilter
= getTaxonNodeFilter(filter
);
96 String classificationFilter
= getClassificationFilter(filter
);
97 String taxonFilter
= getTaxonFilter(filter
);
98 String rootNodeFilter
= getRootNodeFilter(filter
);
99 String rankMaxFilter
= getRankMaxFilter(filter
);
100 String rankMinFilter
= getRankMinFilter(filter
);
101 String areaFilter
= getAreaFilter(filter
);
102 String unpublishFilter
= getUnpublishFilter(filter
);
104 String fullFilter
= getFullFilter(subtreeFilter
, taxonNodeFilter
,
105 classificationFilter
, taxonFilter
,
106 rankMaxFilter
, rankMinFilter
, areaFilter
, rootNodeFilter
,
108 // String groupBy = " GROUP BY tn.uuid ";
110 String orderBy
= getOrderBy(filter
, selectPart
);
111 String fullQuery
= select
+ from
+ " WHERE " + fullFilter
+ groupBy
+ orderBy
;
116 private String
getOrderBy(TaxonNodeFilter filter
, String selectPart
) {
118 if (filter
.getOrderBy()!= null && !selectPart
.contains("count")){
119 orderBy
= "ORDER BY " + filter
.getOrderBy().getHql();
124 private String
getFrom(TaxonNodeFilter filter
){
125 String from
= " FROM TaxonNode tn ";
126 if (hasTaxonFilter(filter
)){
127 from
+= " LEFT JOIN tn.taxon taxon "; //LEFT to allow includeRootNode
129 if(!filter
.getAreaFilter().isEmpty()){
130 from
+= " INNER JOIN taxon.descriptions descriptions "
131 + " INNER JOIN descriptions.descriptionElements " + DESCRIPTION_ELEMENTS
+ " ";
136 private boolean hasTaxonFilter(TaxonNodeFilter filter
) {
137 boolean result
= !filter
.getAreaFilter().isEmpty()
138 || !filter
.isIncludeUnpublished();
142 private String
getAreaFilter(TaxonNodeFilter filter
) {
144 List
<LogicFilter
<NamedArea
>> areaFilter
= filter
.getAreaFilter();
145 boolean isFirst
= true;
146 List
<Integer
> areaIds
= new ArrayList
<>();
147 for (LogicFilter
<NamedArea
> singleFilter
: areaFilter
){
148 areaIds
= getChildAreasRecursively(singleFilter
.getUuid());
149 String op
= isFirst ?
"" : op2Hql(singleFilter
.getOperator());
150 result
= String
.format("(%s%s(" + DESCRIPTION_ELEMENTS
+ ".feature.uuid='" + FEATURE_UUID
+ "' "
151 + " AND " + DESCRIPTION_ELEMENTS
+ ".area.id in (%s)))"
152 + " AND " + DESCRIPTION_ELEMENTS
+ ".status.absenceTerm = %s " ,
153 result
, op
, StringUtils
.collectionToCommaDelimitedString(areaIds
),
160 private List
<Integer
> getChildAreasRecursively(UUID uuid
){
161 List
<Integer
> areaIds
= new ArrayList
<>();
162 NamedArea area
= HibernateProxyHelper
.deproxy(termDao
.load(uuid
), NamedArea
.class);
163 areaIds
.add(area
.getId());
164 String queryStr
= String
.format("SELECT includes.uuid FROM DefinedTermBase t inner join t.includes includes WHERE t.uuid = '%s'",
165 area
.getUuid().toString());
166 Query
<UUID
> query
= getSession().createQuery(queryStr
, UUID
.class);
167 List
<UUID
> childAreas
= query
.list();
168 for (UUID childArea
: childAreas
) {
169 areaIds
.addAll(getChildAreasRecursively(childArea
));
181 private String
getRootNodeFilter(TaxonNodeFilter filter
) {
183 if (!filter
.isIncludeRootNodes()){
184 result
= " ( tn.parent IS NOT NULL ) ";
189 private String
getUnpublishFilter(TaxonNodeFilter filter
) {
191 if (!filter
.isIncludeUnpublished()){
192 result
= " ( taxon.publish = "+HQL_TRUE
+" OR tn.parent IS NULL ) ";
199 * @param subtreeFilter
200 * @param taxonNodeFilter
201 * @param classificationFilter
203 * @param rankMinFilter
204 * @param rankMaxFilter
205 * @param rootNodeFilter
208 private String
getFullFilter(String subtreeFilter
, String taxonNodeFilter
, String classificationFilter
,
209 String taxonFilter
, String rankMaxFilter
, String rankMinFilter
, String areaFilter
, String rootNodeFilter
,
210 String unpublishFilter
) {
211 String result
= " (1=1 ";
212 result
= CdmUtils
.concat(") AND (", result
, subtreeFilter
, taxonNodeFilter
,
213 classificationFilter
, taxonFilter
, rankMaxFilter
, rankMinFilter
, areaFilter
, rootNodeFilter
,
214 unpublishFilter
) + ") ";
223 private <T
> List
<T
> deduplicate(List
<T
> list
) {
224 List
<T
> result
= new ArrayList
<>();
226 if (!result
.contains(uuid
)){
233 private String
getSubtreeFilter(TaxonNodeFilter filter
) {
235 List
<LogicFilter
<TaxonNode
>> subtreeFilter
= filter
.getSubtreeFilter();
236 initializeSubtreeIndex(subtreeFilter
);
237 boolean isFirst
= true;
238 for (LogicFilter
<TaxonNode
> singleFilter
: subtreeFilter
){
239 String treeIndex
= singleFilter
.getTreeIndex();
240 String op
= isFirst ?
"" : op2Hql(singleFilter
.getOperator());
241 if (treeIndex
!= null){
242 result
= String
.format("(%s%s(tn.treeIndex like '%s%%'))", result
, op
, treeIndex
);
244 result
= String
.format("(%s%s(%s))", result
, op
, "(1=0)");
251 private String
getTaxonNodeFilter(TaxonNodeFilter filter
) {
253 List
<LogicFilter
<TaxonNode
>> taxonNodeFilter
= filter
.getTaxonNodesFilter();
254 boolean isFirst
= true;
255 for (LogicFilter
<TaxonNode
> singleFilter
: taxonNodeFilter
){
256 String uuid
= singleFilter
.getUuid().toString();
257 String op
= isFirst ?
"" : op2Hql(singleFilter
.getOperator());
258 result
= String
.format("(%s%s(tn.uuid = '%s'))", result
, op
, uuid
);
264 private String
getRankMaxFilter(TaxonNodeFilter filter
) {
266 LogicFilter
<Rank
> rankFilter
= filter
.getRankMax();
267 if(rankFilter
!=null){
268 UUID rankUuid
= rankFilter
.getUuid();
269 Rank rank
= (Rank
) termDao
.load(rankUuid
);
270 result
= String
.format("(tn.taxon.name.rank.orderIndex >= %s)", rank
.getOrderIndex());
275 private String
getRankMinFilter(TaxonNodeFilter filter
) {
277 LogicFilter
<Rank
> rankFilter
= filter
.getRankMin();
278 if(rankFilter
!=null){
279 UUID rankUuid
= rankFilter
.getUuid();
280 Rank rank
= (Rank
) termDao
.load(rankUuid
);
281 result
= String
.format("(tn.taxon.name.rank.orderIndex <= %s)", rank
.getOrderIndex());
286 private String
getClassificationFilter(TaxonNodeFilter filter
) {
288 List
<LogicFilter
<Classification
>> classificationFilter
= filter
.getClassificationFilter();
289 boolean isFirst
= true;
290 for (LogicFilter
<Classification
> singleFilter
: classificationFilter
){
291 String uuid
= singleFilter
.getUuid().toString();
292 String op
= isFirst ?
"" : op2Hql(singleFilter
.getOperator());
293 result
= String
.format("(%s%s(tn.classification.uuid = '%s'))", result
, op
, uuid
);
299 private String
getTaxonFilter(TaxonNodeFilter filter
) {
301 List
<LogicFilter
<Taxon
>> taxonFilter
= filter
.getTaxonFilter();
302 boolean isFirst
= true;
303 for (LogicFilter
<Taxon
> singleFilter
: taxonFilter
){
304 String uuid
= singleFilter
.getUuid().toString();
305 String op
= isFirst ?
"" : op2Hql(singleFilter
.getOperator());
306 result
= String
.format("(%s%s(tn.taxon.uuid = '%s'))", result
, op
, uuid
);
307 // System.out.println(result);
313 private void initializeSubtreeIndex(List
<LogicFilter
<TaxonNode
>> subtreeFilter
) {
314 for (LogicFilter
<TaxonNode
> filter
: subtreeFilter
){
315 if (filter
.getTreeIndex() == null){
316 //TODO finde without loading, best be sending full list and returning tree indexes
317 TaxonNode node
= taxonNodeDao
.findByUuid(filter
.getUuid());
319 filter
.setTreeIndex(node
.treeIndex());
326 * Returns the HQL string for this operation
328 private String
op2Hql(Op op
){
329 return op
== Op
.NOT ?
" AND NOT " : op
.toString();