Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Lucene queries in getSearchResults and getSearchResultsExact methods of LuceneSearchResultsDAOImpl #1180

Merged
merged 5 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -321,57 +321,29 @@ private String buildQueryString(boolean lastName, boolean firstName, boolean STN
queryBuilder.append(ID_TYPE_FOR_GUID);
queryBuilder.append(" where ");

if (anyID) {
mozzy11 marked this conversation as resolved.
Show resolved Hide resolved
queryBuilder.append(" ( false or ");
if (subjectNumber) {
queryBuilder.append(" piSN.identity_data ilike :");
queryBuilder.append(SUBJECT_NUMBER_PARAM);
queryBuilder.append(" or");
}

if (nationalID) {
queryBuilder.append(" p.national_id ilike :");
queryBuilder.append(NATIONAL_ID_PARAM);
queryBuilder.append(" or");
}

if (externalID) {
queryBuilder.append(" p.external_id ilike :");
queryBuilder.append(EXTERNAL_ID_PARAM);
queryBuilder.append(" or");
}

if (STNumber) {
queryBuilder.append(" pi.identity_data ilike :");
queryBuilder.append(ST_NUMBER_PARAM);
queryBuilder.append(" and");
}

} else {
queryBuilder.append(" ( false or ");
if (subjectNumber) {
queryBuilder.append(" piSN.identity_data ilike :");
queryBuilder.append(SUBJECT_NUMBER_PARAM);
queryBuilder.append(" or");
}
queryBuilder.append(" ( false or ");
if (subjectNumber) {
queryBuilder.append(" piSN.identity_data ilike :");
queryBuilder.append(SUBJECT_NUMBER_PARAM);
queryBuilder.append(" or");
}

if (nationalID) {
queryBuilder.append(" p.national_id ilike :");
queryBuilder.append(NATIONAL_ID_PARAM);
queryBuilder.append(" or");
}
if (nationalID) {
queryBuilder.append(" p.national_id ilike :");
queryBuilder.append(NATIONAL_ID_PARAM);
queryBuilder.append(" or");
}

if (externalID) {
queryBuilder.append(" p.external_id ilike :");
queryBuilder.append(EXTERNAL_ID_PARAM);
queryBuilder.append(" or");
}
if (externalID) {
queryBuilder.append(" p.external_id ilike :");
queryBuilder.append(EXTERNAL_ID_PARAM);
queryBuilder.append(" or");
}

if (STNumber) {
queryBuilder.append(" pi.identity_data ilike :");
queryBuilder.append(ST_NUMBER_PARAM);
queryBuilder.append(" and");
}
if (STNumber) {
queryBuilder.append(" pi.identity_data ilike :");
queryBuilder.append(ST_NUMBER_PARAM);
queryBuilder.append(" and");
}

// Need to close paren before dangling AND/OR.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,103 @@
package org.openelisglobal.sample.daoimpl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import org.apache.commons.validator.GenericValidator;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.openelisglobal.common.exception.LIMSRuntimeException;
import org.openelisglobal.common.provider.query.PatientSearchResults;
import org.openelisglobal.patient.valueholder.Patient;
import org.openelisglobal.patientidentitytype.util.PatientIdentityTypeMap;
import org.openelisglobal.sample.dao.SearchResultsDAO;
import org.springframework.stereotype.Component;

@Component
public class LuceneSearchResultsDAOImpl implements SearchResultsDAO {

@PersistenceContext
EntityManager entityManager;

@Override
@Transactional
public List<PatientSearchResults> getSearchResults(String lastName, String firstName, String STNumber,
String subjectNumber, String nationalID, String externalID, String patientID, String guid,
String dateOfBirth, String gender) throws LIMSRuntimeException {

SearchSession searchSession = Search.session(entityManager);

List<String> hits = searchSession.search(Patient.class).select(f -> f.id(String.class)).where(f -> f.bool(b -> {
if (!GenericValidator.isBlankOrNull(patientID)) {
b.must(f.match().field("id").matching(patientID));
}
if (!GenericValidator.isBlankOrNull(gender)) {
b.must(f.match().field("gender").matching(gender));
}
if (!GenericValidator.isBlankOrNull(dateOfBirth)) {
b.must(f.match().field("birthDateForDisplay").matching(dateOfBirth));
}
if (!GenericValidator.isBlankOrNull(firstName) && !GenericValidator.isBlankOrNull(lastName)) {
b.must(f.nested().objectField("person")
.nest(f.bool().must(f.match().field("person.firstName").matching(firstName).fuzzy())
.must(f.match().field("person.lastName").matching(lastName).fuzzy())));
} else {
if (!GenericValidator.isBlankOrNull(firstName)) {
b.must(f.match().field("person.firstName").matching(firstName).fuzzy());
}
if (!GenericValidator.isBlankOrNull(lastName)) {
b.must(f.match().field("person.lastName").matching(lastName).fuzzy());
}
}
})).fetchAllHits();

List<Long> longHits = hits.stream().map(Long::parseLong).collect(Collectors.toList());
// 'IN' predicate requires the list to contain at least one value
longHits.add(-1L);

String sqlString = buildQueryString(nationalID, externalID, STNumber, subjectNumber, guid);
Query query = entityManager.unwrap(Session.class).createNativeQuery(sqlString);
query.setParameter(ID_TYPE_FOR_ST, Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("ST")));
query.setParameter(ID_TYPE_FOR_SUBJECT_NUMBER,
Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("SUBJECT")));
query.setParameter(ID_TYPE_FOR_GUID,
Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("GUID")));

if (!GenericValidator.isBlankOrNull(nationalID)) {
query.setParameter(NATIONAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(externalID)) {
query.setParameter(EXTERNAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(STNumber)) {
query.setParameter(ST_NUMBER_PARAM, STNumber);
}
if (!GenericValidator.isBlankOrNull(subjectNumber)) {
query.setParameter(SUBJECT_NUMBER_PARAM, subjectNumber);
}
if (!GenericValidator.isBlankOrNull(guid)) {
query.setParameter(GUID, guid);
}
query.setParameter("idList", longHits);

List<Object[]> queryResults = query.list();

List<PatientSearchResults> patientSearchResultsList = new ArrayList<>();

for (Object[] tuple : queryResults) {

PatientSearchResults patientSearchResults = new PatientSearchResults((BigDecimal) tuple[0],
(String) tuple[1], (String) tuple[2], (String) tuple[3], (String) tuple[4], (String) tuple[5],
(String) tuple[6], (String) tuple[7], (String) tuple[8], (String) tuple[9], null);
patientSearchResultsList.add(patientSearchResults);
}

return patientSearchResultsList;
}

Expand All @@ -37,8 +117,135 @@ public List<PatientSearchResults> getSearchResultsExact(String lastName, String
String subjectNumber, String nationalID, String externalID, String patientID, String guid,
String dateOfBirth, String gender) throws LIMSRuntimeException {

SearchSession searchSession = Search.session(entityManager);

List<String> hits = searchSession.search(Patient.class).select(f -> f.id(String.class)).where(f -> f.bool(b -> {
if (!GenericValidator.isBlankOrNull(patientID)) {
b.must(f.match().field("id").matching(patientID));
}
if (!GenericValidator.isBlankOrNull(gender)) {
b.must(f.match().field("gender").matching(gender));
}
if (!GenericValidator.isBlankOrNull(dateOfBirth)) {
b.must(f.match().field("birthDateForDisplay").matching(dateOfBirth));
}
if (!GenericValidator.isBlankOrNull(firstName) && !GenericValidator.isBlankOrNull(lastName)) {
b.must(f.nested().objectField("person")
.nest(f.bool().must(f.match().field("person.firstName").matching(firstName))
.must(f.match().field("person.lastName").matching(lastName))));
} else {
if (!GenericValidator.isBlankOrNull(firstName)) {
b.must(f.match().field("person.firstName").matching(firstName));
}
if (!GenericValidator.isBlankOrNull(lastName)) {
b.must(f.match().field("person.lastName").matching(lastName));
}
}
})).fetchAllHits();

List<Long> longHits = hits.stream().map(Long::parseLong).collect(Collectors.toList());
// 'IN' predicate requires the list to contain at least one value
longHits.add(-1L);

String sqlString = buildQueryString(nationalID, externalID, STNumber, subjectNumber, guid);
Query query = entityManager.unwrap(Session.class).createNativeQuery(sqlString);
query.setParameter(ID_TYPE_FOR_ST, Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("ST")));
query.setParameter(ID_TYPE_FOR_SUBJECT_NUMBER,
Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("SUBJECT")));
query.setParameter(ID_TYPE_FOR_GUID,
Integer.valueOf(PatientIdentityTypeMap.getInstance().getIDForType("GUID")));

if (!GenericValidator.isBlankOrNull(nationalID)) {
query.setParameter(NATIONAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(externalID)) {
query.setParameter(EXTERNAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(STNumber)) {
query.setParameter(ST_NUMBER_PARAM, STNumber);
}
if (!GenericValidator.isBlankOrNull(subjectNumber)) {
query.setParameter(SUBJECT_NUMBER_PARAM, subjectNumber);
}
if (!GenericValidator.isBlankOrNull(guid)) {
query.setParameter(GUID, guid);
}
query.setParameter("idList", longHits);

List<Object[]> queryResults = query.list();

List<PatientSearchResults> patientSearchResultsList = new ArrayList<>();

for (Object[] tuple : queryResults) {

PatientSearchResults patientSearchResults = new PatientSearchResults((BigDecimal) tuple[0],
(String) tuple[1], (String) tuple[2], (String) tuple[3], (String) tuple[4], (String) tuple[5],
(String) tuple[6], (String) tuple[7], (String) tuple[8], (String) tuple[9], null);
patientSearchResultsList.add(patientSearchResults);
}

return patientSearchResultsList;
}

private String buildQueryString(String nationalID, String externalID, String STNumber, String subjectNumber,
String guid) {

StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("select p.id, pr.first_name, pr.last_name, p.gender, p.entered_birth_date, p.national_id,"
+ " p.external_id, pi.identity_data as st, piSN.identity_data as subject,"
+ " piGUID.identity_data as guid ");
queryBuilder.append("from patient p join person pr on p.person_id = pr.id ");
queryBuilder.append("left join patient_identity pi on pi.patient_id = p.id and pi.identity_type_id = :");
queryBuilder.append(ID_TYPE_FOR_ST).append(" ");
queryBuilder.append("left join patient_identity piSN on piSN.patient_id = p.id and piSN.identity_type_id = :");
queryBuilder.append(ID_TYPE_FOR_SUBJECT_NUMBER).append(" ");
queryBuilder.append(
"left join patient_identity piGUID on piGUID.patient_id = p.id and piGUID.identity_type_id" + " = :");
queryBuilder.append(ID_TYPE_FOR_GUID).append(" ");
queryBuilder.append("where ");

queryBuilder.append("( false or ");
if (!GenericValidator.isBlankOrNull(subjectNumber)) {
queryBuilder.append("piSN.identity_data ilike :");
queryBuilder.append(SUBJECT_NUMBER_PARAM).append(" or ");
}

if (!GenericValidator.isBlankOrNull(nationalID)) {
queryBuilder.append("p.national_id ilike :");
queryBuilder.append(NATIONAL_ID_PARAM).append(" or ");
}

if (!GenericValidator.isBlankOrNull(externalID)) {
queryBuilder.append("p.external_id ilike :");
queryBuilder.append(EXTERNAL_ID_PARAM).append(" or ");
}

if (!GenericValidator.isBlankOrNull(STNumber)) {
queryBuilder.append("pi.identity_data ilike :");
queryBuilder.append(ST_NUMBER_PARAM).append(" and ");
}

// Need to close paren before dangling AND/OR.
int lastAndIndex = queryBuilder.lastIndexOf("and");
int lastOrIndex = queryBuilder.lastIndexOf("or");

if (lastAndIndex > lastOrIndex) {
queryBuilder.delete(lastAndIndex, queryBuilder.length());
queryBuilder.append(") and ");
} else if (lastOrIndex > lastAndIndex) {
queryBuilder.delete(lastOrIndex, queryBuilder.length());
queryBuilder.append(") or ");
}

if (!GenericValidator.isBlankOrNull(guid)) {
queryBuilder.append("piGUID.identity_data = :");
queryBuilder.append(GUID).append(" and ");
}

// idList contains patient IDs from Lucene search results matching the fields
// id, person.firstName, person.lastName, birthDateForDisplay and gender
queryBuilder.append("p.id in :idList");

return queryBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,18 @@ public void getSearchResults_shouldGetSearchResultsFromLuceneIndexes() throws Ex
Patient pat = createPatient(firstName, lastname, dob, gender);
String patientId = patientService.insert(pat);

List<PatientSearchResults> searchResults = luceneSearchResultsServiceImpl.getSearchResults(lastname, firstName,
null, null, null, null, null, null, dob, gender);
String searchFirstName = "Johm";
String searchLastName = "Doee";

List<PatientSearchResults> searchResults = luceneSearchResultsServiceImpl.getSearchResults(searchLastName,
searchFirstName, null, null, null, null, null, null, dob, gender);

// The search results are currently expected to be empty because the method has
// not been implemented yet
Assert.assertEquals(0, searchResults.size());
Assert.assertEquals(1, searchResults.size());
PatientSearchResults result = searchResults.get(0);
Assert.assertEquals(patientId, result.getPatientID());
Assert.assertEquals(firstName, result.getFirstName());
Assert.assertEquals(lastname, result.getLastName());
Assert.assertEquals(dob, result.getBirthdate());
}

@Test
Expand All @@ -119,9 +125,12 @@ public void getSearchResultsExact_shouldGetExactSearchResultsFromLuceneIndexes()
List<PatientSearchResults> searchResults = luceneSearchResultsServiceImpl.getSearchResultsExact(lastname,
firstName, null, null, null, null, null, null, dob, gender);

// The search results are currently expected to be empty because the method has
// not been implemented yet
Assert.assertEquals(0, searchResults.size());
Assert.assertEquals(1, searchResults.size());
PatientSearchResults result = searchResults.get(0);
Assert.assertEquals(patientId, result.getPatientID());
Assert.assertEquals(firstName, result.getFirstName());
Assert.assertEquals(lastname, result.getLastName());
Assert.assertEquals(dob, result.getBirthdate());
}

private Patient createPatient(String firstName, String LastName, String birthDate, String gender)
Expand Down
Loading