cleanup
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / taxon / TaxonNodeFilterDaoHibernateImpl.java
1 /**
2 * Copyright (C) 2017 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.UUID;
14
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;
19
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;
34
35 /**
36 * DAO to retrieve taxon node uuids according to a {@link TaxonNodeFilter}.
37 *
38 * @author a.mueller
39 */
40 @Repository
41 public class TaxonNodeFilterDaoHibernateImpl
42 extends CdmEntityDaoBase<TaxonNode>
43 implements ITaxonNodeFilterDao {
44
45 private static final String DESCRIPTION_ELEMENTS = "descriptionElements";
46
47 private static final String FEATURE_UUID = "9fc9d10c-ba50-49ee-b174-ce83fc3f80c6";
48
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
51
52 @Autowired
53 private ITaxonNodeDao taxonNodeDao;
54
55 @Autowired
56 private IDefinedTermDao termDao;
57
58 public TaxonNodeFilterDaoHibernateImpl() {
59 super(TaxonNode.class);
60 }
61
62 @Override
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();
67
68 return result;
69 }
70
71 @Override
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();
76
77 list = deduplicate(list);
78 return list;
79 }
80
81 @Override
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);
87 return list;
88 }
89
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);
103
104 String fullFilter = getFullFilter(subtreeFilter, taxonNodeFilter,
105 classificationFilter, taxonFilter,
106 rankMaxFilter, rankMinFilter, areaFilter, rootNodeFilter,
107 unpublishFilter);
108 // String groupBy = " GROUP BY tn.uuid ";
109 String groupBy = "";
110 String orderBy = getOrderBy(filter, selectPart);
111 String fullQuery = select + from + " WHERE " + fullFilter + groupBy + orderBy;
112
113 return fullQuery;
114 }
115
116 private String getOrderBy(TaxonNodeFilter filter, String selectPart) {
117 String orderBy = "";
118 if (filter.getOrderBy()!= null && !selectPart.contains("count")){
119 orderBy = "ORDER BY " + filter.getOrderBy().getHql();
120 }
121 return orderBy;
122 }
123
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
128 }
129 if(!filter.getAreaFilter().isEmpty()){
130 from += " INNER JOIN taxon.descriptions descriptions "
131 + " INNER JOIN descriptions.descriptionElements " + DESCRIPTION_ELEMENTS + " ";
132 }
133 return from;
134 }
135
136 private boolean hasTaxonFilter(TaxonNodeFilter filter) {
137 boolean result = !filter.getAreaFilter().isEmpty()
138 || !filter.isIncludeUnpublished();
139 return result;
140 }
141
142 private String getAreaFilter(TaxonNodeFilter filter) {
143 String result = "";
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),
154 HQL_FALSE);
155 isFirst = false;
156 }
157 return result;
158 }
159
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));
170 }
171 return areaIds;
172 }
173
174
175
176
177 /**
178 * @param filter
179 * @return
180 */
181 private String getRootNodeFilter(TaxonNodeFilter filter) {
182 String result = "";
183 if (!filter.isIncludeRootNodes()){
184 result = " ( tn.parent IS NOT NULL ) ";
185 }
186 return result;
187 }
188
189 private String getUnpublishFilter(TaxonNodeFilter filter) {
190 String result = "";
191 if (!filter.isIncludeUnpublished()){
192 result = " ( taxon.publish = "+HQL_TRUE+" OR tn.parent IS NULL ) ";
193 }
194 return result;
195 }
196
197
198 /**
199 * @param subtreeFilter
200 * @param taxonNodeFilter
201 * @param classificationFilter
202 * @param taxonFilter
203 * @param rankMinFilter
204 * @param rankMaxFilter
205 * @param rootNodeFilter
206 * @return
207 */
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) + ") ";
215 return result;
216 }
217
218
219 /**
220 * @param list
221 * @return
222 */
223 private <T> List<T> deduplicate(List<T> list) {
224 List<T> result = new ArrayList<>();
225 for (T uuid : list){
226 if (!result.contains(uuid)){
227 result.add(uuid);
228 }
229 }
230 return result;
231 }
232
233 private String getSubtreeFilter(TaxonNodeFilter filter) {
234 String result = "";
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);
243 }else{
244 result = String.format("(%s%s(%s))", result, op, "(1=0)");
245 }
246 isFirst = false;
247 }
248 return result;
249 }
250
251 private String getTaxonNodeFilter(TaxonNodeFilter filter) {
252 String result = "";
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);
259 isFirst = false;
260 }
261 return result;
262 }
263
264 private String getRankMaxFilter(TaxonNodeFilter filter) {
265 String result = "";
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());
271 }
272 return result;
273 }
274
275 private String getRankMinFilter(TaxonNodeFilter filter) {
276 String result = "";
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());
282 }
283 return result;
284 }
285
286 private String getClassificationFilter(TaxonNodeFilter filter) {
287 String result = "";
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);
294 isFirst = false;
295 }
296 return result;
297 }
298
299 private String getTaxonFilter(TaxonNodeFilter filter) {
300 String result = "";
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);
308 isFirst = false;
309 }
310 return result;
311 }
312
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());
318 if (node != null){
319 filter.setTreeIndex(node.treeIndex());
320 }
321 }
322 }
323 }
324
325 /**
326 * Returns the HQL string for this operation
327 */
328 private String op2Hql(Op op){
329 return op == Op.NOT ? " AND NOT " : op.toString();
330 }
331 }