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

openmrs / openmrs-core / 29955961944

22 Jul 2026 08:38PM UTC coverage: 64.136% (+0.04%) from 64.101%
29955961944

push

github

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

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

7 existing lines in 5 files now uncovered.

24316 of 37913 relevant lines covered (64.14%)

0.64 hits per line

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

88.35
/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.JoinType;
20
import javax.persistence.criteria.Predicate;
21
import javax.persistence.criteria.Root;
22
import java.util.ArrayList;
23
import java.util.Arrays;
24
import java.util.Collection;
25
import java.util.Collections;
26
import java.util.HashMap;
27
import java.util.HashSet;
28
import java.util.Iterator;
29
import java.util.LinkedHashSet;
30
import java.util.List;
31
import java.util.Locale;
32
import java.util.Map;
33
import java.util.Objects;
34
import java.util.Optional;
35
import java.util.Set;
36
import java.util.stream.Collectors;
37

38
import javax.persistence.criteria.Subquery;
39

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

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

100
        private static final String CONCEPT_REFERENCE_TERM = "conceptReferenceTerm";
101

102
        private static final String CONCEPT_SOURCE = "conceptSource";
103

104
        private static final String HL7_CODE = "hl7Code";
105

106
        private SessionFactory sessionFactory;
107

108
        private SearchSessionFactory searchSessionFactory;
109

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

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

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

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

148
                        cq.where(cb.equal(root.get("conceptId"), conceptId));
×
149

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

154
                return cc;
1✔
155
        }
156
        
157
        /**
158
         * @see org.openmrs.api.db.ConceptDAO#saveConcept(org.openmrs.Concept)
159
         */
160
        @Override
161
        public Concept saveConcept(Concept concept) throws DAOException {
162
                if ((concept.getConceptId() != null) && (concept.getConceptId() > 0)) {
1✔
163
                        // this method checks the concept_numeric, concept_derived, etc tables
164
                        // to see if a row exists there or not.  This is needed because hibernate
165
                        // doesn't like to insert into concept_numeric but update concept in the
166
                        // same go.  It assumes that its either in both tables or no tables
167
                        insertRowIntoSubclassIfNecessary(concept);
1✔
168
                }
169
                
170
                sessionFactory.getCurrentSession().saveOrUpdate(concept);
1✔
171
                return concept;
1✔
172
        }
173
        
174
        /**
175
         * Convenience method that will check this concept for subtype values (ConceptNumeric,
176
         * ConceptDerived, etc) and insert a line into that subtable if needed. This prevents a
177
         * hibernate ConstraintViolationException
178
         * 
179
         * @param concept the concept that will be inserted
180
         */
181
        private void insertRowIntoSubclassIfNecessary(Concept concept) {
182
                
183
                // check the concept_numeric table
184
                if (concept instanceof ConceptNumeric) {
1✔
185
                        
186
                        String select = "SELECT 1 from concept_numeric WHERE concept_id = :conceptId";
1✔
187
                        Query query = sessionFactory.getCurrentSession().createSQLQuery(select);
1✔
188
                        query.setParameter("conceptId", concept.getConceptId());
1✔
189
                        
190
                        // Converting to concept numeric:  A single concept row exists, but concept numeric has not been populated yet.
191
                        if (JpaUtils.getSingleResultOrNull(query) == null) {
1✔
192
                                // we have to evict the current concept out of the session because
193
                                // the user probably had to change the class of this object to get it
194
                                // to now be a numeric
195
                                // (must be done before the "insert into...")
196
                                sessionFactory.getCurrentSession().clear();
1✔
197
                                
198
                                //Just in case this was changed from concept_complex to numeric
199
                                //We need to add a delete line for each concept sub class that is not concept_numeric
200
                                deleteSubclassConcept("concept_complex", concept.getConceptId());
1✔
201
                                
202
                                String insert = "INSERT INTO concept_numeric (concept_id, allow_decimal) VALUES (:conceptId, false)";
1✔
203
                                query = sessionFactory.getCurrentSession().createSQLQuery(insert);
1✔
204
                                query.setParameter("conceptId", concept.getConceptId());
1✔
205
                                query.executeUpdate();
1✔
206
                                
207
                        } else {
1✔
208
                                // Converting from concept numeric:  The concept and concept numeric rows both exist, so we need to delete concept_numeric.
209
                                
210
                                // concept is changed from numeric to something else
211
                                // hence row should be deleted from the concept_numeric
212
                                if (!concept.isNumeric()) {
1✔
213
                                        deleteSubclassConcept("concept_numeric", concept.getConceptId());
1✔
214
                                }
215
                        }
216
                }
1✔
217
                // check the concept complex table
218
                else if (concept instanceof ConceptComplex) {
1✔
219
                        
220
                        String select = "SELECT 1 FROM concept_complex WHERE concept_id = :conceptId";
1✔
221
                        Query query = sessionFactory.getCurrentSession().createSQLQuery(select);
1✔
222
                        query.setParameter("conceptId", concept.getConceptId());
1✔
223
                        
224
                        // Converting to concept complex:  A single concept row exists, but concept complex has not been populated yet.
225
                        if (JpaUtils.getSingleResultOrNull(query) == null) {
1✔
226
                                // we have to evict the current concept out of the session because
227
                                // the user probably had to change the class of this object to get it
228
                                // to now be a ConceptComplex
229
                                // (must be done before the "insert into...")
230
                                sessionFactory.getCurrentSession().clear();
1✔
231
                                
232
                                //Just in case this was changed from concept_numeric to complex
233
                                //We need to add a delete line for each concept sub class that is not concept_complex
234
                                deleteSubclassConcept("concept_numeric", concept.getConceptId());
1✔
235
                                
236
                                // Add an empty row into the concept_complex table
237
                                String insert = "INSERT INTO concept_complex (concept_id) VALUES (:conceptId)";
1✔
238
                                query = sessionFactory.getCurrentSession().createSQLQuery(insert);
1✔
239
                                query.setParameter("conceptId", concept.getConceptId());
1✔
240
                                query.executeUpdate();
1✔
241
                                
242
                        } else {
1✔
243
                                // Converting from concept complex:  The concept and concept complex rows both exist, so we need to delete the concept_complex row.
244
                                // no stub insert is needed because either a concept row doesn't exist OR a concept_complex row does exist
245
                                
246
                                // concept is changed from complex to something else
247
                                // hence row should be deleted from the concept_complex
248
                                if (!concept.isComplex()) {
×
249
                                        deleteSubclassConcept("concept_complex", concept.getConceptId());
×
250
                                }
251
                        }
252
                }
253
        }
1✔
254
        
255
        /**
256
         * Deletes a concept from a sub class table
257
         * 
258
         * @param tableName the sub class table name
259
         * @param conceptId the concept id
260
         */
261
        private void deleteSubclassConcept(String tableName, Integer conceptId) {
262
                String delete = "DELETE FROM " + tableName + " WHERE concept_id = :conceptId";
1✔
263
                Query query = sessionFactory.getCurrentSession().createSQLQuery(delete);
1✔
264
                query.setParameter("conceptId", conceptId);
1✔
265
                query.executeUpdate();
1✔
266
        }
1✔
267
        
268
        /**
269
         * @see org.openmrs.api.db.ConceptDAO#purgeConcept(org.openmrs.Concept)
270
         */
271
        @Override
272
        public void purgeConcept(Concept concept) throws DAOException {
273
                sessionFactory.getCurrentSession().delete(concept);
1✔
274
        }
1✔
275
        
276
        /**
277
         * @see org.openmrs.api.db.ConceptDAO#getConcept(java.lang.Integer)
278
         */
279
        @Override
280
        public Concept getConcept(Integer conceptId) throws DAOException {
281
                return sessionFactory.getCurrentSession().get(Concept.class, conceptId);
1✔
282
        }
283
        
284
        /**
285
         * @see org.openmrs.api.db.ConceptDAO#getConceptName(java.lang.Integer)
286
         */
287
        @Override
288
        public ConceptName getConceptName(Integer conceptNameId) throws DAOException {
289
                return sessionFactory.getCurrentSession().get(ConceptName.class, conceptNameId);
1✔
290
        }
291
        
292
        /**
293
         * @see org.openmrs.api.db.ConceptDAO#getConceptAnswer(java.lang.Integer)
294
         */
295
        @Override
296
        public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws DAOException {
297
                return sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId);
1✔
298
        }
299
        
300
        /**
301
         * @see org.openmrs.api.db.ConceptDAO#getAllConcepts(java.lang.String, boolean, boolean)
302
         */
303
        @Override
304
        public List<Concept> getAllConcepts(String sortBy, boolean asc, boolean includeRetired) throws DAOException {
305

306
                boolean isNameField = false;
1✔
307

308
                try {
309
                        Concept.class.getDeclaredField(sortBy);
1✔
310
                }
311
                catch (NoSuchFieldException e) {
1✔
312
                        try {
313
                                ConceptName.class.getDeclaredField(sortBy);
1✔
314
                                isNameField = true;
1✔
315
                        }
316
                        catch (NoSuchFieldException e2) {
×
317
                                sortBy = "conceptId";
×
318
                        }
1✔
319
                }
1✔
320

321
                Session session = sessionFactory.getCurrentSession();
1✔
322
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
323
                CriteriaQuery<Concept> cq = cb.createQuery(Concept.class);
1✔
324
                Root<Concept> root = cq.from(Concept.class);
1✔
325

326
                List<Predicate> predicates = new ArrayList<>();
1✔
327

328
                // When sorting by a ConceptName field, join to names filtered on FULLY_SPECIFIED
329
                // to avoid duplicates (each concept should have exactly one fully specified name)
330
                Join<Concept, ConceptName> namesJoin = null;
1✔
331
                if (isNameField) {
1✔
332
                        namesJoin = root.join("names", JoinType.LEFT);
1✔
333
                        predicates.add(cb.equal(namesJoin.get("conceptNameType"), ConceptNameType.FULLY_SPECIFIED));
1✔
334
                }
335

336
                if (!includeRetired) {
1✔
337
                        predicates.add(cb.isFalse(root.get("retired")));
1✔
338
                }
339

340
                cq.select(root).where(predicates.toArray(new Predicate[0]));
1✔
341

342
                if (isNameField) {
1✔
343
                        cq.orderBy(asc ? cb.asc(namesJoin.get(sortBy)) : cb.desc(namesJoin.get(sortBy)));
1✔
344
                } else {
345
                        cq.orderBy(asc ? cb.asc(root.get(sortBy)) : cb.desc(root.get(sortBy)));
1✔
346
                }
347

348
                return session.createQuery(cq).getResultList();
1✔
349
        }
350
        
351
        /**
352
         * @see org.openmrs.api.db.ConceptDAO#saveDrug(org.openmrs.Drug)
353
         */
354
        @Override
355
        public Drug saveDrug(Drug drug) throws DAOException {
356
                sessionFactory.getCurrentSession().saveOrUpdate(drug);
1✔
357
                return drug;
1✔
358
        }
359
        
360
        /**
361
         * @see org.openmrs.api.db.ConceptDAO#getDrug(java.lang.Integer)
362
         */
363
        @Override
364
        public Drug getDrug(Integer drugId) throws DAOException {
365
                return sessionFactory.getCurrentSession().get(Drug.class, drugId);
1✔
366
        }
367
        
368
        /**
369
         * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String, org.openmrs.Concept, boolean)
370
         */
371
        @Override
372
        public List<Drug> getDrugs(String drugName, Concept concept, boolean includeRetired) throws DAOException {
373
                Session session = sessionFactory.getCurrentSession();
1✔
374
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
375
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
376
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
377

378
                List<Predicate> predicates = new ArrayList<>();
1✔
379

380
                if (!includeRetired) {
1✔
381
                        predicates.add(cb.isFalse(drugRoot.get("retired")));
1✔
382
                }
383

384
                if (concept != null) {
1✔
385
                        predicates.add(cb.equal(drugRoot.get("concept"), concept));
1✔
386
                }
387

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

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

398
                return session.createQuery(cq).getResultList();
1✔
399
        }
400

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

413
                Predicate rhs = cb.equal(drugRoot.get("concept"), ingredient);
1✔
414
                Predicate lhs = cb.equal(ingredientJoin.get("ingredient"), ingredient);
1✔
415

416
                cq.where(cb.or(lhs, rhs));
1✔
417

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

453
                if (name != null) {
1✔
454
                        cq.where(cb.equal(root.get("name"), name));
1✔
455
                }
456

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

470
                // Minor bug - was assigning includeRetired instead of evaluating
471
                if (!includeRetired) {
1✔
472
                        cq.where(cb.isFalse(root.get("retired")));
1✔
473
                }
474

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

518
                if (!includeRetired) {
1✔
519
                        cq.where(cb.isFalse(root.get("retired")));
1✔
520
                }
521

522
                return session.createQuery(cq).getResultList();
1✔
523
        }
524

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

535
                if (name != null) {
1✔
536
                        cq.where(cb.like(root.get("name"), MatchMode.START.toCaseSensitivePattern(name)));
1✔
537
                }
538

539
                return session.createQuery(cq).getResultList();
1✔
540
        }
541

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

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

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

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

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

729
                Integer i = c.getConceptId();
1✔
730

731
                cq.where(cb.lessThan(root.get("conceptId"), i));
1✔
732
                cq.orderBy(cb.desc(root.get("conceptId")));
1✔
733

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

736
                if (concepts.isEmpty()) {
1✔
737
                        return null;
1✔
738
                }
739

740
                return concepts.get(0);
1✔
741
        }
742

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

753
                Integer i = c.getConceptId();
1✔
754

755
                cq.where(cb.greaterThan(root.get("conceptId"), i));
1✔
756
                cq.orderBy(cb.asc(root.get("conceptId")));
1✔
757

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

760
                if (concepts.isEmpty()) {
1✔
761
                        return null;
1✔
762
                }
763

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

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

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

838
                Predicate stateCondition = cb.equal(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED);
1✔
839
                Predicate textCondition = cb.equal(root.get("originalText"), text);
1✔
840

841
                cq.where(cb.and(stateCondition, textCondition));
1✔
842

843
                return session.createQuery(cq).getResultList();
1✔
844
        }
845

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

856
                Predicate stateNotEqual = cb.notEqual(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED);
1✔
857
                Predicate originalTextEqual = cb.equal(root.get("originalText"), text);
1✔
858
                Predicate mappedConceptNotNull = cb.isNotNull(root.get("mappedConcept"));
1✔
859

860
                cq.select(root.get("mappedConcept")).distinct(true);
1✔
861
                cq.where(stateNotEqual, originalTextEqual, mappedConceptNotNull);
1✔
862

863
                return session.createQuery(cq).getResultList();
1✔
864
        }
865

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

876
                cq.where(cb.equal(root.get("conceptSet"), concept));
1✔
877
                cq.orderBy(cb.asc(root.get("sortWeight")));
1✔
878

879
                return session.createQuery(cq).getResultList();
1✔
880
        }
881

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

892
                cq.where(cb.equal(root.get("concept"), concept));
1✔
893

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

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

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

961
                List<ConceptNameTag> conceptNameTags = session.createQuery(cq).getResultList();
1✔
962
                if (conceptNameTags.isEmpty()) {
1✔
963
                        return null;
1✔
964
                }
965

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

996
                if (!includeRetired) {
1✔
997
                        cq.where(cb.isFalse(root.get("retired")));
1✔
998
                }
999

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

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

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

1119
                cq.where(predicates.toArray(new Predicate[]{}));
×
1120

1121
                cq.select(root.get("concept"));
×
1122

1123
                Join<ConceptMap, Concept> conceptJoin = root.join("concept");
×
1124
                if (includeRetired) {
×
1125
                        cq.orderBy(cb.asc(conceptJoin.get("retired")));
×
1126
                }
1127

1128
                return session.createQuery(cq).getResultList()
×
1129
                        .stream().distinct().collect(toList());
×
1130
        }
1131

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

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

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

1146
                cq.select(root.get("concept").get("conceptId"));
1✔
1147

1148
                Join<ConceptMap, Concept> conceptJoin = root.join("concept");
1✔
1149
                if (includeRetired) {
1✔
1150
                        cq.orderBy(cb.asc(conceptJoin.get("retired")));
1✔
1151
                }
1152

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

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

1178
                List<Predicate> orPredicates = new ArrayList<>();
1✔
1179

1180
                if (criteria.getUuids() != null && !criteria.getUuids().isEmpty()) {
1✔
1181
                        orPredicates.add(root.get("uuid").in(criteria.getUuids()));
1✔
1182
                }
1183

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

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

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

1204
                if (orPredicates.isEmpty()) {
1✔
1205
                        return new ArrayList<>();
1✔
1206
                }
1207

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

1214
                return session.createQuery(cq).getResultList().stream().distinct().collect(toList());
1✔
1215
        }
1216

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

1230
                Subquery<ConceptMap> sub = cq.subquery(ConceptMap.class);
1✔
1231
                Root<ConceptMap> mapRoot = sub.from(ConceptMap.class);
1✔
1232
                Join<ConceptMap, ConceptReferenceTerm> termJoin = mapRoot.join(CONCEPT_REFERENCE_TERM);
1✔
1233
                Join<ConceptReferenceTerm, ConceptSource> sourceJoin = termJoin.join(CONCEPT_SOURCE);
1✔
1234

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

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

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

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

1277
        @Override
1278
        public ConceptAnswer getConceptAnswerByUuid(String uuid) {
1279
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAnswer.class, uuid);
1✔
1280
        }
1281

1282
        @Override
1283
        public ConceptName getConceptNameByUuid(String uuid) {
1284
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptName.class, uuid);
1✔
1285
        }
1286

1287
        @Override
1288
        public ConceptSet getConceptSetByUuid(String uuid) {
1289
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSet.class, uuid);
1✔
1290
        }
1291

1292
        @Override
1293
        public ConceptSource getConceptSourceByUuid(String uuid) {
1294
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSource.class, uuid);
1✔
1295
        }
1296

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

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

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

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

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

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

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

1374
                Root<ConceptMap> root = cq.from(ConceptMap.class);
1✔
1375
                Join<ConceptMap, ConceptReferenceTerm> conceptReferenceTermJoin = root.join(CONCEPT_REFERENCE_TERM);
1✔
1376

1377
                cq.where(cb.equal(conceptReferenceTermJoin.get(CONCEPT_SOURCE), conceptSource));
1✔
1378

1379
                return session.createQuery(cq).getResultList();
1✔
1380
        }
1381

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

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

1394
                return session.createQuery(cq).uniqueResult();
1✔
1395
        }
1396

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

1406
                Session session = sessionFactory.getCurrentSession();
1✔
1407
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1408
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
1409
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
1410

1411
                cq.where(cb.equal(root.get("uniqueId"), uniqueId));
1✔
1412

1413
                return session.createQuery(cq).uniqueResult();
1✔
1414
        }
1415

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

1425
                Session session = sessionFactory.getCurrentSession();
1✔
1426
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1427
                CriteriaQuery<ConceptSource> cq = cb.createQuery(ConceptSource.class);
1✔
1428
                Root<ConceptSource> root = cq.from(ConceptSource.class);
1✔
1429

1430
                cq.where(cb.equal(root.get(HL7_CODE), hl7Code));
1✔
1431

1432
                return session.createQuery(cq).uniqueResult();
1✔
1433
        }
1434

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

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

1458
        /**
1459
         * @see org.openmrs.api.db.ConceptDAO#getConceptStopWords(java.util.Locale)
1460
         */
1461
        @Override
1462
        public List<String> getConceptStopWords(Locale locale) throws DAOException {
1463

1464
                locale = (locale == null ? Context.getLocale() : locale);
1✔
1465

1466
                Session session = sessionFactory.getCurrentSession();
1✔
1467
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
1468
                CriteriaQuery<String> cq = cb.createQuery(String.class);
1✔
1469
                Root<ConceptStopWord> root = cq.from(ConceptStopWord.class);
1✔
1470

1471
                cq.select(root.get("value"));
1✔
1472
                cq.where(cb.equal(root.get("locale"), locale));
1✔
1473

1474
                return session.createQuery(cq).getResultList();
1✔
1475
        }
1476

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

1488
                        cq.where(cb.and(
1✔
1489
                                cb.equal(root.get("value"), conceptStopWord.getValue()),
1✔
1490
                                cb.equal(root.get("locale"), conceptStopWord.getLocale())));
1✔
1491

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

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

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

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

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

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

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

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

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

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

1673
                                if (locales == null) {
1✔
1674
                                        searchLocales = Collections.singletonList(Context.getLocale());
1✔
1675
                                } else {
1676
                                        searchLocales = new HashSet<>(locales);
1✔
1677
                                }
1678

1679
                                b.must(newConceptNameQuery(f, phrase, searchKeywords, searchLocales, searchExactLocale));
1✔
1680
                        }
1681

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

1699
                        if (answersToConcept != null) {
1✔
1700
                                Collection<ConceptAnswer> answers = answersToConcept.getAnswers(false);
1✔
1701

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

1711
                        if (!includeRetired) {
1✔
1712
                                b.filter(f.match().field("concept.retired").matching(Boolean.FALSE));
1✔
1713
                        }
1714
                }).toPredicate();
1✔
1715
        }
1716

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

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

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

1737
                List<ConceptMapType> conceptMapTypes = session.createQuery(cq).getResultList();
1✔
1738
                conceptMapTypes.sort(new ConceptMapTypeComparator());
1✔
1739

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

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

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

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

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

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

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

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

1834
                cq.where(cb.equal(root.get(CONCEPT_SOURCE), conceptSource));
×
1835

1836
                return session.createQuery(cq).getResultList();
×
1837
        }
1838

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

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

1853
                cq.where(cb.and(namePredicate, sourcePredicate));
1✔
1854

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

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

1903
                List<Predicate> predicates = new ArrayList<>();
1✔
1904
                predicates.add(cb.equal(root.get("code"), code));
1✔
1905
                predicates.add(cb.equal(root.get(CONCEPT_SOURCE), conceptSource));
1✔
1906

1907
                if (!includeRetired) {
1✔
1908
                        predicates.add(cb.isFalse(root.get("retired")));
1✔
1909
                }
1910
                cq.where(predicates.toArray(new Predicate[]{}));
1✔
1911

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

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

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

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

1944
                return session.createQuery(cq).getSingleResult();
1✔
1945
        }
1946

1947

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

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

1963
                TypedQuery<ConceptReferenceTerm> typedQuery = session.createQuery(cq);
1✔
1964

1965
                if (start != null) {
1✔
1966
                        typedQuery.setFirstResult(start);
1✔
1967
                }
1968
                if (length != null && length > 0) {
1✔
1969
                        typedQuery.setMaxResults(length);
1✔
1970
                }
1971

1972
                return typedQuery.getResultList();
1✔
1973
        }
1974

1975
        private List<Predicate> createConceptReferenceTermPredicates(CriteriaBuilder cb, Root<ConceptReferenceTerm> root,
1976
                                                                                                                                 String query, ConceptSource conceptSource, boolean includeRetired) {
1977
                List<Predicate> predicates = new ArrayList<>();
1✔
1978

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

1989
                        predicates.add(cb.or(namePredicate, codePredicate));
1✔
1990
                }
1991

1992
                return predicates;
1✔
1993
        }
1994

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

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

2007
                return session.createQuery(cq).getResultList();
1✔
2008
        }
2009

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

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

2024
                Long conceptMapCount = session.createQuery(conceptMapQuery).uniqueResult();
1✔
2025
                if (conceptMapCount > 0) {
1✔
2026
                        return true;
1✔
2027
                }
2028

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

2036
                Long conceptReferenceTermMapCount = session.createQuery(conceptReferenceTermMapQuery).uniqueResult();
1✔
2037
                return conceptReferenceTermMapCount > 0;
1✔
2038
        }
2039

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

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

2054
                Long conceptCount = session.createQuery(conceptQuery).uniqueResult();
1✔
2055
                if (conceptCount > 0) {
1✔
2056
                        return true;
1✔
2057
                }
2058

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

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

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

2102
                Locale locale = Context.getLocale();
1✔
2103
                Locale language = new Locale(locale.getLanguage() + "%");
1✔
2104
                List<Predicate> predicates = new ArrayList<>();
1✔
2105

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

2115
                cq.where(predicates.toArray(new Predicate[0]));
1✔
2116

2117
                List<ConceptName> list = session.createQuery(cq).getResultList();
1✔
2118
                LinkedHashSet<Concept> concepts = transformNamesToConcepts(list);
1✔
2119

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

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

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

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

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

2194
                Session session = sessionFactory.getCurrentSession();
1✔
2195
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2196
                CriteriaQuery<ConceptName> cq = cb.createQuery(ConceptName.class);
1✔
2197
                Root<ConceptName> root = cq.from(ConceptName.class);
1✔
2198

2199
                List<Predicate> predicates = new ArrayList<>();
1✔
2200

2201
                predicates.add(cb.isFalse(root.get("voided")));
1✔
2202
                predicates.add(cb.or(cb.equal(root.get("locale"), name.getLocale()),
1✔
2203
                        cb.equal(root.get("locale"), new Locale(name.getLocale().getLanguage()))));
1✔
2204

2205
                if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) {
1✔
2206
                        predicates.add(cb.equal(cb.lower(root.get("name")), name.getName().toLowerCase()));
1✔
2207
                } else {
2208
                        predicates.add(cb.equal(root.get("name"), name.getName()));
×
2209
                }
2210

2211
                cq.where(predicates.toArray(new Predicate[0]));
1✔
2212

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

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

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

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

2248
                Session session = sessionFactory.getCurrentSession();
1✔
2249
                CriteriaBuilder cb = session.getCriteriaBuilder();
1✔
2250
                CriteriaQuery<Drug> cq = cb.createQuery(Drug.class);
1✔
2251
                Root<Drug> drugRoot = cq.from(Drug.class);
1✔
2252

2253
                Join<Drug, DrugReferenceMap> drugReferenceMapJoin = drugRoot.join("drugReferenceMaps");
1✔
2254
                Join<DrugReferenceMap, ConceptReferenceTerm> termJoin = drugReferenceMapJoin.join(CONCEPT_REFERENCE_TERM);
1✔
2255
                List<Predicate> basePredicates = createSearchDrugByMappingPredicates(cb, drugRoot, drugReferenceMapJoin, termJoin,
1✔
2256
                    code, conceptSource, includeRetired);
2257

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

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

2266
                return session.createQuery(cq).getResultList().stream().distinct().collect(toList());
1✔
2267
        }
2268

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

2280
                Join<Drug, DrugReferenceMap> drugReferenceMapJoin = drugRoot.join("drugReferenceMaps");
1✔
2281
                Join<DrugReferenceMap, ConceptReferenceTerm> termJoin = drugReferenceMapJoin.join("conceptReferenceTerm");
1✔
2282

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

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

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

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

2313

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

2324
                return session.createQuery(cq).getResultList();
1✔
2325
        }
2326

2327
        /**
2328
         * @see ConceptDAO#saveConceptAttributeType(ConceptAttributeType)
2329
         */
2330
        @Override
2331
        public ConceptAttributeType saveConceptAttributeType(ConceptAttributeType conceptAttributeType) {
2332
                sessionFactory.getCurrentSession().saveOrUpdate(conceptAttributeType);
1✔
2333
                return conceptAttributeType;
1✔
2334
        }
2335

2336
        /**
2337
         * @see ConceptDAO#getConceptAttributeType(Integer)
2338
         */
2339
        @Override
2340
        public ConceptAttributeType getConceptAttributeType(Integer id) {
2341
                return sessionFactory.getCurrentSession().get(ConceptAttributeType.class, id);
1✔
2342
        }
2343

2344
        /**
2345
         * @see ConceptDAO#getConceptAttributeTypeByUuid(String)
2346
         */
2347
        @Override
2348
        public ConceptAttributeType getConceptAttributeTypeByUuid(String uuid) {
2349
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttributeType.class, uuid);
1✔
2350
        }
2351

2352
        /**
2353
         * @see org.openmrs.api.db.ConceptDAO#deleteConceptAttributeType(org.openmrs.ConceptAttributeType)
2354
         */
2355
        @Override
2356
        public void deleteConceptAttributeType(ConceptAttributeType conceptAttributeType) {
2357
                sessionFactory.getCurrentSession().delete(conceptAttributeType);
1✔
2358
        }
1✔
2359

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

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

2375
                return session.createQuery(cq).getResultList();
1✔
2376
        }
2377

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

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

2390
                return session.createQuery(cq).uniqueResult();
1✔
2391
        }
2392

2393
        /**
2394
         * @see ConceptDAO#getConceptAttributeByUuid(String)
2395
         */
2396
        @Override
2397
        public ConceptAttribute getConceptAttributeByUuid(String uuid) {
2398
                return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttribute.class, uuid);
1✔
2399
        }
2400

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

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

2413
                return session.createQuery(cq).getSingleResult();
1✔
2414
        }
2415

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

2423
                cq.where(cb.equal(root.get("conceptClass"), conceptClass));
1✔
2424

2425
                return session.createQuery(cq).getResultList();
1✔
2426
        }
2427

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

2443
                return predicates;
1✔
2444
        }
2445

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

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

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

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

2461
                Predicate namePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()
1✔
2462
                        ? cb.equal(cb.lower(sourceJoin.get("name")), sourceName.toLowerCase())
1✔
2463
                        : cb.equal(sourceJoin.get("name"), sourceName);
1✔
2464
                Predicate hl7CodePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()
1✔
2465
                        ? cb.equal(cb.lower(sourceJoin.get(HL7_CODE)), sourceName.toLowerCase())
1✔
2466
                        : cb.equal(sourceJoin.get(HL7_CODE), sourceName);
1✔
2467

2468
                predicates.add(cb.or(namePredicate, hl7CodePredicate));
1✔
2469

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

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

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

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

2499
                return session.createQuery(cq).getResultList();
1✔
2500
        }
2501

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

2512
                cq.where(cb.equal(root.get("uuid"), uuid));
1✔
2513

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

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

2552
        /**
2553
         * Order-preserving list form of {@link #multiLoadConceptsByHit(List)} for searches that return the
2554
         * concepts directly rather than pairing them into a {@link ConceptSearchResult}. Hits whose concept
2555
         * is no longer present in the database - a stale index entry - are dropped from the results.
2556
         *
2557
         * @param names the page of concept-name hits, in the order they should appear in the results
2558
         * @return the concepts for the page, in hit order, with missing rows removed
2559
         */
2560
        private List<Concept> multiLoadConcepts(List<ConceptName> names) {
2561
                Map<Integer, Concept> conceptsById = multiLoadConceptsByHit(names);
1✔
2562
                return names.stream().map(n -> conceptsById.get(n.getConcept().getConceptId())).filter(Objects::nonNull)
1✔
2563
                        .collect(Collectors.toList());
1✔
2564
        }
2565
}
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