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

Make LuceneSearchResultsServiceImpl the primary implementation for SearchResultsService interface #1218

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8656f99
Make Lucene implementations for search service and DAO primary
rahul6603 Aug 6, 2024
17add82
Merge branch 'develop' into primary-lucene
mozzy11 Aug 8, 2024
9b6a1d5
Merge branch 'develop' into primary-lucene
mozzy11 Aug 12, 2024
49f8ae8
Merge branch 'develop' into primary-lucene
rahul6603 Aug 16, 2024
6f1dab7
Merge branch 'develop' into primary-lucene
mozzy11 Aug 22, 2024
0ffad16
Merge branch 'develop' into primary-lucene
rahul6603 Sep 5, 2024
171d2dd
Merge branch 'develop' into primary-lucene
mozzy11 Sep 19, 2024
03a4568
Merge branch 'develop' into primary-lucene
rahul6603 Oct 1, 2024
4342ce0
Fix SQL query in LuceneSearchResultsDAOImpl
rahul6603 Oct 1, 2024
6682cdc
Merge branch 'develop' into primary-lucene
mozzy11 Oct 8, 2024
d4145d5
Merge branch 'develop' into primary-lucene
mozzy11 Oct 15, 2024
d4196f3
Merge branch 'develop' into primary-lucene
rahul6603 Nov 7, 2024
34c8b49
Merge branch 'develop' into primary-lucene
mozzy11 Nov 19, 2024
19d31dd
Merge branch 'develop' into primary-lucene
rahul6603 Dec 1, 2024
66b2d3b
Merge branch 'develop' into primary-lucene
mozzy11 Dec 16, 2024
baef461
Merge branch 'develop' into primary-lucene
rahul6603 Dec 18, 2024
ac70aba
Merge branch 'develop' into primary-lucene
mozzy11 Jan 23, 2025
c8c787f
some fixes on Lucene Search implementation
mozzy11 Jan 24, 2025
d3a1f36
Merge branch 'develop' into primary-lucene
mozzy11 Jan 24, 2025
d89ab84
fix e2e
mozzy11 Jan 24, 2025
d4a457e
Merge branch 'primary-lucene' of https://github.com/rahul6603/OpenELI…
mozzy11 Jan 24, 2025
7cec24d
Merge branch 'develop' into primary-lucene
mozzy11 Jan 26, 2025
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
@@ -0,0 +1,33 @@
package org.openelisglobal.hibernate.search.massindexer;

import org.openelisglobal.common.log.LogEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class AutoMassIndexer implements ApplicationListener<ContextRefreshedEvent> {

@Autowired
MassIndexerService massIndexerService;

private static boolean alreadyIndexed = false;

public void onApplicationEvent(ContextRefreshedEvent event) {
if (alreadyIndexed) {
return;
}
try {
LogEvent.logInfo(this.getClass().getSimpleName(), "onApplicationEvent",
"Started Rebuilding Lucene Indexes ");
massIndexerService.reindex();
alreadyIndexed = true;
LogEvent.logInfo(this.getClass().getSimpleName(), "onApplicationEvent",
"Finished Rebuilding Lucene Indexes ");
} catch (Exception e) {
LogEvent.logDebug(e);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public ResponseEntity<?> getNCESampleSearch(@RequestParam(required = false) Stri
return ResponseEntity.ok().body(temp);
}
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An error occurred while processing the request.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public interface SearchResultsDAO {
String ID_TYPE_FOR_ST = "stNumberId";
String ID_TYPE_FOR_SUBJECT_NUMBER = "subjectNumberId";
String ID_TYPE_FOR_GUID = "guidId";
String ID_LIST = "idList";

public List<PatientSearchResults> getSearchResults(String lastName, String firstName, String STNumber,
String subjectNumber, String nationalID, String externalID, String patientID, String guid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@
import org.openelisglobal.common.util.DateUtil;
import org.openelisglobal.patientidentitytype.util.PatientIdentityTypeMap;
import org.openelisglobal.sample.dao.SearchResultsDAO;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Primary
public class DBSearchResultsDAOImpl implements SearchResultsDAO {

@PersistenceContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.openelisglobal.common.exception.LIMSRuntimeException;
import org.openelisglobal.common.provider.query.PatientSearchResults;
import org.openelisglobal.common.util.ConfigurationProperties;
import org.openelisglobal.common.util.ConfigurationProperties.Property;
import org.openelisglobal.common.util.DateUtil;
import org.openelisglobal.patient.valueholder.Patient;
import org.openelisglobal.patientidentitytype.util.PatientIdentityTypeMap;
import org.openelisglobal.sample.dao.SearchResultsDAO;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class LuceneSearchResultsDAOImpl implements SearchResultsDAO {

@PersistenceContext
Expand All @@ -33,25 +38,43 @@ public List<PatientSearchResults> getSearchResults(String lastName, String first

SearchSession searchSession = Search.session(entityManager);

boolean queryFirstName = !GenericValidator.isBlankOrNull(firstName);
boolean queryLastName = !GenericValidator.isBlankOrNull(lastName);
boolean queryNationalId = !GenericValidator.isBlankOrNull(nationalID);
boolean querySTNumber = !GenericValidator.isBlankOrNull(STNumber);
boolean querySubjectNumber = !GenericValidator.isBlankOrNull(subjectNumber);
boolean queryExternalId = !GenericValidator.isBlankOrNull(externalID);
boolean queryPatientID = !GenericValidator.isBlankOrNull(patientID);
boolean queryGuid = !GenericValidator.isBlankOrNull(guid);
boolean queryDateOfBirth = !GenericValidator.isBlankOrNull(dateOfBirth);
boolean queryGender = !GenericValidator.isBlankOrNull(gender);

// fields searched from DB
nationalID = '%' + nationalID + '%';
STNumber = '%' + STNumber + '%';
subjectNumber = '%' + subjectNumber + '%';
externalID = '%' + externalID + '%';

List<String> hits = searchSession.search(Patient.class).select(f -> f.id(String.class)).where(f -> f.bool(b -> {
if (!GenericValidator.isBlankOrNull(patientID)) {
if (queryPatientID) {
b.must(f.match().field("id").matching(patientID));
}
if (!GenericValidator.isBlankOrNull(gender)) {
if (queryGender) {
b.must(f.match().field("gender").matching(gender));
}
if (!GenericValidator.isBlankOrNull(dateOfBirth)) {
b.must(f.match().field("birthDateForDisplay").matching(dateOfBirth));
if (queryDateOfBirth) {
b.must(f.bool().should(f.match().field("birthDateForDisplay").matching(dateOfBirth))
.should(f.match().field("birthDateForDisplay").matching(getFormatedDOB(dateOfBirth))));
}
if (!GenericValidator.isBlankOrNull(firstName) && !GenericValidator.isBlankOrNull(lastName)) {
if (queryFirstName && queryLastName) {
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)) {
if (queryFirstName) {
b.must(f.match().field("person.firstName").matching(firstName).fuzzy());
}
if (!GenericValidator.isBlankOrNull(lastName)) {
if (queryLastName) {
b.must(f.match().field("person.lastName").matching(lastName).fuzzy());
}
}
Expand All @@ -60,31 +83,35 @@ public List<PatientSearchResults> getSearchResults(String lastName, String first
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);
int hitsCount = longHits.size();

String sqlString = buildQueryString(nationalID, externalID, STNumber, subjectNumber, guid);
String sqlString = buildQueryString(queryNationalId, queryExternalId, querySTNumber, querySubjectNumber,
queryGuid, hitsCount);
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)) {
if (queryNationalId) {
query.setParameter(NATIONAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(externalID)) {
if (queryExternalId) {
query.setParameter(EXTERNAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(STNumber)) {
if (querySTNumber) {
query.setParameter(ST_NUMBER_PARAM, STNumber);
}
if (!GenericValidator.isBlankOrNull(subjectNumber)) {
if (querySubjectNumber) {
query.setParameter(SUBJECT_NUMBER_PARAM, subjectNumber);
}
if (!GenericValidator.isBlankOrNull(guid)) {
if (queryGuid) {
query.setParameter(GUID, guid);
}
query.setParameter("idList", longHits);
if (hitsCount > 1) {
query.setParameter(ID_LIST, longHits);
}

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

Expand Down Expand Up @@ -119,25 +146,37 @@ public List<PatientSearchResults> getSearchResultsExact(String lastName, String

SearchSession searchSession = Search.session(entityManager);

boolean queryFirstName = !GenericValidator.isBlankOrNull(firstName);
boolean queryLastName = !GenericValidator.isBlankOrNull(lastName);
boolean queryNationalId = !GenericValidator.isBlankOrNull(nationalID);
boolean querySTNumber = !GenericValidator.isBlankOrNull(STNumber);
boolean querySubjectNumber = !GenericValidator.isBlankOrNull(subjectNumber);
boolean queryExternalId = !GenericValidator.isBlankOrNull(externalID);
boolean queryPatientID = !GenericValidator.isBlankOrNull(patientID);
boolean queryGuid = !GenericValidator.isBlankOrNull(guid);
boolean queryDateOfBirth = !GenericValidator.isBlankOrNull(dateOfBirth);
boolean queryGender = !GenericValidator.isBlankOrNull(gender);

List<String> hits = searchSession.search(Patient.class).select(f -> f.id(String.class)).where(f -> f.bool(b -> {
if (!GenericValidator.isBlankOrNull(patientID)) {
if (queryPatientID) {
b.must(f.match().field("id").matching(patientID));
}
if (!GenericValidator.isBlankOrNull(gender)) {
if (queryGender) {
b.must(f.match().field("gender").matching(gender));
}
if (!GenericValidator.isBlankOrNull(dateOfBirth)) {
b.must(f.match().field("birthDateForDisplay").matching(dateOfBirth));
if (queryDateOfBirth) {
b.must(f.bool().should(f.match().field("birthDateForDisplay").matching(dateOfBirth))
.should(f.match().field("birthDateForDisplay").matching(getFormatedDOB(dateOfBirth))));
}
if (!GenericValidator.isBlankOrNull(firstName) && !GenericValidator.isBlankOrNull(lastName)) {
if (queryFirstName && queryLastName) {
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)) {
if (queryFirstName) {
b.must(f.match().field("person.firstName").matching(firstName));
}
if (!GenericValidator.isBlankOrNull(lastName)) {
if (queryLastName) {
b.must(f.match().field("person.lastName").matching(lastName));
}
}
Expand All @@ -146,31 +185,35 @@ public List<PatientSearchResults> getSearchResultsExact(String lastName, String
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);
int hitsCount = longHits.size();

String sqlString = buildQueryString(nationalID, externalID, STNumber, subjectNumber, guid);
String sqlString = buildQueryString(queryNationalId, queryExternalId, querySTNumber, querySubjectNumber,
queryGuid, hitsCount);
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)) {
if (queryNationalId) {
query.setParameter(NATIONAL_ID_PARAM, nationalID);
Copy link
Collaborator

@mozzy11 mozzy11 Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way @rahul6603 , i can see nationalID and externalID are being indexed. why not search them from Lucene indexes instead of the DB

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you would also add that the tests if it works

}
if (!GenericValidator.isBlankOrNull(externalID)) {
if (queryExternalId) {
query.setParameter(EXTERNAL_ID_PARAM, nationalID);
}
if (!GenericValidator.isBlankOrNull(STNumber)) {
if (querySTNumber) {
query.setParameter(ST_NUMBER_PARAM, STNumber);
}
if (!GenericValidator.isBlankOrNull(subjectNumber)) {
if (querySubjectNumber) {
query.setParameter(SUBJECT_NUMBER_PARAM, subjectNumber);
}
if (!GenericValidator.isBlankOrNull(guid)) {
if (queryGuid) {
query.setParameter(GUID, guid);
}
query.setParameter("idList", longHits);
if (hitsCount > 1) {
query.setParameter(ID_LIST, longHits);
}

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

Expand All @@ -187,8 +230,8 @@ public List<PatientSearchResults> getSearchResultsExact(String lastName, String
return patientSearchResultsList;
}

private String buildQueryString(String nationalID, String externalID, String STNumber, String subjectNumber,
String guid) {
private String buildQueryString(Boolean queryNationalID, Boolean queryExternalID, Boolean querySTNumber,
Boolean querySubjectNumber, Boolean queryGuid, int hitsCount) {

StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("select p.id, pr.first_name, pr.last_name, p.gender, p.entered_birth_date, p.national_id,"
Expand All @@ -205,22 +248,22 @@ private String buildQueryString(String nationalID, String externalID, String STN
queryBuilder.append("where ");

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

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

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

if (!GenericValidator.isBlankOrNull(STNumber)) {
if (querySTNumber) {
queryBuilder.append("pi.identity_data ilike :");
queryBuilder.append(ST_NUMBER_PARAM).append(" and ");
}
Expand All @@ -237,15 +280,36 @@ private String buildQueryString(String nationalID, String externalID, String STN
queryBuilder.append(") or ");
}

if (!GenericValidator.isBlankOrNull(guid)) {
if (queryGuid) {
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");
if (hitsCount > 1) {
queryBuilder.append("p.id in :");
queryBuilder.append(ID_LIST).append(" and ");
}

// No matter which was added last there is one dangling AND to remove.
lastAndIndex = queryBuilder.lastIndexOf("and");
lastOrIndex = queryBuilder.lastIndexOf("or");

if (lastAndIndex > lastOrIndex) {
queryBuilder.delete(lastAndIndex, queryBuilder.length());
} else if (lastOrIndex > lastAndIndex) {
queryBuilder.delete(lastOrIndex, queryBuilder.length());
}
return queryBuilder.toString();
}

private String getFormatedDOB(String dob) {
String format1 = "dd/MM/yyyy";
String format2 = "MM/dd/yyyy";
String dobFormated = ConfigurationProperties.getInstance().getPropertyValue(Property.DEFAULT_DATE_LOCALE)
.equals("fr-FR") ? DateUtil.formatStringDate(dob, format2) : DateUtil.formatStringDate(dob, format1);

return dobFormated;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import org.openelisglobal.common.provider.query.PatientSearchResults;
import org.openelisglobal.sample.dao.SearchResultsDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Primary
public class DBSearchResultsServiceImpl implements SearchResultsService {

@Autowired
@Qualifier("DBSearchResultsDAOImpl")
SearchResultsDAO searchResultsDAO;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import org.openelisglobal.sample.dao.SearchResultsDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Service
@Primary
public class LuceneSearchResultsServiceImpl implements SearchResultsService {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class SearchResultsServiceTest extends BaseWebContextSensitiveTest {
PersonService personService;

@Autowired
@Qualifier("DBSearchResultsServiceImpl")
SearchResultsService DBSearchResultsServiceImpl;

@Autowired
Expand Down
Loading