org.openmrs.module.emr.api.db.hibernate.HibernateEmrDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.emr.api.db.hibernate.HibernateEmrDAO.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.emr.api.db.hibernate;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.openmrs.Concept;
import org.openmrs.ConceptClass;
import org.openmrs.ConceptMap;
import org.openmrs.ConceptName;
import org.openmrs.ConceptSearchResult;
import org.openmrs.ConceptSet;
import org.openmrs.ConceptSource;
import org.openmrs.ConceptWord;
import org.openmrs.Location;
import org.openmrs.Patient;
import org.openmrs.PatientIdentifierType;
import org.openmrs.Visit;
import org.openmrs.api.db.hibernate.PatientSearchCriteria;
import org.openmrs.module.emr.api.db.EmrDAO;
import org.openmrs.module.emrapi.EmrApiProperties;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

public class HibernateEmrDAO implements EmrDAO {

    private SessionFactory sessionFactory;
    private EmrApiProperties emrApiProperties;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void setEmrApiProperties(EmrApiProperties emrApiProperties) {
        this.emrApiProperties = emrApiProperties;
    }

    @Override
    public List<Patient> findPatients(String query, Location checkedInAt, Integer start, Integer maxResults) {

        Criteria criteria;
        if (checkedInAt != null) {
            criteria = sessionFactory.getCurrentSession().createCriteria(Visit.class);
            criteria.setProjection(Property.forName("patient"));
            criteria.add(Restrictions.isNull("stopDatetime"));
            criteria.add(Restrictions.eq("location", checkedInAt));
            Criteria patientCriteria = criteria.createCriteria("patient");
            if (StringUtils.isNotBlank(query)) {
                patientCriteria = buildCriteria(query, patientCriteria);
            }
            criteria = patientCriteria;
        } else {
            criteria = sessionFactory.getCurrentSession().createCriteria(Patient.class);
            criteria = buildCriteria(query, criteria);
        }

        if (start != null) {
            criteria.setFirstResult(start);
        }

        if (maxResults != null) {
            criteria.setMaxResults(maxResults);
        }

        return (List<Patient>) criteria.list();
    }

    private Criteria buildCriteria(String query, Criteria criteria) {
        if (query.matches(".*\\d.*")) {
            // has at least one digit, so treat as an identifier
            return new PatientSearchCriteria(sessionFactory, criteria).prepareCriteria(null, query,
                    emrApiProperties.getIdentifierTypesToSearch(), true, true);
        } else {
            // no digits, so treat as a name
            return new PatientSearchCriteria(sessionFactory, criteria).prepareCriteria(query, null,
                    new ArrayList<PatientIdentifierType>(), true, true);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<ConceptSearchResult> conceptSearch(String query, Locale locale, Collection<ConceptClass> classes,
            Collection<Concept> inSets, Collection<ConceptSource> sources, Integer limit) {
        List<String> uniqueWords = ConceptWord.getUniqueWords(query, locale);
        if (uniqueWords.size() == 0) {
            return Collections.emptyList();
        }

        List<ConceptSearchResult> results = new ArrayList<ConceptSearchResult>();

        // find matches based on name
        {
            Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptName.class, "cn");
            criteria.add(Restrictions.eq("voided", false));
            criteria.add(Restrictions.eq("locale", locale));
            criteria.setMaxResults(limit);

            Criteria conceptCriteria = criteria.createCriteria("concept");
            conceptCriteria.add(Restrictions.eq("retired", false));
            if (classes != null) {
                conceptCriteria.add(Restrictions.in("conceptClass", classes));
            }
            if (inSets != null) {
                DetachedCriteria allowedSetMembers = DetachedCriteria.forClass(ConceptSet.class);
                allowedSetMembers.add(Restrictions.in("conceptSet", inSets));
                allowedSetMembers.setProjection(Projections.property("concept"));
                criteria.add(Subqueries.propertyIn("concept", allowedSetMembers));
            }

            for (String word : uniqueWords) {
                criteria.add(Restrictions.ilike("name", word, MatchMode.ANYWHERE));
            }

            Set<Concept> conceptsMatchedByPreferredName = new HashSet<Concept>();
            for (ConceptName matchedName : (List<ConceptName>) criteria.list()) {
                results.add(new ConceptSearchResult(null, matchedName.getConcept(), matchedName,
                        calculateMatchScore(query, uniqueWords, matchedName)));
                if (matchedName.isLocalePreferred()) {
                    conceptsMatchedByPreferredName.add(matchedName.getConcept());
                }
            }

            // don't display synonym matches if the preferred name matches too
            for (Iterator<ConceptSearchResult> i = results.iterator(); i.hasNext();) {
                ConceptSearchResult candidate = i.next();
                if (!candidate.getConceptName().isLocalePreferred()
                        && conceptsMatchedByPreferredName.contains(candidate.getConcept())) {
                    i.remove();
                }
            }
        }

        // find matches based on mapping
        if (sources != null) {
            Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class);
            criteria.setMaxResults(limit);

            Criteria conceptCriteria = criteria.createCriteria("concept");
            conceptCriteria.add(Restrictions.eq("retired", false));
            if (classes != null) {
                conceptCriteria.add(Restrictions.in("conceptClass", classes));
            }

            Criteria mappedTerm = criteria.createCriteria("conceptReferenceTerm");
            mappedTerm.add(Restrictions.eq("retired", false));
            mappedTerm.add(Restrictions.in("conceptSource", sources));
            mappedTerm.add(Restrictions.ilike("code", query, MatchMode.EXACT));

            for (ConceptMap mapping : (List<ConceptMap>) criteria.list()) {
                results.add(new ConceptSearchResult(null, mapping.getConcept(), null,
                        calculateMatchScore(query, mapping)));
            }
        }

        Collections.sort(results, new Comparator<ConceptSearchResult>() {
            @Override
            public int compare(ConceptSearchResult left, ConceptSearchResult right) {
                return right.getTransientWeight().compareTo(left.getTransientWeight());
            }
        });

        if (results.size() > limit) {
            results = results.subList(0, limit);
        }
        return results;
    }

    private Double calculateMatchScore(String query, ConceptMap matchedMapping) {
        // eventually consider weighting this by map type (e.g. same-as > narrower-than > others)
        return 10000d;
    }

    private Double calculateMatchScore(String query, List<String> uniqueWords, ConceptName matchedName) {
        double score = 0d;
        if (query.equalsIgnoreCase(matchedName.getName())) {
            score += 1000d;
        }
        if (matchedName.isLocalePreferred()) {
            score += 500d;
        }
        score -= matchedName.getName().length();
        return score;
    }

}