1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.backend.jpa.objectinfo;
20
21 import java.sql.Date;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.stream.Collectors;
27
28 import org.mycore.backend.jpa.MCREntityManagerProvider;
29 import org.mycore.datamodel.classifications2.MCRCategLinkReference_;
30 import org.mycore.datamodel.classifications2.MCRCategoryID;
31 import org.mycore.datamodel.classifications2.MCRCategoryID_;
32 import org.mycore.datamodel.classifications2.impl.MCRCategoryImpl;
33 import org.mycore.datamodel.classifications2.impl.MCRCategoryImpl_;
34 import org.mycore.datamodel.classifications2.impl.MCRCategoryLinkImpl;
35 import org.mycore.datamodel.classifications2.impl.MCRCategoryLinkImpl_;
36 import org.mycore.datamodel.common.MCRObjectIDDate;
37 import org.mycore.datamodel.ifs2.MCRObjectIDDateImpl;
38 import org.mycore.datamodel.metadata.MCRObjectID;
39 import org.mycore.datamodel.objectinfo.MCRObjectInfo;
40 import org.mycore.datamodel.objectinfo.MCRObjectQuery;
41 import org.mycore.datamodel.objectinfo.MCRObjectQueryResolver;
42
43 import jakarta.persistence.EntityManager;
44 import jakarta.persistence.TypedQuery;
45 import jakarta.persistence.criteria.CriteriaBuilder;
46 import jakarta.persistence.criteria.CriteriaQuery;
47 import jakarta.persistence.criteria.Predicate;
48 import jakarta.persistence.criteria.Root;
49 import jakarta.persistence.metamodel.SingularAttribute;
50
51 public class MCRObjectInfoEntityQueryResolver implements MCRObjectQueryResolver {
52
53 private static final MCRObjectQuery.SortBy SORT_BY_DEFAULT = MCRObjectQuery.SortBy.created;
54
55 protected TypedQuery<MCRObjectInfoEntity> convertQuery(MCRObjectQuery query) {
56 Objects.requireNonNull(query, "The Query cant be null");
57 int offset = query.offset();
58
59 if (offset != -1 && query.afterId() != null) {
60 throw new IllegalArgumentException("offset and after_id should not be combined!");
61 }
62
63 EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
64 CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
65 CriteriaQuery<MCRObjectInfoEntity> criteriaQuery = criteriaBuilder.createQuery(MCRObjectInfoEntity.class);
66 Root<MCRObjectInfoEntity> oe = criteriaQuery.from(MCRObjectInfoEntity.class);
67 criteriaQuery.select(oe);
68
69 List<Predicate> filters = getFilter(query, criteriaBuilder, oe);
70
71 applyClassificationFilter(query, criteriaBuilder, criteriaQuery, oe, filters, em);
72
73 if (query.afterId() != null) {
74 applyLastId(query, criteriaBuilder, criteriaQuery, oe, filters);
75 } else {
76 applySort(query, criteriaBuilder, criteriaQuery, oe);
77 }
78
79 if (filters.size() > 0) {
80 criteriaQuery.where(criteriaBuilder.and(filters.toArray(new Predicate[0])));
81 }
82
83 TypedQuery<MCRObjectInfoEntity> typedQuery = em.createQuery(criteriaQuery);
84
85 if (offset != -1) {
86 typedQuery.setFirstResult(offset);
87 }
88
89 int limit = query.limit();
90 if (limit != -1) {
91 typedQuery.setMaxResults(limit);
92 }
93
94 return typedQuery;
95 }
96
97 private <T> void applyClassificationFilter(MCRObjectQuery query, CriteriaBuilder criteriaBuilder,
98 CriteriaQuery<T> criteriaQuery, Root<MCRObjectInfoEntity> oe, List<Predicate> filters, EntityManager em) {
99 if (query.getIncludeCategories().size() > 0) {
100 List<MCRCategoryImpl> idImplMap = getCategories(query, em);
101
102 if (idImplMap.size() != query.getIncludeCategories().size()) {
103 throw new IllegalArgumentException(
104 "some of " + String.join(", ", query.getIncludeCategories()) + " do not exist!");
105 }
106
107
108 Predicate[] categoryPredicates = idImplMap.stream().map(cat -> {
109 Root<MCRCategoryLinkImpl> cl = criteriaQuery.from(MCRCategoryLinkImpl.class);
110 Root<MCRCategoryImpl> c = criteriaQuery.from(MCRCategoryImpl.class);
111
112 Predicate linkToObject = criteriaBuilder.equal(
113 cl.get(MCRCategoryLinkImpl_.objectReference).get(MCRCategLinkReference_.OBJECT_ID),
114 oe.get(MCRObjectInfoEntity_.ID));
115
116 Predicate categoryToLink = criteriaBuilder.equal(cl.get(MCRCategoryLinkImpl_.CATEGORY),
117 c.get(MCRCategoryImpl_.INTERNAL_ID));
118
119 Predicate between = criteriaBuilder.between(
120 c.get(MCRCategoryImpl_.LEFT),
121 cat.getLeft(),
122 cat.getRight());
123
124 Predicate rootIdEqual = criteriaBuilder
125 .equal(c.get(MCRCategoryImpl_.id).get(MCRCategoryID_.ROOT_ID), cat.getRootID());
126
127 return criteriaBuilder.and(linkToObject, categoryToLink, rootIdEqual, between);
128 }).toArray(Predicate[]::new);
129
130 filters.add(criteriaBuilder.and(categoryPredicates));
131 }
132 }
133
134 private List<MCRCategoryImpl> getCategories(MCRObjectQuery query, EntityManager em) {
135 CriteriaBuilder categCb = em.getCriteriaBuilder();
136 CriteriaQuery<MCRCategoryImpl> categQuery = categCb.createQuery(MCRCategoryImpl.class);
137 Root<MCRCategoryImpl> classRoot = categQuery.from(MCRCategoryImpl.class);
138 categQuery.select(classRoot);
139 List<MCRCategoryID> categoryIDList = query.getIncludeCategories().stream()
140 .map(MCRCategoryID::fromString)
141 .collect(Collectors.toList());
142
143 categQuery.where(classRoot.get("id").in(categoryIDList));
144 TypedQuery<MCRCategoryImpl> typedQuery = em.createQuery(categQuery);
145
146 return typedQuery.getResultList();
147 }
148
149 protected void applySort(MCRObjectQuery query, CriteriaBuilder criteriaBuilder,
150 CriteriaQuery<MCRObjectInfoEntity> criteriaQuery, Root<MCRObjectInfoEntity> source) {
151 MCRObjectQuery.SortBy sf = query.sortBy() == null ? SORT_BY_DEFAULT : query.sortBy();
152
153 SingularAttribute<MCRObjectInfoEntity, ?> attribute = switch (sf) {
154 case id -> MCRObjectInfoEntity_.id;
155 case created -> MCRObjectInfoEntity_.createDate;
156 case modified -> MCRObjectInfoEntity_.modifyDate;
157 };
158
159 if (query.sortAsc() == null || query.sortAsc() == MCRObjectQuery.SortOrder.asc) {
160 criteriaQuery.orderBy(criteriaBuilder.asc(source.get(attribute)));
161 } else {
162 criteriaQuery.orderBy(criteriaBuilder.desc(source.get(attribute)));
163 }
164 }
165
166 protected void applyLastId(MCRObjectQuery query, CriteriaBuilder criteriaBuilder,
167 CriteriaQuery<MCRObjectInfoEntity> criteriaQuery, Root<MCRObjectInfoEntity> source, List<Predicate> filters) {
168 if (query.sortBy() != MCRObjectQuery.SortBy.id && query.sortBy() != null) {
169 throw new UnsupportedOperationException("last id can not be used with " + query.sortBy());
170 }
171 if (query.sortAsc() == null || query.sortAsc() == MCRObjectQuery.SortOrder.asc) {
172 filters.add(criteriaBuilder.greaterThan(source.get(MCRObjectInfoEntity_.id), query.afterId()));
173 criteriaQuery.orderBy(criteriaBuilder.asc(source.get(MCRObjectInfoEntity_.id)));
174 } else {
175 filters.add(criteriaBuilder.lessThan(source.get(MCRObjectInfoEntity_.id), query.afterId()));
176 criteriaQuery.orderBy(criteriaBuilder.desc(source.get(MCRObjectInfoEntity_.id)));
177 }
178 }
179
180 private List<Predicate> getFilter(MCRObjectQuery query, CriteriaBuilder criteriaBuilder,
181 Root<MCRObjectInfoEntity> source) {
182 List<Predicate> predicates = new ArrayList<>();
183
184 Optional.ofNullable(query.type())
185 .map(type -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.objectType), type))
186 .ifPresent(predicates::add);
187
188 Optional.ofNullable(query.project())
189 .map(project -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.objectProject), project))
190 .ifPresent(predicates::add);
191
192 Optional.ofNullable(query.createdBy())
193 .map(creator -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.createdBy), creator))
194 .ifPresent(predicates::add);
195
196 Optional.ofNullable(query.modifiedBy())
197 .map(modifier -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.modifiedBy), modifier))
198 .ifPresent(predicates::add);
199
200 Optional.ofNullable(query.deletedBy())
201 .map(deleter -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.deletedBy), deleter))
202 .ifPresent(predicates::add);
203
204 Optional.ofNullable(query.createdAfter())
205 .map(date -> criteriaBuilder.greaterThanOrEqualTo(source.get(MCRObjectInfoEntity_.createDate), date))
206 .ifPresent(predicates::add);
207
208 Optional.ofNullable(query.createdBefore())
209 .map(date -> criteriaBuilder.lessThanOrEqualTo(source.get(MCRObjectInfoEntity_.createDate), date))
210 .ifPresent(predicates::add);
211
212 Optional.ofNullable(query.modifiedAfter())
213 .map(date -> criteriaBuilder.greaterThanOrEqualTo(source.get(MCRObjectInfoEntity_.modifyDate), date))
214 .ifPresent(predicates::add);
215
216 Optional.ofNullable(query.modifiedBefore())
217 .map(date -> criteriaBuilder.lessThanOrEqualTo(source.get(MCRObjectInfoEntity_.modifyDate), date))
218 .ifPresent(predicates::add);
219
220 Optional.ofNullable(query.deletedAfter())
221 .map(date -> criteriaBuilder.greaterThanOrEqualTo(source.get(MCRObjectInfoEntity_.deleteDate), date))
222 .ifPresent(predicates::add);
223
224 Optional.ofNullable(query.deletedBefore())
225 .map(date -> criteriaBuilder.lessThanOrEqualTo(source.get(MCRObjectInfoEntity_.deleteDate), date))
226 .ifPresent(predicates::add);
227
228 Optional.of(query.numberGreater())
229 .filter(numberBiggerThan -> numberBiggerThan > -1)
230 .map(numberBiggerThan -> criteriaBuilder.greaterThan(source.get(MCRObjectInfoEntity_.objectNumber),
231 numberBiggerThan))
232 .ifPresent(predicates::add);
233
234 Optional.of(query.numberLess())
235 .filter(numberLess -> numberLess > -1)
236 .map(numberLess -> criteriaBuilder.lessThan(source.get(MCRObjectInfoEntity_.objectNumber), numberLess))
237 .ifPresent(predicates::add);
238
239 Optional.ofNullable(query.status())
240 .map(state -> criteriaBuilder.equal(source.get(MCRObjectInfoEntity_.state), state))
241 .ifPresent(predicates::add);
242
243
244
245
246 if (Optional.ofNullable(query.deletedBy()).isEmpty() &&
247 Optional.ofNullable(query.deletedBefore()).isEmpty() &&
248 Optional.ofNullable(query.deletedAfter()).isEmpty()) {
249 predicates.add(criteriaBuilder.isNull(source.get(MCRObjectInfoEntity_.deleteDate)));
250 predicates.add(criteriaBuilder.isNull(source.get(MCRObjectInfoEntity_.deletedBy)));
251 }
252
253 return predicates;
254 }
255
256 @Override
257 public List<MCRObjectID> getIds(MCRObjectQuery objectQuery) {
258 TypedQuery<MCRObjectInfoEntity> typedQuery = convertQuery(objectQuery);
259 return typedQuery.getResultList()
260 .stream()
261 .map(MCRObjectInfoEntity::getId)
262 .collect(Collectors.toList());
263 }
264
265 @Override
266 public List<MCRObjectIDDate> getIdDates(MCRObjectQuery objectQuery) {
267 TypedQuery<MCRObjectInfoEntity> typedQuery = convertQuery(objectQuery);
268 return typedQuery.getResultList()
269 .stream()
270 .map(entity -> new MCRObjectIDDateImpl(Date.from(entity.getModifyDate()), entity.getId().toString()))
271 .collect(Collectors.toList());
272 }
273
274 @Override
275 public List<MCRObjectInfo> getInfos(MCRObjectQuery objectQuery) {
276 TypedQuery<MCRObjectInfoEntity> typedQuery = convertQuery(objectQuery);
277 EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
278
279 return typedQuery.getResultList()
280 .stream()
281 .peek(em::detach)
282 .collect(Collectors.toList());
283 }
284
285 @Override
286 public int count(MCRObjectQuery objectQuery) {
287 EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
288 CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
289 CriteriaQuery<Number> criteriaQuery = criteriaBuilder.createQuery(Number.class);
290 Root<MCRObjectInfoEntity> source = criteriaQuery.from(MCRObjectInfoEntity.class);
291 criteriaQuery.select(criteriaBuilder.count(source));
292
293 List<Predicate> filters = getFilter(objectQuery, criteriaBuilder, source);
294 applyClassificationFilter(objectQuery, criteriaBuilder, criteriaQuery, source, filters, em);
295
296 if (filters.size() > 0) {
297 criteriaQuery.where(criteriaBuilder.and(filters.toArray(new Predicate[0])));
298 }
299
300 return em.createQuery(criteriaQuery).getSingleResult().intValue();
301 }
302 }