LuceneQuery issue: Referencing attribute of an object

Hi everyone,

Intro: I am new to the OpenMRS project and need some help regarding the creation of a Lucene query in the core. I hope someone can point me to a helpful link, example or explain to me the best approach. Thank you!

Issue: I am currently working on a ticket that aims to fix a bug in the PatientService implementation (TRUNK-5395). My approach to fix this issue is to adapt a LuceneQuery to return only the PatientIdentifier objects where a specific constraint is matched. The call looks like the following:

patientIdentifierLuceneQuery.include("identifierType.patientIdentifierTypeId", identifierType.getId()); (GitHub)

The PatientIdentifier class itself has an attribute called identifierType of the type PatientIdentifierType GitHub Link. This attribute is referenced inside the LuceneQuery I am creating.

However, I am interested in the value of an attribute of this particular object, which is called patientIdentifierTypeId. (api/src/main/java/org/openmrs/PatientIdentifierType.java#L60) --> hence identifierType.patientIdentifierTypeId

In the end, I am trying to restrict the returned objects to only the once which have as attribute an identifierType, that has as patientIdentifierTypeId a certain Integer value. However, this restriction does not seem to work and I a seem to struggle to find a way to define a constraint that restricts the value of an attribute that is itself an object.

Does someone has an idea how I can define such a constraint?

Shouldn’t it be?

patientIdentifierLuceneQuery.include("patientIdentifierTypeId", identifierType.getId());

Since this is already a LuceneQuery.newQuery(PatientIdentifier.class, ...) you should reference members directly, and patientIdentifierTypeId is right there in PatientIdentifier.

hi @mksd, Thank you for your support :slight_smile:

the link you shared is pointing to patientIdentifierId and not to patientIdentifierTypeId (See here

I have to filter for the value that is not directly a member of PatientIdentifier.class but inside a object references by it. Basically, the chain is: PatientIdentifier->PatientIdentifierType->Element

That’s why I think that I can’t reference it directly? Or am I wrong?

I tried this but I still got as result a 0.

Right, I went too fast there, your syntax is the correct one to refer to a “member of member” (not attribute btw).

I think the issue might be with your use of include(..). Multiple consecutive includes correspond to AND statements. So I think that what you are doing is asking for the patient identifier type ID to be all of those provided in the list, which will always return 0 results as soon as you specify two distinct values or more. Could it be what happens?

It “should” work with one single value specified for the patient identifier type ID though.

@mksd Thank you for the hint. May you have as well one for the next issue I got :frowning:

I investigated a bit further and noticed that the PatientIdentifier objects that are returned do not have the PatientIdentifierType set as expected by looking at the Database setup file.

The test case uses the PatientServiceTest-findPatients.xml file to setup the database:

  <patient_identifier_type patient_identifier_type_id="1" name="Test Identifier Type" description="Test description" creator="1" date_created="2005-01-01 00:00:00.0" required="true" retired="true" uuid="c5576187-9a67-43a7-9b7c-04db22851211"/> 
  <patient_identifier patient_identifier_id="2" identifier="12345" identifier_type="1" patient_id="2" preferred="0" location_id="1" creator="1" date_created="2004-01-01 00:00:00.0" voided="false" uuid="504c83c7-cfbf-4ae7-a4da-bdfa3236689f"/>

Looking at this XML file I would assume that the patient_identifier with the column identifier set to"12345" would be linked to PatientIdentifierType with the value 1. However, the execution of the test case shows that the returned PatientIdentifier has as identifier_type an object that is far away from the one specified in XML. In fact, the patientIdentifierTypeID is set to null apposed to 1:

Breakpoint set here

How come that the linking for patient_identifier_type and patient_identifier in the XML world are not reflected in the Java world?

You probably need to share your code, I believe what could be happening is that the patient identifier type from the screenshot looks like a proxy object where all its fields are not yet fully populated.

As @wyclif suggested it seems that you’re inspecting a proxy object that’s been lazy loaded. Meaning that not everything is just yet filled in.

What about your unit test, could you make it work with a one patient identifier type query though?

BTW I deleted the filter from the first post to track down the error. Just to make sure everyone is on the same page. I am now using the function as it was before my addition. But I think this helps to track down the issue:

@wyclif The Unit test looks like the following:

	public void shouldGetPatientsByIdentifierAndIdentifierType() throws Exception {
	initializeInMemoryDatabase();
	executeDataSet(FIND_PATIENTS_XML);
	authenticate();
	updateSearchIndex();
	
	List<PatientIdentifierType> types = new ArrayList<>();
	types.add(new PatientIdentifierType(1));
	// make sure we get back only one patient
	List<Patient> patients = patientService.getPatients("1234", null, types, false);
	assertEquals(1, patients.size())

The test walks deep into the code of the HibernatePatientDAO and finally a function is called, that creates a LuceneQuery.

LuceneQuery<PatientIdentifier> identifierQuery = getPatientIdentifierLuceneQuery(query, includeVoided, identifierTypes);
//....
patientIdentifiers.getList().forEach(patientIdentifier -> patients.add(getPatient((Integer) patientIdentifier[0])))

The local branch I am working on Current code

@wyclif How would I be able to fully populate the object to check if my source is working. I am sorry in case I am asking such a beginner question

@mksd I am not sure what you mean with a one patient identifer type query? But I executed the query where only one match exists and it still fails.

More details

I had a look to the persistence context. In fact, the PatientIdentifierType from the XML file is loaded:

However, if we have a look to the PatientIdentifier object we can see that the values are empty. My expectation would be that inside the context both Java objects are somehow linked together. In the sense that the PatientIdentifier object contains the valid PatientIdentifierType object a s member:

My feeling is that the XML has to be changed or that a Hibernate parameter has to be set. What do is your idea? And thank for all the support I got so far

@fruether the reason the fields are blank is because it’s a proxy object, so the problem isn’t that the Java objects are different from the XML dataset, what you are seeing is normal.

@wyclif Alright

So the original issue I had is that if I filter in a LuceneQuery for identifierType.patientIdentifierTypeId but I am not getting any result although the database_init() indicates that this constraint should be matched by several PatientIdentifier

My doubts are:

  • Is this behavior explained by the fact that I am dealing with a proxy object? If so, any idea/approach how I can overcome this issue?
  • If this is not the explanation for the original issue is there another hint/idea I can investigate?

I’m not really understanding what the real issues is, but along the lines of proxy objects, if you have any logic that needs to work with the property values of a proxy object, then you need to ask hibernate to initialize it, i think the call is Hibernate.initialize(object).

@wyclif Let me try it. Thank you for your support.

Well, the real issue is: I was adding the following constraint to a given LuceneQuery:

Then I tested this new code with the test case I showed earlier. It failed since 0 PatientIdentifiers were found that matched this criteria. However, this is not the expected since according to the database initial values (see XML I quoted) such an identifier should exist.

@mksd Could this also be the reasons that my LuceneQuery is not giving the expected result or is this an independent problem? Since my main focus is to get this query to return the valid result, I need to verify if I am debugging/researching into the right direction. Any more hints?

I suspect the issue is that patientIdentifierTypeId is not annotated with @DocumentId and thus not stored as such in the Lucene index. Please add that annotation and see, if it helps.

Also I think you need to pass a collection as a second parameter to the include method, if you want to include items matching any of the given values instead of calling include multiple times and matching all of the given values as noted by @mksd. See https://github.com/openmrs/openmrs-core/blob/master/api/src/main/java/org/openmrs/api/db/hibernate/search/LuceneQuery.java#L148

2 Likes

@raff Thanks for the support. I did a “quick & dirty” try run and the DocumentId annotations seems to have fixed this issue. Thank you!

I am not able to continue working on it for now. I will use the weekend to hopefully implement everything and be able to fix the bug. I will let you know when I am done.

1 Like