• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 29955974657

22 Jul 2026 08:38PM UTC coverage: 66.238% (+0.02%) from 66.218%
29955974657

push

github

ibacher
TRUNK-6711: Batch-load search result pages (#6363)

99 of 105 new or added lines in 4 files covered. (94.29%)

8 existing lines in 5 files now uncovered.

24430 of 36882 relevant lines covered (66.24%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.39
/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConceptDAO.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.api.db.hibernate;
11

12
import static java.util.stream.Collectors.toList;
13

14
import javax.persistence.Query;
15
import javax.persistence.TypedQuery;
16
import javax.persistence.criteria.CriteriaBuilder;
17
import javax.persistence.criteria.CriteriaQuery;
18
import javax.persistence.criteria.Join;
19
import javax.persistence.criteria.Predicate;
20
import javax.persistence.criteria.Root;
21
import java.util.ArrayList;
22
import java.util.Arrays;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.HashMap;
26
import java.util.HashSet;
27
import java.util.Iterator;
28
import java.util.LinkedHashSet;
29
import java.util.List;
30
import java.util.Locale;
31
import java.util.Map;
32
import java.util.Objects;
33
import java.util.Optional;
34
import java.util.Set;
35
import java.util.stream.Collectors;
36

37
import javax.persistence.criteria.Subquery;
38

39
import org.apache.commons.lang3.StringUtils;
40
import org.hibernate.FlushMode;
41
import org.hibernate.Session;
42
import org.hibernate.SessionFactory;
43
import org.hibernate.search.engine.search.predicate.SearchPredicate;
44
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep;
45
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
46
import org.hibernate.search.engine.search.query.SearchQuery;
47
import org.hibernate.search.mapper.orm.scope.SearchScope;
48
import org.hibernate.search.mapper.orm.session.SearchSession;
49
import org.openmrs.Concept;
50
import org.openmrs.ConceptAnswer;
51
import org.openmrs.ConceptAttribute;
52
import org.openmrs.ConceptAttributeType;
53
import org.openmrs.ConceptClass;
54
import org.openmrs.ConceptComplex;
55
import org.openmrs.ConceptDatatype;
56
import org.openmrs.ConceptDescription;
57
import org.openmrs.ConceptMap;
58
import org.openmrs.ConceptMapType;
59
import org.openmrs.ConceptName;
60
import org.openmrs.ConceptNameTag;
61
import org.openmrs.ConceptNumeric;
62
import org.openmrs.ConceptProposal;
63
import org.openmrs.ConceptReferenceRange;
64
import org.openmrs.ConceptReferenceTerm;
65
import org.openmrs.ConceptReferenceTermMap;
66
import org.openmrs.ConceptSearchResult;
67
import org.openmrs.ConceptSet;
68
import org.openmrs.ConceptSource;
69
import org.openmrs.ConceptStopWord;
70
import org.openmrs.Drug;
71
import org.openmrs.DrugIngredient;
72
import org.openmrs.DrugReferenceMap;
73
import org.openmrs.api.APIException;
74
import org.openmrs.api.ConceptService;
75
import org.openmrs.api.context.Context;
76
import org.openmrs.api.db.ConceptDAO;
77
import org.openmrs.api.db.DAOException;
78
import org.openmrs.api.db.hibernate.search.SearchQueryUnique;
79
import org.openmrs.api.db.hibernate.search.session.SearchSessionFactory;
80
import org.openmrs.parameter.ConceptSearchCriteria;
81
import org.openmrs.util.ConceptMapTypeComparator;
82
import org.openmrs.util.OpenmrsConstants;
83
import org.slf4j.Logger;
84
import org.slf4j.LoggerFactory;
85
import org.springframework.util.CollectionUtils;
86

87
/**
88
 * The Hibernate class for Concepts, Drugs, and related classes. <br>
89
 * <br>
90
 * Use the {@link ConceptService} to access these methods
91
 * 
92
 * @see ConceptService
93
 */
94
public class HibernateConceptDAO implements ConceptDAO {
1✔
95
        
96
        private static final Logger log = LoggerFactory.getLogger(HibernateConceptDAO.class);
1✔
97

98
        private static final String CONCEPT_REFERENCE_TERM = "conceptReferenceTerm";
99

100
        private static final String CONCEPT_SOURCE = "conceptSource";
101

102
        private static final String HL7_CODE = "hl7Code";
103

104
        private SessionFactory sessionFactory;
105

106
        private SearchSessionFactory searchSessionFactory;
107

108
        /**
109
         * Sets the session factory
110
         *
111
         * @param sessionFactory
112
         */
113
        public void setSessionFactory(SessionFactory sessionFactory) {
114
                this.sessionFactory = sessionFactory;
1✔
115
        }
1✔
116

117
        /**
118
         * Sets the search session factory
119
         * 
120
         * @param searchSessionFactory
121
         */
122
        public void setSearchSessionFactory(SearchSessionFactory searchSessionFactory) {
123
                this.searchSessionFactory = searchSessionFactory;
1✔
124
        }
1✔
125

126
        /**
127
         * @see org.openmrs.api.db.ConceptDAO#getConceptComplex(java.lang.Integer)
128
         */
129
        @Override
130
        public ConceptComplex getConceptComplex(Integer conceptId) {
131
                ConceptComplex cc;
132
                Session session = sessionFactory.getCurrentSession();
1✔
133
                Object obj = session.get(ConceptComplex.class, conceptId);
1✔
134
                // If Concept has already been read & cached, we may get back a Concept instead of
135
                // ConceptComplex.  If this happens, we need to clear the object from the cache
136
                // and re-fetch it as a ConceptComplex
137
                if (obj != null && !obj.getClass().equals(ConceptComplex.class)) {
1✔
138
                        // remove from cache
139
                        session.detach(obj);
×
140

141
                        // session.get() did not work here, we need to perform a query to get a ConceptComplex
142
                        CriteriaBuilder cb = session.getCriteriaBuilder();
×
143
                        CriteriaQuery<ConceptComplex> cq = cb.createQuery(ConceptComplex.class);
×
144
                        Root<ConceptComplex> root = cq.from(ConceptComplex.class);
×
145

146
                        cq.where(cb.equal(root.get("conceptId"), conceptId));
×
147

148
                        obj = session.createQuery(cq).uniqueResult();
×
149
                }
150
                cc = (ConceptComplex) obj;
1✔
151

152
                return cc;
1✔
153
        }
154
        
155
        /**
156
         * @see org.openmrs.api.db.ConceptDAO#saveConcept(org.openmrs.Concept)
157
         */
158
        @Override
159
        public Concept saveConcept(Concept concept) throws DAOException {
160
                if ((concept.getConceptId() != null) && (concept.getConceptId() > 0)) {
1✔
161
                        // this method checks the concept_numeric, concept_derived, etc tables
162
                        // to see if a row exists there or not.  This is needed because hibernate
163
                        // doesn't like to insert into concept_numeric but update concept in the
164
                        // same go.  It assumes that its either in both tables or no tables
165
                        insertRowIntoSubclassIfNecessary(concept);
1✔
166
                }
167
                
168
                sessionFactory.getCurrentSession().saveOrUpdate(concept);
1✔
169
                return concept;
1✔
170
        }
171
        
172
        /**
173
         * Convenience method that will check this concept for subtype values (ConceptNumeric,
174
         * ConceptDerived, etc) and insert a line into that subtable if needed. This prevents a
175
         * hibernate ConstraintViolationException
176
         * 
177
         * @param concept the concept that will be inserted
178
         */
179
        private void insertRowIntoSubclassIfNecessary(Concept concept) {
180
                
181
                // check the concept_numeric table
182
                if (concept instanceof ConceptNumeric) {
1✔
183
                        
184
                        String select = "SELECT 1 from concept_numeric WHERE concept_id = :conceptId";
1✔
185
                        Query query = sessionFactory.getCurrentSession().createSQLQuery(select);
1✔
186
                        query.setParameter("conceptId", concept.getConceptId());
1✔
187
                        
188
                        // Converting to concept numeric:  A single concept row exists, but concept numeric has not been populated yet.
189
                        if (JpaUtils.getSingleResultOrNull(query) == null) {
1✔
190
                                // we have to evict the current concept out of the session because
191
                                // the user probably had to change the class of this object to get it
192
                                // to now be a numeric
193
                                // (must be done before the "insert into...")
194
                                sessionFactory.getCurrentSession().clear();
1✔
195
                                
196
                                //Just in case this was changed from concept_complex to numeric
197
                                //We need to add a delete line for each concept sub class that is not concept_numeric
198
                                deleteSubclassConcept("concept_complex", concept.getConceptId());
1✔
199
                                
200
                                String insert = "INSERT INTO concept_numeric (concept_id, allow_decimal) VALUES (:conceptId, false)";
1✔
201
                                query = sessionFactory.getCurrentSession().createSQLQuery(insert);
1✔
202
                                query.setParameter("conceptId", concept.getConceptId());
1✔
203
                                query.executeUpdate();
1✔
204
                                
205
                        } else {
1✔
206
                                // Converting from concept numeric:  The concept and concept numeric rows both exist, so we need to delete concept_numeric.
207
                                
208
                                // concept is changed from numeric to something else
209
                                // hence row should be deleted from the concept_numeric
210
                                if (!concept.isNumeric()) {
1✔
211
                                        deleteSubclassConcept("concept_numeric", concept.getConceptId());
1✔
212
                                }
213
                        }
214
                }
1✔
215
                // check the concept complex table
216
                else if (concept instanceof ConceptComplex) {
1✔
217
                        
218
                        String select = "SELECT 1 FROM concept_complex WHERE concept_id = :conceptId";
1✔
219
                        Query query = sessionFactory.getCurrentSession().createSQLQuery(select);
1✔
220
                        query.setParameter("conceptId", concept.getConceptId());
1✔
221
                        
222
                        // Converting to concept complex:  A single concept row exists, but concept complex has not been populated yet.
223
                        if (JpaUtils.getSingleResultOrNull(query) == null) {
1✔
224
                                // we have to evict the current concept out of the session because
225
                                // the user probably had to change the class of this object to get it
226
                                // to now be a ConceptComplex
227
                                // (must be done before the "insert into...")
228
                                sessionFactory.getCurrentSession().clear();
1✔
229
                                
230
                                //Just in case this was changed from concept_numeric to complex
231
                                //We need to add a delete line for each concept sub class that is not concept_complex
232
                                deleteSubclassConcept("concept_numeric", concept.getConceptId());
1✔
233
                                
234
                                // Add an empty row into the concept_complex table
235
                                String insert = "INSERT INTO concept_complex (concept_id) VALUES (:conceptId)";
1✔
236
                                query = sessionFactory.getCurrentSession().createSQLQuery(insert);
1✔
237
                                query.setParameter("conceptId", concept.getConceptId());
1✔
238
                                query.executeUpdate();
1✔
239
                                
240
                        } else {
1✔
241
                                // Converting from concept complex:  The concept and concept complex rows both exist, so we need to delete the concept_complex row.
242
                                // no stub insert is needed because either a concept row doesn't exist OR a concept_complex row does exist
243
                                
244
                                // concept is changed from complex to something else
245
                                // hence row should be deleted from the concept_complex
246
                                if (!concept.isComplex()) {
×
247
                                        deleteSubclassConcept("concept_complex", concept.getConceptId());
×
248
                                }
249
                        }
250
                }
251
        }
1✔
252
        
253
        /**
254
         * Deletes a concept from a sub class table
255
         * 
256
         * @param tableName the sub class table name
257
         * @param conceptId the concept id
258
         */
259
        private void deleteSubclassConcept(String tableName, Integer conceptId) {
260
                String delete = "DELETE FROM " + tableName + " WHERE concept_id = :conceptId";
1✔
261
                Query query = sessionFactory.getCurrentSession().createSQLQuery(delete);
1✔
262
                query.setParameter("conceptId", conceptId);
1✔
263
                query.executeUpdate();
1✔
264
        }
1✔
265
        
266
        /**
267
         * @see org.openmrs.api.db.ConceptDAO#purgeConcept(org.openmrs.Concept)
268
         */
269
        @Override
270
        public void purgeConcept(Concept concept) throws DAOException {
271
                sessionFactory.getCurrentSession().delete(concept);
1✔
272
        }
1✔
273
        
274
        /**
275
         * @see org.openmrs.api.db.ConceptDAO#getConcept(java.lang.Integer)
276
         */
277
        @Override
278
        public Concept getConcept(Integer conceptId) throws DAOException {
279
                return sessionFactory.getCurrentSession().get(Concept.class, conceptId);
1✔
280
        }
281
        
282
        /**
283
         * @see org.openmrs.api.db.ConceptDAO#getConceptName(java.lang.Integer)
284
         */
285
        @Override
286
        public ConceptName getConceptName(Integer conceptNameId) throws DAOException {
287
                return sessionFactory.getCurrentSession().get(ConceptName.class, conceptNameId);
1✔
288
        }
289
        
290
        /**
291
         * @see org.openmrs.api.db.ConceptDAO#getConceptAnswer(java.lang.Integer)
292
         */
293
        @Override
294
        public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws DAOException {
295
                return sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId);
1✔
296
        }
297
        
298
        /**
299
         * @see org.openmrs.api.db.ConceptDAO#getAllConcepts(java.lang.String, boolean, boolean)
300
         */
301
        @Override
302
        @SuppressWarnings("unchecked")
303
        public List<Concept> getAllConcepts(String sortBy, boolean asc, boolean includeRetired) throws DAOException {
304
                
305
                boolean isNameField = false;
1✔
306
                
307
                try {
308
                        Concept.class.getDeclaredField(sortBy);
1✔
309
                }
310
                catch (NoSuchFieldException e) {
1✔
311
                        try {
312
                                ConceptName.class.getDeclaredField(sortBy);
1✔
313
                                isNameField = true;
1✔
314
                        }
315
                        catch (NoSuchFieldException e2) {
×
316
                                sortBy = "conceptId";
×
317
                        }
1✔
318
                }
1✔
319
                
320
                String hql = "";
1✔
321
                if (isNameField) {
1✔
322
                        hql += "select concept";
1✔
323
                }
324
                
325
                hql += " from Concept as concept";
1✔
326
                boolean hasWhereClause = false;
1✔
327
                if (isNameField) {
1✔
328
                        hasWhereClause = true;
1✔
329
                        //This assumes every concept has a unique(avoid duplicates) fully specified name
330
                        //which should be true for a clean concept dictionary
331
                        hql += " left join concept.names as names where names.conceptNameType = 'FULLY_SPECIFIED'";
1✔
332
                }
333
                
334
                if (!includeRetired) {
1✔
335
                        if (hasWhereClause) {
1✔
336
                                hql += " and";
1✔
337
                        } else {
338
                                hql += " where";
1✔
339
                        }
340
                        hql += " concept.retired = false";
1✔
341
                        
342
                }
343
                
344
                if (isNameField) {
1✔
345
                        hql += " order by names." + sortBy;
1✔
346
                } else {
347
                        hql += " order by concept." + sortBy;
1✔
348
                }
349
                
350
                hql += asc ? " asc" : " desc";
1✔
351
                Query query = sessionFactory.getCurrentSession().createQuery(hql);
1✔
352
                return (List<Concept>) query.getResultList();
1✔
353
        }
354
        
355
        /**
356
         * @see org.openmrs.api.db.ConceptDAO#saveDrug(org.openmrs.Drug)
357
         */
358
        @Override
359
        public Drug saveDrug(Drug drug) throws DAOException {
360
                sessionFactory.getCurrentSession().saveOrUpdate(drug);
1✔
361
                return drug;
1✔
362
        }
363
        
364
        /**
365
         * @see org.openmrs.api.db.ConceptDAO#getDrug(java.lang.Integer)
366
         */
367
        @Override
368
        public Drug getDrug(Integer drugId) throws DAOException {
369
                return sessionFactory.getCurrentSession().get(Drug.class, drugId);
1✔
370
        }
371
        
372
        /**
373
         * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String, org.openmrs.Concept, boolean)
374
         */
375
        @Override
376
        public List<Drug> getDrugs(String drugName, Concept concept, boolean includeRetired) throws DAOException {
377
                Session session = sessionFactory.getCurrentSession();
1✔
378
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
379
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
380
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
381

382
                List<Predicate> predicates = new ArrayList<>();
1✔
383

384
                if (!includeRetired) {
1✔
385
                        predicates.add(cb.isFalse(drugRoot.get("retired")));
1✔
386
                }
387

388
                if (concept != null) {
1✔
389
                        predicates.add(cb.equal(drugRoot.get("concept"), concept));
1✔
390
                }
391

392
                if (drugName != null) {
1✔
393
                        if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) {
1✔
394
                                predicates.add(cb.equal(cb.lower(drugRoot.get("name")), MatchMode.EXACT.toLowerCasePattern(drugName)));
1✔
395
                        } else {
396
                                predicates.add(cb.equal(drugRoot.get("name"), MatchMode.EXACT.toCaseSensitivePattern(drugName)));
×
397
                        }
398
                }
399

400
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
401

402
                return session.createQuery(cq).getResultList();
1✔
403
        }
404

405
        /**
406
         * @see org.openmrs.api.db.ConceptDAO#getDrugsByIngredient(org.openmrs.Concept)
407
         */
408
        @Override
409
        public List<Drug> getDrugsByIngredient(Concept ingredient) {
410
                Session session = sessionFactory.getCurrentSession();
1✔
411
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
412
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
413
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
414
                
415
                Join<Drug, DrugIngredient> ingredientJoin = drugRoot.join("ingredients");
1✔
416

417
                Predicate rhs = cb.equal(drugRoot.get("concept"), ingredient);
1✔
418
                Predicate lhs = cb.equal(ingredientJoin.get("ingredient"), ingredient);
1✔
419

420
                cq.where(cb.or(lhs, rhs));
1✔
421

422
                return session.createQuery(cq).getResultList();
1✔
423
        }
424
        
425
        /**
426
         * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String)
427
         */
428
        @Override
429
        public List<Drug> getDrugs(final String phrase) throws DAOException {
430
                SearchQuery<Drug> drugQuery = newDrugQuery(phrase, true, false, Context.getLocale(), false, null, false);
1✔
431
                
432
                if (drugQuery == null) {
1✔
433
                        return Collections.emptyList();
×
434
                }
435
                
436
                return drugQuery.fetchAllHits();
1✔
437
        }
438
        
439
        /**
440
         * @see org.openmrs.api.db.ConceptDAO#getConceptClass(java.lang.Integer)
441
         */
442
        @Override
443
        public ConceptClass getConceptClass(Integer i) throws DAOException {
444
                return sessionFactory.getCurrentSession().get(ConceptClass.class, i);
1✔
445
        }
446
        
447
        /**
448
         * @see org.openmrs.api.db.ConceptDAO#getConceptClasses(java.lang.String)
449
         */
450
        @Override
451
        public List<ConceptClass> getConceptClasses(String name) throws DAOException {
452
                Session session = sessionFactory.getCurrentSession();
1✔
453
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
454
                CriteriaQuery<ConceptClass> cq = cb.createQuery(ConceptClass.class);
1✔
455
                Root<ConceptClass> root = cq.from(ConceptClass.class);
1✔
456

457
                if (name != null) {
1✔
458
                        cq.where(cb.equal(root.get("name"), name));
1✔
459
                }
460

461
                return session.createQuery(cq).getResultList();
1✔
462
        }
463
        
464
        /**
465
         * @see org.openmrs.api.db.ConceptDAO#getAllConceptClasses(boolean)
466
         */
467
        @Override
468
        public List<ConceptClass> getAllConceptClasses(boolean includeRetired) throws DAOException {
469
                Session session = sessionFactory.getCurrentSession();
1✔
470
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
471
                CriteriaQuery<ConceptClass> cq = cb.createQuery(ConceptClass.class);
1✔
472
                Root<ConceptClass> root = cq.from(ConceptClass.class);
1✔
473

474
                // Minor bug - was assigning includeRetired instead of evaluating
475
                if (!includeRetired) {
1✔
476
                        cq.where(cb.isFalse(root.get("retired")));
1✔
477
                }
478

479
                return session.createQuery(cq).getResultList();
1✔
480
        }
481
        
482
        /**
483
         * @see org.openmrs.api.db.ConceptDAO#saveConceptClass(org.openmrs.ConceptClass)
484
         */
485
        @Override
486
        public ConceptClass saveConceptClass(ConceptClass cc) throws DAOException {
487
                sessionFactory.getCurrentSession().saveOrUpdate(cc);
1✔
488
                return cc;
1✔
489
        }
490
        
491
        /**
492
         * @see org.openmrs.api.db.ConceptDAO#purgeConceptClass(org.openmrs.ConceptClass)
493
         */
494
        @Override
495
        public void purgeConceptClass(ConceptClass cc) throws DAOException {
496
                sessionFactory.getCurrentSession().delete(cc);
1✔
497
        }
1✔
498
        
499
        /**
500
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptNameTag(ConceptNameTag)
501
         */
502
        @Override
503
        public void deleteConceptNameTag(ConceptNameTag cnt) throws DAOException {
504
                sessionFactory.getCurrentSession().delete(cnt);
1✔
505
        }
1✔
506
        
507
        /**
508
         * @see org.openmrs.api.db.ConceptDAO#getConceptDatatype(java.lang.Integer)
509
         */
510
        @Override
511
        public ConceptDatatype getConceptDatatype(Integer i) {
512
                return sessionFactory.getCurrentSession().get(ConceptDatatype.class, i);
1✔
513
        }
514
        
515
        @Override
516
        public List<ConceptDatatype> getAllConceptDatatypes(boolean includeRetired) throws DAOException {
517
                Session session = sessionFactory.getCurrentSession();
1✔
518
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
519
                CriteriaQuery<ConceptDatatype> cq = cb.createQuery(ConceptDatatype.class);
1✔
520
                Root<ConceptDatatype> root = cq.from(ConceptDatatype.class);
1✔
521

522
                if (!includeRetired) {
1✔
523
                        cq.where(cb.isFalse(root.get("retired")));
1✔
524
                }
525

526
                return session.createQuery(cq).getResultList();
1✔
527
        }
528

529
        /**
530
         * @param name the name of the ConceptDatatype
531
         * @return a List of ConceptDatatype whose names start with the passed name
532
         */
533
        public List<ConceptDatatype> getConceptDatatypes(String name) throws DAOException {
534
                Session session = sessionFactory.getCurrentSession();
1✔
535
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
536
                CriteriaQuery<ConceptDatatype> cq = cb.createQuery(ConceptDatatype.class);
1✔
537
                Root<ConceptDatatype> root = cq.from(ConceptDatatype.class);
1✔
538

539
                if (name != null) {
1✔
540
                        cq.where(cb.like(root.get("name"), MatchMode.START.toCaseSensitivePattern(name)));
1✔
541
                }
542

543
                return session.createQuery(cq).getResultList();
1✔
544
        }
545

546
        /**
547
         * @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByName(String)
548
         */
549
        @Override
550
        public ConceptDatatype getConceptDatatypeByName(String name) throws DAOException {
551
                Session session = sessionFactory.getCurrentSession();
1✔
552
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
553
                CriteriaQuery<ConceptDatatype> cq = cb.createQuery(ConceptDatatype.class);
1✔
554
                Root<ConceptDatatype> root = cq.from(ConceptDatatype.class);
1✔
555

556
                if (name != null) {
1✔
557
                        cq.where(cb.equal(root.get("name"), name));
1✔
558
                }
559
                return session.createQuery(cq).uniqueResult();
1✔
560
        }
561
        
562
        /**
563
         * @see org.openmrs.api.db.ConceptDAO#saveConceptDatatype(org.openmrs.ConceptDatatype)
564
         */
565
        @Override
566
        public ConceptDatatype saveConceptDatatype(ConceptDatatype cd) throws DAOException {
567
                sessionFactory.getCurrentSession().saveOrUpdate(cd);
×
568
                return cd;
×
569
        }
570
        
571
        /**
572
         * @see org.openmrs.api.db.ConceptDAO#purgeConceptDatatype(org.openmrs.ConceptDatatype)
573
         */
574
        @Override
575
        public void purgeConceptDatatype(ConceptDatatype cd) throws DAOException {
576
                sessionFactory.getCurrentSession().delete(cd);
×
577
        }
×
578
        
579
        /**
580
         * @see org.openmrs.api.db.ConceptDAO#getConceptNumeric(java.lang.Integer)
581
         */
582
        @Override
583
        public ConceptNumeric getConceptNumeric(Integer i) {
584
                ConceptNumeric cn;
585
                Object obj = sessionFactory.getCurrentSession().get(ConceptNumeric.class, i);
1✔
586
                // If Concept has already been read & cached, we may get back a Concept instead of
587
                // ConceptNumeric.  If this happens, we need to clear the object from the cache
588
                // and re-fetch it as a ConceptNumeric
589
                if (obj != null && !obj.getClass().equals(ConceptNumeric.class)) {
1✔
590
                        // remove from cache
591
                        sessionFactory.getCurrentSession().evict(obj);
×
592
                        // session.get() did not work here, we need to perform a query to get a ConceptNumeric
593
                        Query query = sessionFactory.getCurrentSession().createQuery("from ConceptNumeric where conceptId = :conceptId")
×
594
                                .setParameter("conceptId", i);
×
595
                        obj = JpaUtils.getSingleResultOrNull(query);
×
596
                }
597
                cn = (ConceptNumeric) obj;
1✔
598
                
599
                return cn;
1✔
600
        }
601
        
602
        /**
603
         * @see org.openmrs.api.db.ConceptDAO#getConcepts(java.lang.String, java.util.Locale, boolean,
604
         *      java.util.List, java.util.List)
605
         */
606
        @Override
607
        public List<Concept> getConcepts(final String name, final Locale loc, final boolean searchOnPhrase,
608
                final List<ConceptClass> classes, final List<ConceptDatatype> datatypes) throws DAOException {
609
                
610
                final Locale locale;
611
                if (loc == null) {
1✔
612
                        locale = Context.getLocale();
1✔
613
                } else {
614
                        locale = loc;
1✔
615
                }
616

617
                return SearchQueryUnique
1✔
618
                        .search(searchSessionFactory,
1✔
619
                            SearchQueryUnique.newQuery(
1✔
620
                                ConceptName.class, f -> newConceptNamePredicate(f, name, !searchOnPhrase,
1✔
621
                                    Collections.singletonList(locale), false, false, classes, null, datatypes, null, null),
1✔
622
                                "concept.conceptId", this::multiLoadConcepts));
623
        }
624
        
625
        private LinkedHashSet<Concept> transformNamesToConcepts(List<ConceptName> names) {
626
                LinkedHashSet<Concept> concepts = new LinkedHashSet<>();
1✔
627
                
628
                for (ConceptName name : names) {
1✔
629
                        concepts.add(name.getConcept());
1✔
630
                }
1✔
631
                
632
                return concepts;
1✔
633
        }
634
        
635
        private SearchPredicate newConceptNameQuery(SearchPredicateFactory f, final String name, final boolean searchKeywords, 
636
                                                                                                final Collection<Locale> locales, final boolean searchExactLocale) {
637
                return f.bool().with(b -> {
1✔
638
                        if (searchExactLocale) {
1✔
639
                                b.must(f.terms().field("locale").matchingAny(locales));
1✔
640
                        } else {
641
                                b.must(f.bool().with(bb -> {
1✔
642
                                        for (Locale locale : locales) {
1✔
643
                                                bb.should(f.wildcard().field("locale").matching(locale.getLanguage() + "*"));
1✔
644
                                                if (!StringUtils.isBlank(locale.getCountry())) {
1✔
645
                                                        bb.should(f.match().field("locale").matching(locale).boost(2f));
1✔
646
                                                }
647
                                        }
1✔
648
                                }));
1✔
649
                        }
650
                        b.filter(f.match().field("voided").matching(false));
1✔
651
                        
652
                        b.must(f.bool().with(bb -> {
1✔
653
                                List<String> tokenizedName = tokenizeName(name, locales);
1✔
654
                                BooleanPredicateClausesStep<?> nameQuery = newNameQuery(f, tokenizedName, name, searchKeywords);
1✔
655

656
                                bb.should(f.match().field("concept.conceptMappings.conceptReferenceTerm.code").matching(name).boost(10f));
1✔
657
                                bb.should(f.and()
1✔
658
                                        .add(nameQuery)
1✔
659
                                        .add(f.match().field("localePreferred").matching(true)).boost(2f));
1✔
660
                                bb.should(nameQuery);
1✔
661
                        }));
1✔
662
                }).toPredicate();
1✔
663
        }
664
        
665
        private BooleanPredicateClausesStep<?> newNameQuery(SearchPredicateFactory f, final List<String> tokenizedName, final String name,
666
                                                                                                                final boolean searchKeywords) {
667
                return f.bool().with(b -> {
1✔
668
                        b.minimumShouldMatchNumber(1);
1✔
669
                        b.should(f.phrase().field("name").matching(name).boost(8f));
1✔
670
                        if (searchKeywords) {
1✔
671
                                if (!tokenizedName.isEmpty()) {
1✔
672
                                        b.should(f.bool().with(bb -> {
1✔
673
                                                for (String token : tokenizedName) {
1✔
674
                                                        bb.must(f.bool().with(bbb -> {
1✔
675
                                                                bbb.minimumShouldMatchNumber(1);
1✔
676
                                                                bbb.should(f.match().field("name").matching(token).boost(3f));
1✔
677
                                                                bbb.should(f.wildcard().field("name").matching(token + "*").boost(2f));
1✔
678
                                                                bbb.should(f.match().field("name").matching(token).fuzzy(1, 3));
1✔
679
                                                        }));
1✔
680
                                                }
1✔
681
                                        }));
1✔
682
                                }
683
                        }
684
                });
1✔
685
        }
686
        
687
        private List<String> tokenizeName(final String escapedName, final Collection<Locale> locales) {
688
                List<String> words = new ArrayList<>(Arrays.asList(escapedName.trim().split(" ")));
1✔
689
                
690
                Set<String> stopWords = new HashSet<>();
1✔
691
                for (Locale locale : locales) {
1✔
692
                        stopWords.addAll(Context.getConceptService().getConceptStopWords(locale));
1✔
693
                }
1✔
694
                
695
                List<String> tokenizedName = new ArrayList<>();
1✔
696
                
697
                for (String word : words) {
1✔
698
                        word = word.trim();
1✔
699
                        
700
                        if (!word.isEmpty() && !stopWords.contains(word.toUpperCase())) {
1✔
701
                                tokenizedName.add(word);
1✔
702
                        }
703
                }
1✔
704
                
705
                return tokenizedName;
1✔
706
        }
707
        
708
        /**
709
         * gets questions for the given answer concept
710
         * 
711
         * @see org.openmrs.api.db.ConceptDAO#getConceptsByAnswer(org.openmrs.Concept)
712
         */
713
        @Override
714
        @SuppressWarnings("unchecked")
715
        public List<Concept> getConceptsByAnswer(Concept concept) {
716
                String q = "select c from Concept c join c.answers ca where ca.answerConcept = :answer";
1✔
717
                Query query = sessionFactory.getCurrentSession().createQuery(q);
1✔
718
                query.setParameter("answer", concept);
1✔
719
                
720
                return query.getResultList();
1✔
721
        }
722

723
        /**
724
         * @see org.openmrs.api.db.ConceptDAO#getPrevConcept(org.openmrs.Concept)
725
         */
726
        @Override
727
        public Concept getPrevConcept(Concept c) {
728
                Session session = sessionFactory.getCurrentSession();
1✔
729
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
730
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
731
                Root<Concept> root = cq.from(Concept.class);
1✔
732

733
                Integer i = c.getConceptId();
1✔
734

735
                cq.where(cb.lessThan(root.get("conceptId"), i));
1✔
736
                cq.orderBy(cb.desc(root.get("conceptId")));
1✔
737

738
                List<Concept> concepts = session.createQuery(cq).setMaxResults(1).getResultList();
1✔
739

740
                if (concepts.isEmpty()) {
1✔
741
                        return null;
1✔
742
                }
743

744
                return concepts.get(0);
1✔
745
        }
746

747
        /**
748
         * @see org.openmrs.api.db.ConceptDAO#getNextConcept(org.openmrs.Concept)
749
         */
750
        @Override
751
        public Concept getNextConcept(Concept c) {
752
                Session session = sessionFactory.getCurrentSession();
1✔
753
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
754
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
755
                Root<Concept> root = cq.from(Concept.class);
1✔
756

757
                Integer i = c.getConceptId();
1✔
758

759
                cq.where(cb.greaterThan(root.get("conceptId"), i));
1✔
760
                cq.orderBy(cb.asc(root.get("conceptId")));
1✔
761

762
                List<Concept> concepts = session.createQuery(cq).setMaxResults(1).getResultList();
1✔
763

764
                if (concepts.isEmpty()) {
1✔
765
                        return null;
1✔
766
                }
767

768
                return concepts.get(0);
1✔
769
        }
770
        
771
        /**
772
         * @see org.openmrs.api.db.ConceptDAO#getConceptsWithDrugsInFormulary()
773
         */
774
        @Override
775
        @SuppressWarnings("unchecked")
776
        public List<Concept> getConceptsWithDrugsInFormulary() {
777
                Query query = sessionFactory.getCurrentSession().createQuery(
1✔
778
                    "select distinct concept from Drug d where d.retired = false");
779
                return query.getResultList();
1✔
780
        }
781
        
782
        /**
783
         * @see org.openmrs.api.db.ConceptDAO#purgeDrug(org.openmrs.Drug)
784
         */
785
        @Override
786
        public void purgeDrug(Drug drug) throws DAOException {
787
                sessionFactory.getCurrentSession().delete(drug);
1✔
788
        }
1✔
789
        
790
        /**
791
         * @see org.openmrs.api.db.ConceptDAO#saveConceptProposal(org.openmrs.ConceptProposal)
792
         */
793
        @Override
794
        public ConceptProposal saveConceptProposal(ConceptProposal cp) throws DAOException {
795
                sessionFactory.getCurrentSession().saveOrUpdate(cp);
1✔
796
                return cp;
1✔
797
        }
798
        
799
        /**
800
         * @see org.openmrs.api.db.ConceptDAO#purgeConceptProposal(org.openmrs.ConceptProposal)
801
         */
802
        @Override
803
        public void purgeConceptProposal(ConceptProposal cp) throws DAOException {
804
                sessionFactory.getCurrentSession().delete(cp);
1✔
805
        }
1✔
806

807
        /**
808
         * @see org.openmrs.api.db.ConceptDAO#getAllConceptProposals(boolean)
809
         */
810
        @Override
811
        public List<ConceptProposal> getAllConceptProposals(boolean includeCompleted) throws DAOException {
812
                Session session = sessionFactory.getCurrentSession();
1✔
813
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
814
                CriteriaQuery<ConceptProposal> cq = cb.createQuery(ConceptProposal.class);
1✔
815
                Root<ConceptProposal> root = cq.from(ConceptProposal.class);
1✔
816

817
                if (!includeCompleted) {
1✔
818
                        cq.where(cb.equal(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
1✔
819
                }
820
                cq.orderBy(cb.asc(root.get("originalText")));
1✔
821
                return session.createQuery(cq).getResultList();
1✔
822
        }
823
        
824
        /**
825
         * @see org.openmrs.api.db.ConceptDAO#getConceptProposal(java.lang.Integer)
826
         */
827
        @Override
828
        public ConceptProposal getConceptProposal(Integer conceptProposalId) throws DAOException {
829
                return sessionFactory.getCurrentSession().get(ConceptProposal.class, conceptProposalId);
1✔
830
        }
831
        
832
        /**
833
         * @see org.openmrs.api.db.ConceptDAO#getConceptProposals(java.lang.String)
834
         */
835
        @Override
836
        public List<ConceptProposal> getConceptProposals(String text) throws DAOException {
837
                Session session = sessionFactory.getCurrentSession();
1✔
838
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
839
                CriteriaQuery<ConceptProposal> cq = cb.createQuery(ConceptProposal.class);
1✔
840
                Root<ConceptProposal> root = cq.from(ConceptProposal.class);
1✔
841

842
                Predicate stateCondition = cb.equal(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED);
1✔
843
                Predicate textCondition = cb.equal(root.get("originalText"), text);
1✔
844

845
                cq.where(cb.and(stateCondition, textCondition));
1✔
846

847
                return session.createQuery(cq).getResultList();
1✔
848
        }
849

850
        /**
851
         * @see org.openmrs.api.db.ConceptDAO#getProposedConcepts(java.lang.String)
852
         */
853
        @Override
854
        public List<Concept> getProposedConcepts(String text) throws DAOException {
855
                Session session = sessionFactory.getCurrentSession();
1✔
856
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
857
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
858
                Root<ConceptProposal> root = cq.from(ConceptProposal.class);
1✔
859

860
                Predicate stateNotEqual = cb.notEqual(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED);
1✔
861
                Predicate originalTextEqual = cb.equal(root.get("originalText"), text);
1✔
862
                Predicate mappedConceptNotNull = cb.isNotNull(root.get("mappedConcept"));
1✔
863

864
                cq.select(root.get("mappedConcept")).distinct(true);
1✔
865
                cq.where(stateNotEqual, originalTextEqual, mappedConceptNotNull);
1✔
866

867
                return session.createQuery(cq).getResultList();
1✔
868
        }
869

870
        /**
871
         * @see org.openmrs.api.db.ConceptDAO#getConceptSetsByConcept(org.openmrs.Concept)
872
         */
873
        @Override
874
        public List<ConceptSet> getConceptSetsByConcept(Concept concept) {
875
                Session session = sessionFactory.getCurrentSession();
1✔
876
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
877
                CriteriaQuery<ConceptSet> cq = cb.createQuery(ConceptSet.class);
1✔
878
                Root<ConceptSet> root = cq.from(ConceptSet.class);
1✔
879

880
                cq.where(cb.equal(root.get("conceptSet"), concept));
1✔
881
                cq.orderBy(cb.asc(root.get("sortWeight")));
1✔
882

883
                return session.createQuery(cq).getResultList();
1✔
884
        }
885

886
        /**
887
         * @see org.openmrs.api.db.ConceptDAO#getSetsContainingConcept(org.openmrs.Concept)
888
         */
889
        @Override
890
        public List<ConceptSet> getSetsContainingConcept(Concept concept) {
891
                Session session = sessionFactory.getCurrentSession();
1✔
892
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
893
                CriteriaQuery<ConceptSet> cq = cb.createQuery(ConceptSet.class);
1✔
894
                Root<ConceptSet> root = cq.from(ConceptSet.class);
1✔
895

896
                cq.where(cb.equal(root.get("concept"), concept));
1✔
897

898
                return session.createQuery(cq).getResultList();
1✔
899
        }
900
        
901
        /**
902
         * returns a list of n-generations of parents of a concept in a concept set
903
         * 
904
         * @param current
905
         * @return List&lt;Concept&gt;
906
         * @throws DAOException
907
         */
908
        @SuppressWarnings("unchecked")
909
        private List<Concept> getParents(Concept current) throws DAOException {
910
                List<Concept> parents = new ArrayList<>();
×
911
                if (current != null) {
×
912
                        Query query = sessionFactory.getCurrentSession().createQuery(
×
913
                            "from Concept c join c.conceptSets sets where sets.concept = ?").setParameter(0, current);
×
914
                        List<Concept> immedParents = query.getResultList();
×
915
                        for (Concept c : immedParents) {
×
916
                                parents.addAll(getParents(c));
×
917
                        }
×
918
                        parents.add(current);
×
919
                        if (log.isDebugEnabled()) {
×
920
                                log.debug("parents found: ");
×
921
                                for (Concept c : parents) {
×
922
                                        log.debug("id: {}", c.getConceptId());
×
923
                                }
×
924
                        }        
925
                }
926
                return parents;
×
927
        }
928
        
929
        /**
930
         * @see org.openmrs.api.db.ConceptDAO#getLocalesOfConceptNames()
931
         */
932
        @Override
933
        public Set<Locale> getLocalesOfConceptNames() {
934
                Set<Locale> locales = new HashSet<>();
1✔
935
                
936
                Query query = sessionFactory.getCurrentSession().createQuery("select distinct locale from ConceptName");
1✔
937
                
938
                for (Object locale : query.getResultList()) {
1✔
939
                        locales.add((Locale) locale);
1✔
940
                }
1✔
941
                
942
                return locales;
1✔
943
        }
944
        
945
        /**
946
         * @see org.openmrs.api.db.ConceptDAO#getConceptNameTag(java.lang.Integer)
947
         */
948
        @Override
949
        public ConceptNameTag getConceptNameTag(Integer i) {
950
                return sessionFactory.getCurrentSession().get(ConceptNameTag.class, i);
1✔
951
        }
952

953
        /**
954
         * @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByName(java.lang.String)
955
         */
956
        @Override
957
        public ConceptNameTag getConceptNameTagByName(String name) {
958
                Session session = sessionFactory.getCurrentSession();
1✔
959
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
960
                CriteriaQuery<ConceptNameTag> cq = cb.createQuery(ConceptNameTag.class);
1✔
961
                Root<ConceptNameTag> root = cq.from(ConceptNameTag.class);
1✔
962

963
                cq.where(cb.equal(root.get("tag"), name));
1✔
964

965
                List<ConceptNameTag> conceptNameTags = session.createQuery(cq).getResultList();
1✔
966
                if (conceptNameTags.isEmpty()) {
1✔
967
                        return null;
1✔
968
                }
969

970
                return conceptNameTags.get(0);
1✔
971
        }
972
        
973
        /**
974
         * @see org.openmrs.api.db.ConceptDAO#getAllConceptNameTags()
975
         */
976
        @Override
977
        @SuppressWarnings("unchecked")
978
        public List<ConceptNameTag> getAllConceptNameTags() {
979
                return sessionFactory.getCurrentSession().createQuery("from ConceptNameTag cnt order by cnt.tag").list();
1✔
980
        }
981
        
982
        /**
983
         * @see org.openmrs.api.db.ConceptDAO#getConceptSource(java.lang.Integer)
984
         */
985
        @Override
986
        public ConceptSource getConceptSource(Integer conceptSourceId) {
987
                return sessionFactory.getCurrentSession().get(ConceptSource.class, conceptSourceId);
1✔
988
        }
989
        
990
        /**
991
         * @see org.openmrs.api.db.ConceptDAO#getAllConceptSources(boolean)
992
         */
993
        @Override
994
        public List<ConceptSource> getAllConceptSources(boolean includeRetired) throws DAOException {
995
                Session session = sessionFactory.getCurrentSession();
1✔
996
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
997
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
998
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
999

1000
                if (!includeRetired) {
1✔
1001
                        cq.where(cb.isFalse(root.get("retired")));
1✔
1002
                }
1003

1004
                return session.createQuery(cq).getResultList();
1✔
1005
        }
1006
        
1007
        /**
1008
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptSource(org.openmrs.ConceptSource)
1009
         */
1010
        @Override
1011
        public ConceptSource deleteConceptSource(ConceptSource cs) throws DAOException {
1012
                sessionFactory.getCurrentSession().delete(cs);
1✔
1013
                return cs;
1✔
1014
        }
1015
        
1016
        /**
1017
         * @see org.openmrs.api.db.ConceptDAO#saveConceptSource(org.openmrs.ConceptSource)
1018
         */
1019
        @Override
1020
        public ConceptSource saveConceptSource(ConceptSource conceptSource) throws DAOException {
1021
                sessionFactory.getCurrentSession().saveOrUpdate(conceptSource);
1✔
1022
                return conceptSource;
1✔
1023
        }
1024
        
1025
        /**
1026
         * @see org.openmrs.api.db.ConceptDAO#saveConceptNameTag(org.openmrs.ConceptNameTag)
1027
         */
1028
        @Override
1029
        public ConceptNameTag saveConceptNameTag(ConceptNameTag nameTag) {
1030
                if (nameTag == null) {
1✔
1031
                        return null;
×
1032
                }
1033
                
1034
                sessionFactory.getCurrentSession().saveOrUpdate(nameTag);
1✔
1035
                return nameTag;
1✔
1036
        }
1037
        
1038
        /**
1039
         * @see org.openmrs.api.db.ConceptDAO#getMaxConceptId()
1040
         */
1041
        public Integer getMinConceptId() {
1042
                Query query = sessionFactory.getCurrentSession().createQuery("select min(conceptId) from Concept");
1✔
1043
                return JpaUtils.getSingleResultOrNull(query);
1✔
1044
        }
1045
        
1046
        /**
1047
         * @see org.openmrs.api.db.ConceptDAO#getMaxConceptId()
1048
         */
1049
        @Override
1050
        public Integer getMaxConceptId() {
1051
                Query query = sessionFactory.getCurrentSession().createQuery("select max(conceptId) from Concept");
1✔
1052
                return JpaUtils.getSingleResultOrNull(query);
1✔
1053
        }
1054
        
1055
        /**
1056
         * @see org.openmrs.api.db.ConceptDAO#conceptIterator()
1057
         */
1058
        @Override
1059
        public Iterator<Concept> conceptIterator() {
1060
                return new ConceptIterator();
1✔
1061
        }
1062
        
1063
        /**
1064
         * An iterator that loops over all concepts in the dictionary one at a time
1065
         */
1066
        private class ConceptIterator implements Iterator<Concept> {
1067
                
1068
                Concept currentConcept = null;
1✔
1069
                
1070
                Concept nextConcept;
1071
                
1072
                public ConceptIterator() {
1✔
1073
                        final int firstConceptId = getMinConceptId();
1✔
1074
                        nextConcept = getConcept(firstConceptId);
1✔
1075
                }
1✔
1076
                
1077
                /**
1078
                 * @see java.util.Iterator#hasNext()
1079
                 */
1080
                @Override
1081
                public boolean hasNext() {
1082
                        return nextConcept != null;
1✔
1083
                }
1084
                
1085
                /**
1086
                 * @see java.util.Iterator#next()
1087
                 */
1088
                @Override
1089
                public Concept next() {
1090
                        if (currentConcept != null) {
1✔
1091
                                sessionFactory.getCurrentSession().evict(currentConcept);
1✔
1092
                        }
1093
                        
1094
                        currentConcept = nextConcept;
1✔
1095
                        nextConcept = getNextConcept(currentConcept);
1✔
1096
                        
1097
                        return currentConcept;
1✔
1098
                }
1099
                
1100
                /**
1101
                 * @see java.util.Iterator#remove()
1102
                 */
1103
                @Override
1104
                public void remove() {
1105
                        throw new UnsupportedOperationException();
×
1106
                }
1107
                
1108
        }
1109

1110
        /**
1111
         * @see org.openmrs.api.db.ConceptDAO#getConceptsByMapping(String, String, boolean)
1112
         */
1113
        @Override
1114
        @Deprecated
1115
        public List<Concept> getConceptsByMapping(String code, String sourceName, boolean includeRetired) {
1116
                Session session = sessionFactory.getCurrentSession();
×
1117
                CriteriaBuilder cb = session.getCriteriaBuilder();
×
1118
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
×
1119
                Root<ConceptMap> root = cq.from(ConceptMap.class);
×
1120

1121
                List<Predicate> predicates = createSearchConceptMapCriteria(cb, root, code, sourceName, includeRetired);
×
1122

1123
                cq.where(predicates.toArray(new Predicate[]{}));
×
1124

1125
                cq.select(root.get("concept"));
×
1126

1127
                Join<ConceptMap, Concept> conceptJoin = root.join("concept");
×
1128
                if (includeRetired) {
×
1129
                        cq.orderBy(cb.asc(conceptJoin.get("retired")));
×
1130
                }
1131

1132
                return session.createQuery(cq).getResultList()
×
1133
                        .stream().distinct().collect(toList());
×
1134
        }
1135

1136
        /**
1137
         * @see org.openmrs.api.db.ConceptDAO#getConceptIdsByMapping(String, String, boolean)
1138
         */
1139
        @Override
1140
        public List<Integer> getConceptIdsByMapping(String code, String sourceName, boolean includeRetired) {
1141
                Session session = sessionFactory.getCurrentSession();
1✔
1142
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1143
                CriteriaQuery<Integer> cq = cb.createQuery(Integer.class);
1✔
1144
                Root<ConceptMap> root = cq.from(ConceptMap.class);
1✔
1145

1146
                List<Predicate> predicates = createSearchConceptMapCriteria(cb, root, code, sourceName, includeRetired);
1✔
1147

1148
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
1149

1150
                cq.select(root.get("concept").get("conceptId"));
1✔
1151

1152
                Join<ConceptMap, Concept> conceptJoin = root.join("concept");
1✔
1153
                if (includeRetired) {
1✔
1154
                        cq.orderBy(cb.asc(conceptJoin.get("retired")));
1✔
1155
                }
1156

1157
                return session.createQuery(cq).getResultList()
1✔
1158
                        .stream().distinct().collect(toList());
1✔
1159
        }
1160
        
1161
        /**
1162
         * @see org.openmrs.api.db.ConceptDAO#getConceptByUuid(java.lang.String)
1163
         */
1164
        @Override
1165
        public Concept getConceptByUuid(String uuid) {
1166
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Concept.class, uuid);
1✔
1167
        }
1168

1169
        /**
1170
         * @see org.openmrs.api.db.ConceptDAO#getConcepts(ConceptSearchCriteria)
1171
         */
1172
        @Override
1173
        public List<Concept> getConcepts(ConceptSearchCriteria criteria) {
1174
                if (criteria == null) {
1✔
1175
                        return Collections.emptyList();
×
1176
                }
1177
                Session session = sessionFactory.getCurrentSession();
1✔
1178
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1179
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
1180
                Root<Concept> root = cq.from(Concept.class);
1✔
1181

1182
                List<Predicate> orPredicates = new ArrayList<>();
1✔
1183

1184
                if (criteria.getUuids() != null && !criteria.getUuids().isEmpty()) {
1✔
1185
                        orPredicates.add(root.get("uuid").in(criteria.getUuids()));
1✔
1186
                }
1187

1188
                if (criteria.getConceptIds() != null && !criteria.getConceptIds().isEmpty()) {
1✔
1189
                        orPredicates.add(root.get("conceptId").in(criteria.getConceptIds()));
1✔
1190
                }
1191

1192
                if (criteria.getMappings() != null && !criteria.getMappings().isEmpty()) {
1✔
1193
                        boolean caseSensitive = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive();
1✔
1194
                        for (String mapping : criteria.getMappings()) {
1✔
1195
                                buildMappingExistsPredicate(cq, root, cb, mapping, caseSensitive).ifPresent(orPredicates::add);
1✔
1196
                        }
1✔
1197
                }
1198

1199
                if (criteria.getNames() != null && !criteria.getNames().isEmpty()) {
1✔
1200
                        boolean caseSensitive = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive();
1✔
1201
                        Locale locale = Context.getLocale();
1✔
1202
                        Locale language = new Locale(locale.getLanguage() + "%");
1✔
1203
                        for (String name : criteria.getNames()) {
1✔
1204
                                orPredicates.add(buildNameExistsPredicate(cq, root, cb, name, caseSensitive, locale, language));
1✔
1205
                        }
1✔
1206
                }
1207

1208
                if (orPredicates.isEmpty()) {
1✔
1209
                        return new ArrayList<>();
1✔
1210
                }
1211

1212
                Predicate combined = cb.or(orPredicates.toArray(new Predicate[0]));
1✔
1213
                if (!criteria.getIncludeRetired()) {
1✔
1214
                        combined = cb.and(combined, cb.isFalse(root.get("retired")));
1✔
1215
                }
1216
                cq.where(combined);
1✔
1217

1218
                return session.createQuery(cq).getResultList().stream().distinct().collect(toList());
1✔
1219
        }
1220

1221
        // mapping is assumed to be in the form "source:code"
1222
        private Optional<Predicate> buildMappingExistsPredicate(CriteriaQuery<Concept> cq, Root<Concept> root,
1223
                CriteriaBuilder cb, String mapping, boolean caseSensitive) {
1224
                int idx = mapping.indexOf(":");
1✔
1225
                if (idx < 0 || idx >= mapping.length() - 1) {
1✔
1226
                        return Optional.empty();
×
1227
                }
1228
                String sourceName = mapping.substring(0, idx).trim();
1✔
1229
                String code = mapping.substring(idx + 1).trim();
1✔
1230
                if (sourceName.isEmpty() || code.isEmpty()) {
1✔
1231
                        return Optional.empty();
×
1232
                }
1233

1234
                Subquery<ConceptMap> sub = cq.subquery(ConceptMap.class);
1✔
1235
                Root<ConceptMap> mapRoot = sub.from(ConceptMap.class);
1✔
1236
                Join<ConceptMap, ConceptReferenceTerm> termJoin = mapRoot.join(CONCEPT_REFERENCE_TERM);
1✔
1237
                Join<ConceptReferenceTerm, ConceptSource> sourceJoin = termJoin.join(CONCEPT_SOURCE);
1✔
1238

1239
                List<Predicate> predicates = new ArrayList<>();
1✔
1240
                predicates.add(cb.equal(mapRoot.get("concept"), root));
1✔
1241
                if (caseSensitive) {
1✔
1242
                        predicates.add(cb.equal(cb.lower(termJoin.get("code")), code.toLowerCase()));
1✔
1243
                        predicates.add(cb.or(cb.equal(cb.lower(sourceJoin.get("name")), sourceName.toLowerCase()),
1✔
1244
                            cb.equal(cb.lower(sourceJoin.get(HL7_CODE)), sourceName.toLowerCase())));
1✔
1245
                } else {
1246
                        predicates.add(cb.equal(termJoin.get("code"), code));
×
1247
                        predicates.add(
×
1248
                            cb.or(cb.equal(sourceJoin.get("name"), sourceName), cb.equal(sourceJoin.get(HL7_CODE), sourceName)));
×
1249
                }
1250
                sub.select(mapRoot).where(predicates.toArray(new Predicate[0]));
1✔
1251
                return Optional.of(cb.exists(sub));
1✔
1252
        }
1253

1254
        private Predicate buildNameExistsPredicate(CriteriaQuery<Concept> cq, Root<Concept> root, CriteriaBuilder cb,
1255
                String name, boolean caseSensitive, Locale locale, Locale language) {
1256
                Subquery<ConceptName> sub = cq.subquery(ConceptName.class);
1✔
1257
                Root<ConceptName> nameRoot = sub.from(ConceptName.class);
1✔
1258

1259
                List<Predicate> predicates = new ArrayList<>();
1✔
1260
                predicates.add(cb.equal(nameRoot.get("concept"), root));
1✔
1261
                predicates.add(cb.or(cb.equal(nameRoot.get("locale"), locale),
1✔
1262
                    cb.like(nameRoot.get("locale").as(String.class), language.toString())));
1✔
1263
                if (caseSensitive) {
1✔
1264
                        predicates.add(cb.like(cb.lower(nameRoot.get("name")), name.toLowerCase()));
1✔
1265
                } else {
1266
                        predicates.add(cb.equal(nameRoot.get("name"), name));
×
1267
                }
1268
                predicates.add(cb.isFalse(nameRoot.get("voided")));
1✔
1269
                sub.select(nameRoot).where(predicates.toArray(new Predicate[0]));
1✔
1270
                return cb.exists(sub);
1✔
1271
        }
1272

1273
        /**
1274
         * @see org.openmrs.api.db.ConceptDAO#getConceptClassByUuid(java.lang.String)
1275
         */
1276
        @Override
1277
        public ConceptClass getConceptClassByUuid(String uuid) {
1278
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptClass.class, uuid);
1✔
1279
        }
1280

1281
        @Override
1282
        public ConceptAnswer getConceptAnswerByUuid(String uuid) {
1283
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAnswer.class, uuid);
1✔
1284
        }
1285

1286
        @Override
1287
        public ConceptName getConceptNameByUuid(String uuid) {
1288
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptName.class, uuid);
1✔
1289
        }
1290

1291
        @Override
1292
        public ConceptSet getConceptSetByUuid(String uuid) {
1293
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSet.class, uuid);
1✔
1294
        }
1295

1296
        @Override
1297
        public ConceptSource getConceptSourceByUuid(String uuid) {
1298
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSource.class, uuid);
1✔
1299
        }
1300

1301
        /**
1302
         * @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByUuid(java.lang.String)
1303
         */
1304
        @Override
1305
        public ConceptDatatype getConceptDatatypeByUuid(String uuid) {
1306
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptDatatype.class, uuid);
1✔
1307
        }
1308

1309
        /**
1310
         * @see org.openmrs.api.db.ConceptDAO#getConceptNumericByUuid(java.lang.String)
1311
         */
1312
        @Override
1313
        public ConceptNumeric getConceptNumericByUuid(String uuid) {
1314
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptNumeric.class, uuid);
1✔
1315
        }
1316

1317
        /**
1318
         * @see org.openmrs.api.db.ConceptDAO#getConceptProposalByUuid(java.lang.String)
1319
         */
1320
        @Override
1321
        public ConceptProposal getConceptProposalByUuid(String uuid) {
1322
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptProposal.class, uuid);
1✔
1323
        }
1324
        
1325
        /**
1326
         * @see org.openmrs.api.db.ConceptDAO#getDrugByUuid(java.lang.String)
1327
         */
1328
        @Override
1329
        public Drug getDrugByUuid(String uuid) {
1330
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Drug.class, uuid);
1✔
1331
        }
1332

1333
        @Override
1334
        public DrugIngredient getDrugIngredientByUuid(String uuid) {
1335
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, DrugIngredient.class, uuid);
1✔
1336
        }
1337
        
1338
        /**
1339
         * @see org.openmrs.api.db.ConceptDAO#getConceptUuids()
1340
         */
1341
        @Override
1342
        @SuppressWarnings("unchecked")
1343
        public Map<Integer, String> getConceptUuids() {
1344
                Map<Integer, String> ret = new HashMap<>();
×
1345
                Query q = sessionFactory.getCurrentSession().createQuery("select conceptId, uuid from Concept");
×
1346
                List<Object[]> list = q.getResultList();
×
1347
                for (Object[] o : list) {
×
1348
                        ret.put((Integer) o[0], (String) o[1]);
×
1349
                }
×
1350
                return ret;
×
1351
        }
1352

1353
        /**
1354
         * @see org.openmrs.api.db.ConceptDAO#getConceptDescriptionByUuid(java.lang.String)
1355
         */
1356
        @Override
1357
        public ConceptDescription getConceptDescriptionByUuid(String uuid) {
1358
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptDescription.class, uuid);
1✔
1359
        }
1360

1361
        /**
1362
         * @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByUuid(java.lang.String)
1363
         */
1364
        @Override
1365
        public ConceptNameTag getConceptNameTagByUuid(String uuid) {
1366
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptNameTag.class, uuid);
1✔
1367
        }
1368

1369
        /**
1370
         * @see org.openmrs.api.db.ConceptDAO#getConceptMapsBySource(ConceptSource)
1371
         */
1372
        @Override
1373
        public List<ConceptMap> getConceptMapsBySource(ConceptSource conceptSource) throws DAOException {
1374
                Session session = sessionFactory.getCurrentSession();
1✔
1375
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1376
                CriteriaQuery<ConceptMap> cq = cb.createQuery(ConceptMap.class);
1✔
1377

1378
                Root<ConceptMap> root = cq.from(ConceptMap.class);
1✔
1379
                Join<ConceptMap, ConceptReferenceTerm> conceptReferenceTermJoin = root.join(CONCEPT_REFERENCE_TERM);
1✔
1380

1381
                cq.where(cb.equal(conceptReferenceTermJoin.get(CONCEPT_SOURCE), conceptSource));
1✔
1382

1383
                return session.createQuery(cq).getResultList();
1✔
1384
        }
1385

1386
        /**
1387
         * @see org.openmrs.api.db.ConceptDAO#getConceptSourceByName(java.lang.String)
1388
         */
1389
        @Override
1390
        public ConceptSource getConceptSourceByName(String conceptSourceName) throws DAOException {
1391
                Session session = sessionFactory.getCurrentSession();
1✔
1392
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1393
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
1394
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
1395

1396
                cq.where(cb.equal(root.get("name"), conceptSourceName));
1✔
1397

1398
                return session.createQuery(cq).uniqueResult();
1✔
1399
        }
1400

1401
        /**
1402
         * @see org.openmrs.api.db.ConceptDAO#getConceptSourceByUniqueId(java.lang.String)
1403
         */
1404
        @Override
1405
        public ConceptSource getConceptSourceByUniqueId(String uniqueId) {
1406
                if (StringUtils.isBlank(uniqueId)) {
1✔
1407
                        return null;
1✔
1408
                }
1409

1410
                Session session = sessionFactory.getCurrentSession();
1✔
1411
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1412
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
1413
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
1414

1415
                cq.where(cb.equal(root.get("uniqueId"), uniqueId));
1✔
1416

1417
                return session.createQuery(cq).uniqueResult();
1✔
1418
        }
1419

1420
        /**
1421
         * @see org.openmrs.api.db.ConceptDAO#getConceptSourceByHL7Code(java.lang.String)
1422
         */
1423
        @Override
1424
        public ConceptSource getConceptSourceByHL7Code(String hl7Code) {
1425
                if (StringUtils.isBlank(hl7Code)) {
1✔
1426
                        return null;
1✔
1427
                }
1428

1429
                Session session = sessionFactory.getCurrentSession();
1✔
1430
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1431
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
1432
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
1433

1434
                cq.where(cb.equal(root.get(HL7_CODE), hl7Code));
1✔
1435

1436
                return session.createQuery(cq).uniqueResult();
1✔
1437
        }
1438

1439
        /**
1440
         * @see org.openmrs.api.db.ConceptDAO#getSavedConceptDatatype(org.openmrs.Concept)
1441
         */
1442
        @Override
1443
        public ConceptDatatype getSavedConceptDatatype(Concept concept) {
1444
                Query sql = sessionFactory.getCurrentSession().createSQLQuery(
1✔
1445
                                "select datatype.* from concept_datatype datatype, concept concept where " +
1446
                                        "datatype.concept_datatype_id = concept.datatype_id and concept.concept_id=:conceptId")
1447
                        .addEntity(ConceptDatatype.class);
1✔
1448
                sql.setParameter("conceptId", concept.getConceptId());
1✔
1449

1450
                return JpaUtils.getSingleResultOrNull(sql);
1✔
1451
        }
1452
        
1453
        /**
1454
         * @see org.openmrs.api.db.ConceptDAO#getSavedConceptName(org.openmrs.ConceptName)
1455
         */
1456
        @Override
1457
        public ConceptName getSavedConceptName(ConceptName conceptName) {
1458
                sessionFactory.getCurrentSession().refresh(conceptName);
1✔
1459
                return conceptName;
1✔
1460
        }
1461

1462
        /**
1463
         * @see org.openmrs.api.db.ConceptDAO#getConceptStopWords(java.util.Locale)
1464
         */
1465
        @Override
1466
        public List<String> getConceptStopWords(Locale locale) throws DAOException {
1467

1468
                locale = (locale == null ? Context.getLocale() : locale);
1✔
1469

1470
                Session session = sessionFactory.getCurrentSession();
1✔
1471
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1472
                CriteriaQuery<String> cq = cb.createQuery(String.class);
1✔
1473
                Root<ConceptStopWord> root = cq.from(ConceptStopWord.class);
1✔
1474

1475
                cq.select(root.get("value"));
1✔
1476
                cq.where(cb.equal(root.get("locale"), locale));
1✔
1477

1478
                return session.createQuery(cq).getResultList();
1✔
1479
        }
1480

1481
        /**
1482
         * @see org.openmrs.api.db.ConceptDAO#saveConceptStopWord(org.openmrs.ConceptStopWord)
1483
         */
1484
        @Override
1485
        public ConceptStopWord saveConceptStopWord(ConceptStopWord conceptStopWord) throws DAOException {
1486
                if (conceptStopWord != null) {
1✔
1487
                        Session session = sessionFactory.getCurrentSession();
1✔
1488
                        CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1489
                        CriteriaQuery<ConceptStopWord> cq = cb.createQuery(ConceptStopWord.class);
1✔
1490
                        Root<ConceptStopWord> root = cq.from(ConceptStopWord.class);
1✔
1491

1492
                        cq.where(cb.and(
1✔
1493
                                cb.equal(root.get("value"), conceptStopWord.getValue()),
1✔
1494
                                cb.equal(root.get("locale"), conceptStopWord.getLocale())));
1✔
1495

1496
                        List<ConceptStopWord> stopWordList = session.createQuery(cq).getResultList();
1✔
1497

1498
                        if (!stopWordList.isEmpty()) {
1✔
1499
                                throw new DAOException("Duplicate ConceptStopWord Entry");
1✔
1500
                        }
1501
                        session.saveOrUpdate(conceptStopWord);
1✔
1502
                }
1503
                return conceptStopWord;
1✔
1504
        }
1505
        
1506
        /**
1507
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptStopWord(java.lang.Integer)
1508
         */
1509
        @Override
1510
        public void deleteConceptStopWord(Integer conceptStopWordId) throws DAOException {
1511
                if (conceptStopWordId == null) {
1✔
1512
                        throw new DAOException("conceptStopWordId is null");
×
1513
                }
1514

1515
                Session session = sessionFactory.getCurrentSession();
1✔
1516
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1517
                CriteriaQuery<ConceptStopWord> cq = cb.createQuery(ConceptStopWord.class);
1✔
1518
                Root<ConceptStopWord> root = cq.from(ConceptStopWord.class);
1✔
1519
                
1520
                cq.where(cb.equal(root.get("conceptStopWordId"), conceptStopWordId));
1✔
1521

1522
                ConceptStopWord csw = session.createQuery(cq).uniqueResult();
1✔
1523
                if (csw == null) {
1✔
1524
                        throw new DAOException("Concept Stop Word not found or already deleted");
×
1525
                }
1526
                session.delete(csw);
1✔
1527
        }
1✔
1528

1529
        /**
1530
         * @see org.openmrs.api.db.ConceptDAO#getAllConceptStopWords()
1531
         */
1532
        @Override
1533
        public List<ConceptStopWord> getAllConceptStopWords() {
1534
                Session session = sessionFactory.getCurrentSession();
1✔
1535
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1536
                CriteriaQuery<ConceptStopWord> cq = cb.createQuery(ConceptStopWord.class);
1✔
1537
                cq.from(ConceptStopWord.class);
1✔
1538

1539
                return session.createQuery(cq).getResultList();
1✔
1540
        }
1541
        
1542
        /**
1543
         * @see ConceptService#getCountOfDrugs(String, Concept, boolean, boolean, boolean)
1544
         */
1545
        @Override
1546
        public Long getCountOfDrugs(String drugName, Concept concept, boolean searchKeywords, boolean searchDrugConceptNames,
1547
                boolean includeRetired) throws DAOException {
1548
                SearchQuery<Drug> drugsQuery = newDrugQuery(drugName, searchKeywords, searchDrugConceptNames, Context.getLocale(),
1✔
1549
                    false, concept, includeRetired);
1550
                
1551
                if (drugsQuery == null) {
1✔
1552
                        return 0L;
×
1553
                }
1554
                
1555
                return drugsQuery.fetchTotalHitCount();
1✔
1556
        }
1557
        
1558
        /**
1559
         * <strong>Should</strong> return a drug if either the drug name or concept name matches the phase not both
1560
         * <strong>Should</strong> return distinct drugs
1561
         * <strong>Should</strong> return a drug, if phrase match concept_name No need to match both concept_name and
1562
         *         drug_name
1563
         * <strong>Should</strong> return drug when phrase match drug_name even searchDrugConceptNames is false
1564
         * <strong>Should</strong> return a drug if phrase match drug_name No need to match both concept_name and
1565
         *         drug_name
1566
         */
1567
        @Override
1568
        public List<Drug> getDrugs(String drugName, Concept concept, boolean searchKeywords, boolean searchDrugConceptNames,
1569
                boolean includeRetired, Integer start, Integer length) throws DAOException {
1570
                SearchQuery<Drug> drugsQuery = newDrugQuery(drugName, searchKeywords, searchDrugConceptNames, Context.getLocale(),
1✔
1571
                    false, concept, includeRetired);
1572
                
1573
                if (drugsQuery == null) {
1✔
1574
                        return Collections.emptyList();
×
1575
                }
1576
                
1577
                return drugsQuery.fetchHits(start, length);
1✔
1578
        }
1579
        
1580
        private SearchQuery<Drug> newDrugQuery(String drugName, boolean searchKeywords, boolean searchDrugConceptNames,
1581
                                                                                   Locale locale, boolean exactLocale, Concept concept, boolean includeRetired) {
1582
                if (StringUtils.isBlank(drugName) && concept == null) {
1✔
1583
                        return null;
×
1584
                }
1585
                final Collection<Locale> locales = Collections.singletonList(locale == null ? Context.getLocale() : locale);
1✔
1586

1587
                final List<Object> conceptIds;
1588
                // If a concept is supplied, skip the relatively expensive concept-name full-text search
1589
                if (searchDrugConceptNames && concept == null) {
1✔
1590
                        SearchSession searchSession = searchSessionFactory.getSearchSession();
1✔
1591
                        SearchScope<ConceptName> scope = searchSession.scope(ConceptName.class);
1✔
1592
                        
1593
                        SearchPredicate conceptNamePredicate = newConceptNamePredicate(scope.predicate(), drugName, searchKeywords, locales, exactLocale,
1✔
1594
                                includeRetired, null, null, null, null, null);
1595

1596
                        conceptIds = SearchQueryUnique.findUniqueKeys(searchSession, scope, conceptNamePredicate, "concept.conceptId");
1✔
1597
                } else {
1✔
1598
                        conceptIds = Collections.emptyList();
1✔
1599
                }
1600
                
1601
                return searchSessionFactory.getSearchSession().search(Drug.class).where(f -> f.bool().with(b -> {
1✔
1602
                        b.minimumShouldMatchNumber(1);
1✔
1603
                        List<String> tokenizedName = tokenizeName(drugName, locales);
1✔
1604
                        BooleanPredicateClausesStep<?> nameQuery = newNameQuery(f, tokenizedName, drugName, searchKeywords);
1✔
1605
                        b.should(f.match().field("drugReferenceMaps.conceptReferenceTerm.code").matching(drugName).boost(10f));
1✔
1606
                        b.should(nameQuery.boost(0.5f));
1✔
1607
                        if (concept != null) {
1✔
1608
                                b.should(f.match().field("concept.conceptId").matching(concept.getId()).boost(0.1f));
1✔
1609
                        }
1610
                        if (!conceptIds.isEmpty()) {
1✔
1611
                                float boost = 0.1f;
1✔
1612
                                int boostItems = 10; // boost first items in order
1✔
1613
                                int i = 0;
1✔
1614
                                for (Object conceptId : conceptIds) {
1✔
1615
                                        b.should(f.match().field("concept.conceptId").matching(conceptId).boost(boost));
1✔
1616
                                        boost = boost * 0.9f;
1✔
1617
                                        i++;
1✔
1618
                                        if (boostItems == i) {
1✔
1619
                                                break;
×
1620
                                        }
1621
                                }
1✔
1622
                                if (conceptIds.size() > boostItems) {
1✔
1623
                                        b.should(f.terms().field("concept.conceptId").matchingAny(
×
1624
                                                conceptIds.subList(boostItems, conceptIds.size())).boost(boost));
×
1625
                                }
1626
                        }
1627
                        if (!includeRetired) {
1✔
1628
                                b.filter(f.match().field("retired").matching(Boolean.FALSE));
1✔
1629
                        }
1630
                })).toQuery();
1✔
1631
        }
1632

1633
        /**
1634
         * @see ConceptDAO#getConcepts(String, List, boolean, List, List, List, List, Concept, Integer,
1635
         *      Integer)
1636
         */
1637
        @Override
1638
        public List<ConceptSearchResult> getConcepts(final String phrase, final List<Locale> locales,
1639
                final boolean includeRetired, final List<ConceptClass> requireClasses, final List<ConceptClass> excludeClasses,
1640
                final List<ConceptDatatype> requireDatatypes, final List<ConceptDatatype> excludeDatatypes,
1641
                final Concept answersToConcept, final Integer start, final Integer size) throws DAOException {
1642

1643
                return SearchQueryUnique.search(searchSessionFactory,
1✔
1644
                    SearchQueryUnique.newQuery(ConceptName.class,
1✔
1645
                        f -> newConceptNamePredicate(f, phrase, true, locales, false, includeRetired, requireClasses, excludeClasses,
1✔
1646
                            requireDatatypes, excludeDatatypes, answersToConcept),
1647
                        "concept.conceptId", (List<ConceptName> names) -> {
1648
                                Map<Integer, Concept> conceptsById = multiLoadConceptsByHit(names);
1✔
1649
                                return names.stream().map(n -> {
1✔
1650
                                        Concept concept = conceptsById.get(n.getConcept().getConceptId());
1✔
1651
                                        return concept == null ? null : new ConceptSearchResult(phrase, concept, n);
1✔
1652
                                }).filter(Objects::nonNull).collect(Collectors.toList());
1✔
1653
                        }),
1654
                    start, size);
1655
        }
1656
        
1657
        @Override
1658
        public Integer getCountOfConcepts(final String phrase, List<Locale> locales, boolean includeRetired,
1659
                List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
1660
                List<ConceptDatatype> excludeDatatypes, Concept answersToConcept) throws DAOException {
1661
                
1662
                
1663
                return Math.toIntExact(SearchQueryUnique.searchCount(searchSessionFactory, 
1✔
1664
                        SearchQueryUnique.newQuery(ConceptName.class, f -> newConceptNamePredicate(f, phrase, 
1✔
1665
                                true, locales, false, includeRetired, requireClasses, excludeClasses, 
1666
                                requireDatatypes, excludeDatatypes, answersToConcept), "concept.conceptId")));
1667
        }
1668
        
1669
        private SearchPredicate newConceptNamePredicate(SearchPredicateFactory f, final String phrase, boolean searchKeywords,
1670
                                                                                                           Collection<Locale> locales, boolean searchExactLocale, boolean includeRetired, List<ConceptClass> requireClasses,
1671
                                                                                                           List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
1672
                                                                                                           List<ConceptDatatype> excludeDatatypes, Concept answersToConcept) {
1673
                return f.bool().with(b -> {
1✔
1674
                        if (!StringUtils.isBlank(phrase)) {
1✔
1675
                                final Collection<Locale> searchLocales;
1676

1677
                                if (locales == null) {
1✔
1678
                                        searchLocales = Collections.singletonList(Context.getLocale());
1✔
1679
                                } else {
1680
                                        searchLocales = new HashSet<>(locales);
1✔
1681
                                }
1682

1683
                                b.must(newConceptNameQuery(f, phrase, searchKeywords, searchLocales, searchExactLocale));
1✔
1684
                        }
1685

1686
                        if (!CollectionUtils.isEmpty(requireClasses)) {
1✔
1687
                                b.filter(f.terms().field("concept.conceptClass")
1✔
1688
                                        .matchingAny(requireClasses));
1✔
1689
                        }
1690
                        if (!CollectionUtils.isEmpty(excludeClasses)) {
1✔
1691
                                b.filter(f.not(f.terms().field("concept.conceptClass")
1✔
1692
                                        .matchingAny(excludeClasses)));
1✔
1693
                        }
1694
                        if (!CollectionUtils.isEmpty(requireDatatypes)) {
1✔
1695
                                b.filter(f.terms().field("concept.datatype")
×
1696
                                        .matchingAny(requireDatatypes));
×
1697
                        }
1698
                        if (!CollectionUtils.isEmpty(excludeDatatypes)) {
1✔
1699
                                b.filter(f.not(f.terms().field("concept.datatype")
×
1700
                                        .matchingAny(excludeDatatypes)));
×
1701
                        }
1702

1703
                        if (answersToConcept != null) {
1✔
1704
                                Collection<ConceptAnswer> answers = answersToConcept.getAnswers(false);
1✔
1705

1706
                                if (answers != null && !answers.isEmpty()) {
1✔
1707
                                        List<Integer> ids = new ArrayList<>();
×
1708
                                        for (ConceptAnswer conceptAnswer : answersToConcept.getAnswers(false)) {
×
1709
                                                ids.add(conceptAnswer.getAnswerConcept().getId());
×
1710
                                        }
×
1711
                                        b.filter(f.terms().field("concept.conceptId").matchingAny(ids));
×
1712
                                }
1713
                        }
1714

1715
                        if (!includeRetired) {
1✔
1716
                                b.filter(f.match().field("concept.retired").matching(Boolean.FALSE));
1✔
1717
                        }
1718
                }).toPredicate();
1✔
1719
        }
1720

1721
        /**
1722
         * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypes(boolean, boolean)
1723
         */
1724
        @Override
1725
        public List<ConceptMapType> getConceptMapTypes(boolean includeRetired, boolean includeHidden) throws DAOException {
1726
                Session session = sessionFactory.getCurrentSession();
1✔
1727
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1728
                CriteriaQuery<ConceptMapType> cq = cb.createQuery(ConceptMapType.class);
1✔
1729
                Root<ConceptMapType> root = cq.from(ConceptMapType.class);
1✔
1730

1731
                List<Predicate> predicates = new ArrayList<>();
1✔
1732
                if (!includeRetired) {
1✔
1733
                        predicates.add(cb.isFalse(root.get("retired")));
1✔
1734
                }
1735
                if (!includeHidden) {
1✔
1736
                        predicates.add(cb.isFalse(root.get("isHidden")));
1✔
1737
                }
1738

1739
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
1740

1741
                List<ConceptMapType> conceptMapTypes = session.createQuery(cq).getResultList();
1✔
1742
                conceptMapTypes.sort(new ConceptMapTypeComparator());
1✔
1743

1744
                return conceptMapTypes;
1✔
1745
        }
1746
        
1747
        /**
1748
         * @see org.openmrs.api.db.ConceptDAO#getConceptMapType(java.lang.Integer)
1749
         */
1750
        @Override
1751
        public ConceptMapType getConceptMapType(Integer conceptMapTypeId) throws DAOException {
1752
                return sessionFactory.getCurrentSession().get(ConceptMapType.class, conceptMapTypeId);
1✔
1753
        }
1754

1755
        /**
1756
         * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypeByUuid(java.lang.String)
1757
         */
1758
        @Override
1759
        public ConceptMapType getConceptMapTypeByUuid(String uuid) throws DAOException {
1760
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptMapType.class, uuid);
1✔
1761
        }
1762

1763
        /**
1764
         * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypeByName(java.lang.String)
1765
         */
1766
        @Override
1767
        public ConceptMapType getConceptMapTypeByName(String name) throws DAOException {
1768
                Session session = sessionFactory.getCurrentSession();
1✔
1769
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1770
                CriteriaQuery<ConceptMapType> cq = cb.createQuery(ConceptMapType.class);
1✔
1771
                Root<ConceptMapType> root = cq.from(ConceptMapType.class);
1✔
1772

1773
                cq.where(cb.like(cb.lower(root.get("name")), MatchMode.EXACT.toLowerCasePattern(name)));
1✔
1774

1775
                return session.createQuery(cq).uniqueResult();
1✔
1776
        }
1777
        
1778
        /**
1779
         * @see org.openmrs.api.db.ConceptDAO#saveConceptMapType(org.openmrs.ConceptMapType)
1780
         */
1781
        @Override
1782
        public ConceptMapType saveConceptMapType(ConceptMapType conceptMapType) throws DAOException {
1783
                sessionFactory.getCurrentSession().saveOrUpdate(conceptMapType);
1✔
1784
                return conceptMapType;
1✔
1785
        }
1786
        
1787
        /**
1788
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptMapType(org.openmrs.ConceptMapType)
1789
         */
1790
        @Override
1791
        public void deleteConceptMapType(ConceptMapType conceptMapType) throws DAOException {
1792
                sessionFactory.getCurrentSession().delete(conceptMapType);
1✔
1793
        }
1✔
1794

1795
        /**
1796
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTerms(boolean)
1797
         */
1798
        @Override
1799
        public List<ConceptReferenceTerm> getConceptReferenceTerms(boolean includeRetired) throws DAOException {
1800
                Session session = sessionFactory.getCurrentSession();
1✔
1801
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1802
                CriteriaQuery<ConceptReferenceTerm> cq = cb.createQuery(ConceptReferenceTerm.class);
1✔
1803
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
1✔
1804

1805
                if (!includeRetired) {
1✔
1806
                        cq.where(cb.isFalse(root.get("retired")));
1✔
1807
                }
1808
                return session.createQuery(cq).getResultList();
1✔
1809
        }
1810
        
1811
        /**
1812
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTerm(java.lang.Integer)
1813
         */
1814
        @Override
1815
        public ConceptReferenceTerm getConceptReferenceTerm(Integer conceptReferenceTermId) throws DAOException {
1816
                return sessionFactory.getCurrentSession().get(ConceptReferenceTerm.class,
1✔
1817
                    conceptReferenceTermId);
1818
        }
1819

1820
        /**
1821
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByUuid(java.lang.String)
1822
         */
1823
        @Override
1824
        public ConceptReferenceTerm getConceptReferenceTermByUuid(String uuid) throws DAOException {
1825
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptReferenceTerm.class, uuid);
1✔
1826
        }
1827
        
1828
        /**
1829
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermsBySource(ConceptSource)
1830
         */
1831
        @Override
1832
        public List<ConceptReferenceTerm> getConceptReferenceTermsBySource(ConceptSource conceptSource) throws DAOException {
1833
                Session session = sessionFactory.getCurrentSession();
×
1834
                CriteriaBuilder cb = session.getCriteriaBuilder();
×
1835
                CriteriaQuery<ConceptReferenceTerm> cq = cb.createQuery(ConceptReferenceTerm.class);
×
1836
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
×
1837

1838
                cq.where(cb.equal(root.get(CONCEPT_SOURCE), conceptSource));
×
1839

1840
                return session.createQuery(cq).getResultList();
×
1841
        }
1842

1843
        /**
1844
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByName(java.lang.String,
1845
         *      org.openmrs.ConceptSource)
1846
         */
1847
        @Override
1848
        public ConceptReferenceTerm getConceptReferenceTermByName(String name, ConceptSource conceptSource) throws DAOException {
1849
                Session session = sessionFactory.getCurrentSession();
1✔
1850
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1851
                CriteriaQuery<ConceptReferenceTerm> cq = cb.createQuery(ConceptReferenceTerm.class);
1✔
1852
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
1✔
1853

1854
                Predicate namePredicate = cb.like(cb.lower(root.get("name")), MatchMode.EXACT.toLowerCasePattern(name));
1✔
1855
                Predicate sourcePredicate = cb.equal(root.get(CONCEPT_SOURCE), conceptSource);
1✔
1856

1857
                cq.where(cb.and(namePredicate, sourcePredicate));
1✔
1858

1859
                List<ConceptReferenceTerm> terms = session.createQuery(cq).getResultList();
1✔
1860
                if (terms.isEmpty()) {
1✔
1861
                        return null;
×
1862
                } else if (terms.size() > 1) {
1✔
1863
                        throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithNameInSource",
×
1864
                                new Object[]{name, conceptSource.getName()});
×
1865
                }
1866
                return terms.get(0);
1✔
1867
        }
1868
        
1869
        /**
1870
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByCode(java.lang.String,
1871
         *      org.openmrs.ConceptSource)
1872
         */
1873
        @Override
1874
        public ConceptReferenceTerm getConceptReferenceTermByCode(String code, ConceptSource conceptSource) throws DAOException {
1875
                List<ConceptReferenceTerm> conceptReferenceTerms = getConceptReferenceTermByCode(code, conceptSource, true);
1✔
1876
                
1877
                if (conceptReferenceTerms.isEmpty()) {
1✔
1878
                        return null;
1✔
1879
                } else if (conceptReferenceTerms.size() > 1) {
1✔
1880
                        List<ConceptReferenceTerm> unretiredConceptReferenceTerms = conceptReferenceTerms.stream()
1✔
1881
                                .filter(term -> !term.getRetired())
1✔
1882
                                        .collect(toList());
1✔
1883
                        if (unretiredConceptReferenceTerms.size() == 1) {
1✔
1884
                                return unretiredConceptReferenceTerms.get(0);
1✔
1885
                        }
1886
                        
1887
                        // either more than one unretired concept term or more than one retired concept term
1888
                        throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithCodeInSource",
×
1889
                                new Object[] { code, conceptSource.getName() });
×
1890
                }
1891
                
1892
                return conceptReferenceTerms.get(0);
1✔
1893
        }
1894

1895
        /**
1896
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByCode(java.lang.String,
1897
         *      org.openmrs.ConceptSource, boolean)
1898
         */
1899
        @Override
1900
        public List<ConceptReferenceTerm> getConceptReferenceTermByCode(String code, ConceptSource conceptSource,
1901
                boolean includeRetired) throws DAOException {
1902
                Session session = sessionFactory.getCurrentSession();
1✔
1903
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1904
                CriteriaQuery<ConceptReferenceTerm> cq = cb.createQuery(ConceptReferenceTerm.class);
1✔
1905
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
1✔
1906

1907
                List<Predicate> predicates = new ArrayList<>();
1✔
1908
                predicates.add(cb.equal(root.get("code"), code));
1✔
1909
                predicates.add(cb.equal(root.get(CONCEPT_SOURCE), conceptSource));
1✔
1910

1911
                if (!includeRetired) {
1✔
1912
                        predicates.add(cb.isFalse(root.get("retired")));
1✔
1913
                }
1914
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
1915

1916
                return session.createQuery(cq).getResultList();
1✔
1917
        }
1918
        
1919
        /**
1920
         * @see org.openmrs.api.db.ConceptDAO#saveConceptReferenceTerm(org.openmrs.ConceptReferenceTerm)
1921
         */
1922
        @Override
1923
        public ConceptReferenceTerm saveConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws DAOException {
1924
                sessionFactory.getCurrentSession().saveOrUpdate(conceptReferenceTerm);
1✔
1925
                return conceptReferenceTerm;
1✔
1926
        }
1927
        
1928
        /**
1929
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptReferenceTerm(org.openmrs.ConceptReferenceTerm)
1930
         */
1931
        @Override
1932
        public void deleteConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws DAOException {
1933
                sessionFactory.getCurrentSession().delete(conceptReferenceTerm);
1✔
1934
        }
1✔
1935

1936
        @Override
1937
        public Long getCountOfConceptReferenceTerms(String query, ConceptSource conceptSource, boolean includeRetired)
1938
                throws DAOException {
1939
                Session session = sessionFactory.getCurrentSession();
1✔
1940
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1941
                CriteriaQuery<Long> cq = cb.createQuery(Long.class);
1✔
1942
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
1✔
1943

1944
                List<Predicate> predicates = createConceptReferenceTermPredicates(cb, root, query, conceptSource, includeRetired);
1✔
1945

1946
                cq.where(predicates.toArray(new Predicate[]{})).select(cb.count(root));
1✔
1947

1948
                return session.createQuery(cq).getSingleResult();
1✔
1949
        }
1950

1951

1952
        /**
1953
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTerms(String, ConceptSource, Integer,
1954
         *      Integer, boolean)
1955
         */
1956
        @Override
1957
        public List<ConceptReferenceTerm> getConceptReferenceTerms(String query, ConceptSource conceptSource, Integer start,
1958
                                                                                                                           Integer length, boolean includeRetired) throws APIException {
1959
                Session session = sessionFactory.getCurrentSession();
1✔
1960
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1961
                CriteriaQuery<ConceptReferenceTerm> cq = cb.createQuery(ConceptReferenceTerm.class);
1✔
1962
                Root<ConceptReferenceTerm> root = cq.from(ConceptReferenceTerm.class);
1✔
1963

1964
                List<Predicate> predicates = createConceptReferenceTermPredicates(cb, root, query, conceptSource, includeRetired);
1✔
1965
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
1966

1967
                TypedQuery<ConceptReferenceTerm> typedQuery = session.createQuery(cq);
1✔
1968

1969
                if (start != null) {
1✔
1970
                        typedQuery.setFirstResult(start);
1✔
1971
                }
1972
                if (length != null && length > 0) {
1✔
1973
                        typedQuery.setMaxResults(length);
1✔
1974
                }
1975

1976
                return typedQuery.getResultList();
1✔
1977
        }
1978

1979
        private List<Predicate> createConceptReferenceTermPredicates(CriteriaBuilder cb, Root<ConceptReferenceTerm> root,
1980
                                                                                                                                 String query, ConceptSource conceptSource, boolean includeRetired) {
1981
                List<Predicate> predicates = new ArrayList<>();
1✔
1982

1983
                if (conceptSource != null) {
1✔
1984
                        predicates.add(cb.equal(root.get(CONCEPT_SOURCE), conceptSource));
1✔
1985
                }
1986
                if (!includeRetired) {
1✔
1987
                        predicates.add(cb.isFalse(root.get("retired")));
1✔
1988
                }
1989
                if (query != null) {
1✔
1990
                        Predicate namePredicate = cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(query));
1✔
1991
                        Predicate codePredicate = cb.like(cb.lower(root.get("code")), MatchMode.ANYWHERE.toLowerCasePattern(query));
1✔
1992

1993
                        predicates.add(cb.or(namePredicate, codePredicate));
1✔
1994
                }
1995

1996
                return predicates;
1✔
1997
        }
1998

1999
        /**
2000
         * @see org.openmrs.api.db.ConceptDAO#getReferenceTermMappingsTo(ConceptReferenceTerm)
2001
         */
2002
        @Override
2003
        public List<ConceptReferenceTermMap> getReferenceTermMappingsTo(ConceptReferenceTerm term) throws DAOException {
2004
                Session session = sessionFactory.getCurrentSession();
1✔
2005
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2006
                CriteriaQuery<ConceptReferenceTermMap> cq = cb.createQuery(ConceptReferenceTermMap.class);
1✔
2007
                Root<ConceptReferenceTermMap> root = cq.from(ConceptReferenceTermMap.class);
1✔
2008

2009
                cq.where(cb.equal(root.get("termB"), term));
1✔
2010

2011
                return session.createQuery(cq).getResultList();
1✔
2012
        }
2013

2014
        /**
2015
         * @see org.openmrs.api.db.ConceptDAO#isConceptReferenceTermInUse(org.openmrs.ConceptReferenceTerm)
2016
         */
2017
        @Override
2018
        public boolean isConceptReferenceTermInUse(ConceptReferenceTerm term) throws DAOException {
2019
                Session session = sessionFactory.getCurrentSession();
1✔
2020
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2021

2022
                // Check in ConceptMap table
2023
                CriteriaQuery<Long> conceptMapQuery = cb.createQuery(Long.class);
1✔
2024
                Root<ConceptMap> conceptMapRoot = conceptMapQuery.from(ConceptMap.class);
1✔
2025
                conceptMapQuery.select(cb.count(conceptMapRoot));
1✔
2026
                conceptMapQuery.where(cb.equal(conceptMapRoot.get(CONCEPT_REFERENCE_TERM), term));
1✔
2027

2028
                Long conceptMapCount = session.createQuery(conceptMapQuery).uniqueResult();
1✔
2029
                if (conceptMapCount > 0) {
1✔
2030
                        return true;
1✔
2031
                }
2032

2033
                // Check in ConceptReferenceTermMap table
2034
                CriteriaQuery<Long> conceptReferenceTermMapQuery = cb.createQuery(Long.class);
1✔
2035
                Root<ConceptReferenceTermMap> conceptReferenceTermMapRoot =
1✔
2036
                        conceptReferenceTermMapQuery.from(ConceptReferenceTermMap.class);
1✔
2037
                conceptReferenceTermMapQuery.select(cb.count(conceptReferenceTermMapRoot));
1✔
2038
                conceptReferenceTermMapQuery.where(cb.equal(conceptReferenceTermMapRoot.get("termB"), term));
1✔
2039

2040
                Long conceptReferenceTermMapCount = session.createQuery(conceptReferenceTermMapQuery).uniqueResult();
1✔
2041
                return conceptReferenceTermMapCount > 0;
1✔
2042
        }
2043

2044
        /**
2045
         * @see org.openmrs.api.db.ConceptDAO#isConceptMapTypeInUse(org.openmrs.ConceptMapType)
2046
         */
2047
        @Override
2048
        public boolean isConceptMapTypeInUse(ConceptMapType mapType) throws DAOException {
2049
                Session session = sessionFactory.getCurrentSession();
1✔
2050
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2051

2052
                // Check in ConceptMap table
2053
                CriteriaQuery<Long> conceptQuery = cb.createQuery(Long.class);
1✔
2054
                Root<ConceptMap> conceptRoot = conceptQuery.from(ConceptMap.class);
1✔
2055
                conceptQuery.select(cb.count(conceptRoot));
1✔
2056
                conceptQuery.where(cb.equal(conceptRoot.get("conceptMapType"), mapType));
1✔
2057

2058
                Long conceptCount = session.createQuery(conceptQuery).uniqueResult();
1✔
2059
                if (conceptCount > 0) {
1✔
2060
                        return true;
1✔
2061
                }
2062

2063
                // Check in ConceptReferenceTermMap table
2064
                CriteriaQuery<Long> conceptReferenceTermMapQuery = cb.createQuery(Long.class);
1✔
2065
                Root<ConceptReferenceTermMap> conceptReferenceTermMapRoot = conceptReferenceTermMapQuery.from(ConceptReferenceTermMap.class);
1✔
2066
                conceptReferenceTermMapQuery.select(cb.count(conceptReferenceTermMapRoot));
1✔
2067
                conceptReferenceTermMapQuery.where(cb.equal(conceptReferenceTermMapRoot.get("conceptMapType"), mapType));
1✔
2068

2069
                Long conceptReferenceTermMapCount = session.createQuery(conceptReferenceTermMapQuery).uniqueResult();
1✔
2070
                return conceptReferenceTermMapCount > 0;
1✔
2071
        }
2072
        
2073
        /**
2074
         * @see org.openmrs.api.db.ConceptDAO#getConceptsByName(java.lang.String, java.util.Locale,
2075
         *      java.lang.Boolean)
2076
         */
2077
        @Override
2078
        public List<Concept> getConceptsByName(final String name, final Locale locale, final Boolean exactLocale) {
2079
                
2080
                List<Locale> locales = new ArrayList<>();
1✔
2081
                if (locale == null) {
1✔
2082
                        locales.add(Context.getLocale());
×
2083
                } else {
2084
                        locales.add(locale);
1✔
2085
                }
2086
                
2087
                boolean searchExactLocale = (exactLocale == null) ? false : exactLocale;
1✔
2088

2089
                return SearchQueryUnique.search(searchSessionFactory,
1✔
2090
                    SearchQueryUnique.newQuery(ConceptName.class,
1✔
2091
                        f -> newConceptNamePredicate(f, name, true, locales, searchExactLocale, false, null, null, null, null, null),
1✔
2092
                        "concept.conceptId", this::multiLoadConcepts));
2093
        }
2094
        
2095
        /**
2096
         * @see org.openmrs.api.db.ConceptDAO#getConceptByName(java.lang.String)
2097
         */
2098
        @Override
2099
        public Concept getConceptByName(final String name) {
2100
                Session session = sessionFactory.getCurrentSession();
1✔
2101
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2102
                CriteriaQuery<ConceptName> cq = cb.createQuery(ConceptName.class);
1✔
2103
                Root<ConceptName> root = cq.from(ConceptName.class);
1✔
2104
                Join<ConceptName, Concept> conceptJoin = root.join("concept");
1✔
2105

2106
                Locale locale = Context.getLocale();
1✔
2107
                Locale language = new Locale(locale.getLanguage() + "%");
1✔
2108
                List<Predicate> predicates = new ArrayList<>();
1✔
2109

2110
                predicates.add(cb.or(cb.equal(root.get("locale"), locale), cb.like(root.get("locale").as(String.class), language.toString())));
1✔
2111
                if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) {
1✔
2112
                        predicates.add(cb.like(cb.lower(root.get("name")), name.toLowerCase()));
1✔
2113
                } else {
2114
                        predicates.add(cb.equal(root.get("name"), name));
×
2115
                }
2116
                predicates.add(cb.isFalse(root.get("voided")));
1✔
2117
                predicates.add(cb.isFalse(conceptJoin.get("retired")));
1✔
2118

2119
                cq.where(predicates.toArray(new Predicate[0]));
1✔
2120

2121
                List<ConceptName> list = session.createQuery(cq).getResultList();
1✔
2122
                LinkedHashSet<Concept> concepts = transformNamesToConcepts(list);
1✔
2123

2124
                if (concepts.size() == 1) {
1✔
2125
                        return concepts.iterator().next();
1✔
2126
                } else if (list.isEmpty()) {
×
2127
                        log.warn("No concept found for '" + name + "'");
×
2128
                } else {
2129
                        log.warn("Multiple concepts found for '" + name + "'");
×
2130

2131
                        for (Concept concept : concepts) {
×
2132
                                for (ConceptName conceptName : concept.getNames(locale)) {
×
2133
                                        if (conceptName.getName().equalsIgnoreCase(name)) {
×
2134
                                                return concept;
×
2135
                                        }
2136
                                }
×
2137
                                for (ConceptName indexTerm : concept.getIndexTermsForLocale(locale)) {
×
2138
                                        if (indexTerm.getName().equalsIgnoreCase(name)) {
×
2139
                                                return concept;
×
2140
                                        }
2141
                                }
×
2142
                        }
×
2143
                }
2144

2145
                return null;
×
2146
        }
2147
        
2148
        /**
2149
         * @see org.openmrs.api.db.ConceptDAO#getDefaultConceptMapType()
2150
         */
2151
        @Override
2152
        public ConceptMapType getDefaultConceptMapType() throws DAOException {
2153
                FlushMode previousFlushMode = sessionFactory.getCurrentSession().getHibernateFlushMode();
1✔
2154
                sessionFactory.getCurrentSession().setHibernateFlushMode(FlushMode.MANUAL);
1✔
2155
                try {
2156
                        //Defaults to same-as if the gp is not set.
2157
                        String defaultConceptMapType = Context.getAdministrationService().getGlobalProperty(
1✔
2158
                            OpenmrsConstants.GP_DEFAULT_CONCEPT_MAP_TYPE);
2159
                        if (defaultConceptMapType == null) {
1✔
2160
                                throw new DAOException("The default concept map type is not set. You need to set the '"
×
2161
                                        + OpenmrsConstants.GP_DEFAULT_CONCEPT_MAP_TYPE + "' global property.");
2162
                        }
2163
                        
2164
                        ConceptMapType conceptMapType = getConceptMapTypeByName(defaultConceptMapType);
1✔
2165
                        if (conceptMapType == null) {
1✔
2166
                                throw new DAOException("The default concept map type (name: " + defaultConceptMapType
×
2167
                                        + ") does not exist! You need to set the '" + OpenmrsConstants.GP_DEFAULT_CONCEPT_MAP_TYPE
2168
                                        + "' global property.");
2169
                        }
2170
                        return conceptMapType;
1✔
2171
                }
2172
                finally {
2173
                        sessionFactory.getCurrentSession().setHibernateFlushMode(previousFlushMode);
1✔
2174
                }
2175
        }
2176

2177
        /**
2178
         * @see org.openmrs.api.db.ConceptDAO#isConceptNameDuplicate(org.openmrs.ConceptName)
2179
         */
2180
        @Override
2181
        public boolean isConceptNameDuplicate(ConceptName name) {
2182
                if (name.getVoided()) {
1✔
2183
                        return false;
×
2184
                }
2185
                if (name.getConcept() != null) {
1✔
2186
                        if (name.getConcept().getRetired()) {
1✔
2187
                                return false;
×
2188
                        }
2189

2190
                        //If it is not a default name of a concept, it cannot be a duplicate.
2191
                        //Note that a concept may not have a default name for the given locale, if just a short name or
2192
                        //a search term is set.
2193
                        if (!name.equals(name.getConcept().getName(name.getLocale()))) {
1✔
2194
                                return false;
1✔
2195
                        }
2196
                }
2197

2198
                Session session = sessionFactory.getCurrentSession();
1✔
2199
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2200
                CriteriaQuery<ConceptName> cq = cb.createQuery(ConceptName.class);
1✔
2201
                Root<ConceptName> root = cq.from(ConceptName.class);
1✔
2202

2203
                List<Predicate> predicates = new ArrayList<>();
1✔
2204

2205
                predicates.add(cb.isFalse(root.get("voided")));
1✔
2206
                predicates.add(cb.or(cb.equal(root.get("locale"), name.getLocale()),
1✔
2207
                        cb.equal(root.get("locale"), new Locale(name.getLocale().getLanguage()))));
1✔
2208

2209
                if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) {
1✔
2210
                        predicates.add(cb.equal(cb.lower(root.get("name")), name.getName().toLowerCase()));
1✔
2211
                } else {
2212
                        predicates.add(cb.equal(root.get("name"), name.getName()));
×
2213
                }
2214

2215
                cq.where(predicates.toArray(new Predicate[0]));
1✔
2216

2217
                List<ConceptName> candidateNames = session.createQuery(cq).getResultList();
1✔
2218

2219
                for (ConceptName candidateName : candidateNames) {
1✔
2220
                        if (candidateName.getConcept().getRetired()) {
1✔
2221
                                continue;
1✔
2222
                        }
2223
                        if (candidateName.getConcept().equals(name.getConcept())) {
1✔
2224
                                continue;
1✔
2225
                        }
2226
                        // If it is a default name for a concept
2227
                        if (candidateName.getConcept().getName(candidateName.getLocale()).equals(candidateName)) {
1✔
2228
                                return true;
1✔
2229
                        }
2230
                }
1✔
2231

2232
                return false;
1✔
2233
        }
2234
        
2235
        /**
2236
         * @see ConceptDAO#getDrugs(String, java.util.Locale, boolean, boolean)
2237
         */
2238
        @Override
2239
        public List<Drug> getDrugs(String searchPhrase, Locale locale, boolean exactLocale, boolean includeRetired) {
2240
                SearchQuery<Drug> drugQuery = newDrugQuery(searchPhrase, true, true, locale, exactLocale, null, includeRetired);
1✔
2241
                
2242
                return drugQuery.fetchAllHits();
1✔
2243
        }
2244

2245
        /**
2246
         * @see org.openmrs.api.db.ConceptDAO#getDrugsByMapping(String, ConceptSource, Collection, boolean)
2247
         */
2248
        @Override
2249
        public List<Drug> getDrugsByMapping(String code, ConceptSource conceptSource,
2250
                                                                                Collection<ConceptMapType> withAnyOfTheseTypes, boolean includeRetired) throws DAOException {
2251

2252
                Session session = sessionFactory.getCurrentSession();
1✔
2253
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2254
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
2255
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
2256

2257
                Join<Drug, DrugReferenceMap> drugReferenceMapJoin = drugRoot.join("drugReferenceMaps");
1✔
2258
                Join<DrugReferenceMap, ConceptReferenceTerm> termJoin = drugReferenceMapJoin.join(CONCEPT_REFERENCE_TERM);
1✔
2259
                List<Predicate> basePredicates = createSearchDrugByMappingPredicates(cb, drugRoot, drugReferenceMapJoin, termJoin,
1✔
2260
                    code, conceptSource, includeRetired);
2261

2262
                if (!withAnyOfTheseTypes.isEmpty()) {
1✔
2263
                        // Create a predicate to check if the ConceptMapType is in the provided collection
2264
                        Predicate mapTypePredicate = drugReferenceMapJoin.get("conceptMapType").in(withAnyOfTheseTypes);
1✔
2265
                        basePredicates.add(mapTypePredicate);
1✔
2266
                }
2267

2268
                cq.where(basePredicates.toArray(new Predicate[]{}));
1✔
2269

2270
                return session.createQuery(cq).getResultList().stream().distinct().collect(toList());
1✔
2271
        }
2272

2273
        /**
2274
         * @see org.openmrs.api.db.ConceptDAO#getDrugs
2275
         */
2276
        @Override
2277
        public Drug getDrugByMapping(String code, ConceptSource conceptSource,
2278
                                                                 Collection<ConceptMapType> withAnyOfTheseTypesOrOrderOfPreference) throws DAOException {
2279
                Session session = sessionFactory.getCurrentSession();
1✔
2280
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2281
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
2282
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
2283

2284
                Join<Drug, DrugReferenceMap> drugReferenceMapJoin = drugRoot.join("drugReferenceMaps");
1✔
2285
                Join<DrugReferenceMap, ConceptReferenceTerm> termJoin = drugReferenceMapJoin.join("conceptReferenceTerm");
1✔
2286

2287
                List<Predicate> basePredicates = createSearchDrugByMappingPredicates(cb, drugRoot, drugReferenceMapJoin, termJoin, code, conceptSource, true);
1✔
2288

2289
                if (!withAnyOfTheseTypesOrOrderOfPreference.isEmpty()) {
1✔
2290
                        for (ConceptMapType conceptMapType : withAnyOfTheseTypesOrOrderOfPreference) {
1✔
2291
                                List<Predicate> predicates = new ArrayList<>(basePredicates);
1✔
2292
                                predicates.add(cb.equal(drugReferenceMapJoin.get("conceptMapType"), conceptMapType));
1✔
2293
                                cq.where(predicates.toArray(new Predicate[]{}));
1✔
2294

2295
                                TypedQuery<Drug> query = session.createQuery(cq);
1✔
2296
                                List<Drug> drugs = query.getResultList();
1✔
2297
                                if (drugs.size() > 1) {
1✔
2298
                                        throw new DAOException("There are multiple matches for the highest-priority ConceptMapType");
1✔
2299
                                } else if (drugs.size() == 1) {
1✔
2300
                                        return drugs.get(0);
1✔
2301
                                }
2302
                        }
1✔
2303
                } else {
2304
                        cq.where(basePredicates.toArray(new Predicate[]{}));
1✔
2305

2306
                        TypedQuery<Drug> query = session.createQuery(cq);
1✔
2307
                        List<Drug> drugs = query.getResultList();
1✔
2308
                        if (drugs.size() > 1) {
1✔
2309
                                throw new DAOException("There are multiple matches for the highest-priority ConceptMapType");
×
2310
                        } else if (drugs.size() == 1) {
1✔
2311
                                return drugs.get(0);
1✔
2312
                        }
2313
                }
2314
                return null;
1✔
2315
        }
2316

2317

2318
        /**
2319
         * @see ConceptDAO#getAllConceptAttributeTypes()
2320
         */
2321
        @Override
2322
        public List<ConceptAttributeType> getAllConceptAttributeTypes() {
2323
                Session session = sessionFactory.getCurrentSession();
1✔
2324
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2325
                CriteriaQuery<ConceptAttributeType> cq = cb.createQuery(ConceptAttributeType.class);
1✔
2326
                cq.from(ConceptAttributeType.class);
1✔
2327

2328
                return session.createQuery(cq).getResultList();
1✔
2329
        }
2330

2331
        /**
2332
         * @see ConceptDAO#saveConceptAttributeType(ConceptAttributeType)
2333
         */
2334
        @Override
2335
        public ConceptAttributeType saveConceptAttributeType(ConceptAttributeType conceptAttributeType) {
2336
                sessionFactory.getCurrentSession().saveOrUpdate(conceptAttributeType);
1✔
2337
                return conceptAttributeType;
1✔
2338
        }
2339

2340
        /**
2341
         * @see ConceptDAO#getConceptAttributeType(Integer)
2342
         */
2343
        @Override
2344
        public ConceptAttributeType getConceptAttributeType(Integer id) {
2345
                return sessionFactory.getCurrentSession().get(ConceptAttributeType.class, id);
1✔
2346
        }
2347

2348
        /**
2349
         * @see ConceptDAO#getConceptAttributeTypeByUuid(String)
2350
         */
2351
        @Override
2352
        public ConceptAttributeType getConceptAttributeTypeByUuid(String uuid) {
2353
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttributeType.class, uuid);
1✔
2354
        }
2355

2356
        /**
2357
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptAttributeType(org.openmrs.ConceptAttributeType)
2358
         */
2359
        @Override
2360
        public void deleteConceptAttributeType(ConceptAttributeType conceptAttributeType) {
2361
                sessionFactory.getCurrentSession().delete(conceptAttributeType);
1✔
2362
        }
1✔
2363

2364
        /**
2365
         * @see org.openmrs.api.db.ConceptDAO#getConceptAttributeTypes(String)
2366
         */
2367
        @Override
2368
        public List<ConceptAttributeType> getConceptAttributeTypes(String name) {
2369
                Session session = sessionFactory.getCurrentSession();
1✔
2370
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2371
                CriteriaQuery<ConceptAttributeType> cq = cb.createQuery(ConceptAttributeType.class);
1✔
2372
                Root<ConceptAttributeType> root = cq.from(ConceptAttributeType.class);
1✔
2373

2374
                //match name anywhere and case insensitive
2375
                if (name != null) {
1✔
2376
                        cq.where(cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(name)));
1✔
2377
                }
2378

2379
                return session.createQuery(cq).getResultList();
1✔
2380
        }
2381

2382
        /**
2383
         * @see org.openmrs.api.db.ConceptDAO#getConceptAttributeTypeByName(String)
2384
         */
2385
        @Override
2386
        public ConceptAttributeType getConceptAttributeTypeByName(String exactName) {
2387
                Session session = sessionFactory.getCurrentSession();
1✔
2388
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2389
                CriteriaQuery<ConceptAttributeType> cq = cb.createQuery(ConceptAttributeType.class);
1✔
2390
                Root<ConceptAttributeType> root = cq.from(ConceptAttributeType.class);
1✔
2391

2392
                cq.where(cb.equal(root.get("name"), exactName));
1✔
2393

2394
                return session.createQuery(cq).uniqueResult();
1✔
2395
        }
2396

2397
        /**
2398
         * @see ConceptDAO#getConceptAttributeByUuid(String)
2399
         */
2400
        @Override
2401
        public ConceptAttribute getConceptAttributeByUuid(String uuid) {
2402
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttribute.class, uuid);
1✔
2403
        }
2404

2405
        /**
2406
         * @see ConceptDAO#getConceptAttributeCount(ConceptAttributeType)
2407
         */
2408
        @Override
2409
        public long getConceptAttributeCount(ConceptAttributeType conceptAttributeType) {
2410
                Session session = sessionFactory.getCurrentSession();
1✔
2411
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2412
                CriteriaQuery<Long> cq = cb.createQuery(Long.class);
1✔
2413
                Root<ConceptAttribute> root = cq.from(ConceptAttribute.class);
1✔
2414

2415
                cq.select(cb.count(root)).where(cb.equal(root.get("attributeType"), conceptAttributeType));
1✔
2416

2417
                return session.createQuery(cq).getSingleResult();
1✔
2418
        }
2419

2420
        @Override
2421
        public List<Concept> getConceptsByClass(ConceptClass conceptClass) {
2422
                Session session = sessionFactory.getCurrentSession();
1✔
2423
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2424
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
2425
                Root<Concept> root = cq.from(Concept.class);
1✔
2426

2427
                cq.where(cb.equal(root.get("conceptClass"), conceptClass));
1✔
2428

2429
                return session.createQuery(cq).getResultList();
1✔
2430
        }
2431

2432
        private List<Predicate> createSearchDrugByMappingPredicates(CriteriaBuilder cb, Root<Drug> drugRoot, Join<Drug, DrugReferenceMap> drugReferenceMapJoin,
2433
                                                                                                                                Join<DrugReferenceMap, ConceptReferenceTerm> termJoin,
2434
                                                                                                                                String code, ConceptSource conceptSource, boolean includeRetired) {
2435
                List<Predicate> predicates = new ArrayList<>();
1✔
2436
                
2437
                if (code != null) {
1✔
2438
                        predicates.add(cb.equal(termJoin.get("code"), code));
1✔
2439
                }
2440
                if (conceptSource != null) {
1✔
2441
                        predicates.add(cb.equal(termJoin.get(CONCEPT_SOURCE), conceptSource));
1✔
2442
                }
2443
                if (!includeRetired) {
1✔
2444
                        predicates.add(cb.isFalse(drugRoot.get("retired")));
1✔
2445
                }
2446

2447
                return predicates;
1✔
2448
        }
2449

2450
        private List<Predicate> createSearchConceptMapCriteria(CriteriaBuilder cb, Root<ConceptMap> root, String code, String sourceName, boolean includeRetired) {
2451
                List<Predicate> predicates = new ArrayList<>();
1✔
2452

2453
                Join<ConceptMap, ConceptReferenceTerm> termJoin = root.join(CONCEPT_REFERENCE_TERM);
1✔
2454

2455
                // Match the source code to the passed code
2456
                if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) {
1✔
2457
                        predicates.add(cb.equal(cb.lower(termJoin.get("code")), code.toLowerCase()));
1✔
2458
                } else {
2459
                        predicates.add(cb.equal(termJoin.get("code"), code));
×
2460
                }
2461

2462
                // Join to concept reference source and match to the hl7Code or source name
2463
                Join<ConceptReferenceTerm, ConceptSource> sourceJoin = termJoin.join(CONCEPT_SOURCE);
1✔
2464

2465
                Predicate namePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()
1✔
2466
                        ? cb.equal(cb.lower(sourceJoin.get("name")), sourceName.toLowerCase())
1✔
2467
                        : cb.equal(sourceJoin.get("name"), sourceName);
1✔
2468
                Predicate hl7CodePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()
1✔
2469
                        ? cb.equal(cb.lower(sourceJoin.get(HL7_CODE)), sourceName.toLowerCase())
1✔
2470
                        : cb.equal(sourceJoin.get(HL7_CODE), sourceName);
1✔
2471

2472
                predicates.add(cb.or(namePredicate, hl7CodePredicate));
1✔
2473

2474
                // Join to concept and filter retired ones if necessary
2475
                Join<ConceptMap, Concept> conceptJoin = root.join("concept");
1✔
2476
                if (!includeRetired) {
1✔
2477
                        predicates.add(cb.isFalse(conceptJoin.get("retired")));
1✔
2478
                }
2479
                return predicates;
1✔
2480
        }
2481

2482
        /**
2483
         * @see org.openmrs.api.db.ConceptDAO#saveConceptReferenceRange(ConceptReferenceRange)
2484
         */
2485
        @Override
2486
        public ConceptReferenceRange saveConceptReferenceRange(ConceptReferenceRange conceptReferenceRange) {
2487
                sessionFactory.getCurrentSession().saveOrUpdate(conceptReferenceRange);
1✔
2488
                return conceptReferenceRange;
1✔
2489
        }
2490

2491
        /**
2492
         * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceRangesByConceptId(Integer)
2493
         */
2494
        @Override
2495
        public List<ConceptReferenceRange> getConceptReferenceRangesByConceptId(Integer conceptId) {
2496
                Session session = sessionFactory.getCurrentSession();
1✔
2497
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2498
                CriteriaQuery<ConceptReferenceRange> cq = cb.createQuery(ConceptReferenceRange.class);
1✔
2499
                Root<ConceptReferenceRange> root = cq.from(ConceptReferenceRange.class);
1✔
2500

2501
                cq.where(cb.equal(root.get("conceptNumeric"), conceptId));
1✔
2502

2503
                return session.createQuery(cq).getResultList();
1✔
2504
        }
2505

2506
        /**
2507
         * @see ConceptDAO#getConceptReferenceRangeByUuid(String)
2508
         */
2509
        @Override
2510
        public ConceptReferenceRange getConceptReferenceRangeByUuid(String uuid) {
2511
                Session session = sessionFactory.getCurrentSession();
1✔
2512
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2513
                CriteriaQuery<ConceptReferenceRange> cq = cb.createQuery(ConceptReferenceRange.class);
1✔
2514
                Root<ConceptReferenceRange> root = cq.from(ConceptReferenceRange.class);
1✔
2515

2516
                cq.where(cb.equal(root.get("uuid"), uuid));
1✔
2517

2518
                return session.createQuery(cq).uniqueResult();
1✔
2519
        }
2520
        
2521
        /**
2522
         * @see org.openmrs.api.db.ConceptDAO#purgeConceptReferenceRange(org.openmrs.ConceptReferenceRange)
2523
         */
2524
        @Override
2525
        public void purgeConceptReferenceRange(ConceptReferenceRange conceptReferenceRange) {
2526
                sessionFactory.getCurrentSession().delete(conceptReferenceRange);
1✔
2527
        }
1✔
2528

2529
        /**
2530
         * Loads the concepts referenced by a page of concept-name search hits with a single
2531
         * {@code multiLoad} instead of one {@code get} per hit, so a page issues a bounded number of
2532
         * entity loads and Hibernate's collection batch fetching (see the {@code batch-size} on the concept
2533
         * collections in Concept.hbm.xml) can span the whole page. The concepts are returned keyed by id so
2534
         * callers can pair each hit with its concept and preserve hit order themselves; concepts no longer
2535
         * present in the database (a stale search-index entry) are absent from the map.
2536
         *
2537
         * @param names the page of concept-name hits
2538
         * @return the concepts referenced by the hits, keyed by concept id
2539
         */
2540
        private Map<Integer, Concept> multiLoadConceptsByHit(List<ConceptName> names) {
2541
                List<Integer> conceptIds = names.stream().map(n -> n.getConcept().getConceptId()).collect(Collectors.toList());
1✔
2542
                List<Concept> concepts = sessionFactory.getCurrentSession().byMultipleIds(Concept.class).multiLoad(conceptIds);
1✔
2543
                Map<Integer, Concept> conceptsById = new HashMap<>();
1✔
2544
                for (Concept concept : concepts) {
1✔
2545
                        if (concept != null) {
1✔
2546
                                conceptsById.put(concept.getConceptId(), concept);
1✔
2547
                        }
2548
                }
1✔
2549
                if (conceptsById.size() < conceptIds.size()) {
1✔
NEW
2550
                        log.debug("Dropped {} concept search hit(s) with no matching row (stale search index?)",
×
NEW
2551
                            conceptIds.size() - conceptsById.size());
×
2552
                }
2553
                return conceptsById;
1✔
2554
        }
2555

2556
        /**
2557
         * Order-preserving list form of {@link #multiLoadConceptsByHit(List)} for searches that return the
2558
         * concepts directly rather than pairing them into a {@link ConceptSearchResult}. Hits whose concept
2559
         * is no longer present in the database - a stale index entry - are dropped from the results.
2560
         *
2561
         * @param names the page of concept-name hits, in the order they should appear in the results
2562
         * @return the concepts for the page, in hit order, with missing rows removed
2563
         */
2564
        private List<Concept> multiLoadConcepts(List<ConceptName> names) {
2565
                Map<Integer, Concept> conceptsById = multiLoadConceptsByHit(names);
1✔
2566
                return names.stream().map(n -> conceptsById.get(n.getConcept().getConceptId())).filter(Objects::nonNull)
1✔
2567
                        .collect(Collectors.toList());
1✔
2568
        }
2569
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc