Initial commit of changes to support $lastn operation.
This commit is contained in:
parent
ecd2bcf492
commit
4e4c8dbd9a
|
@ -155,6 +155,8 @@
|
||||||
<ignoredResource>javac.bat</ignoredResource>
|
<ignoredResource>javac.bat</ignoredResource>
|
||||||
<ignoredResource>about.html</ignoredResource>
|
<ignoredResource>about.html</ignoredResource>
|
||||||
<ignoredResource>changelog.xml</ignoredResource>
|
<ignoredResource>changelog.xml</ignoredResource>
|
||||||
|
<ignoredResource>.*/favicon.ico$</ignoredResource>
|
||||||
|
<ignoredResource>Log4j-charsets.properties</ignoredResource>
|
||||||
</ignoredResourcePatterns>
|
</ignoredResourcePatterns>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-elasticsearch-6</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>hapi-fhir-elasticsearch-6</name>
|
||||||
|
<!-- FIXME change it to the project's website -->
|
||||||
|
<url>http://www.example.com</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.7</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.7</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Elasticsearch -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
|
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||||
|
<version>6.5.4</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.github.spullara.mustache.java</groupId>
|
||||||
|
<artifactId>compiler</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.tdunning</groupId>
|
||||||
|
<artifactId>t-digest</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
|
<artifactId>jopt-simple</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.lucene</groupId>
|
||||||
|
<artifactId>lucene-analyzers-common</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.lucene</groupId>
|
||||||
|
<artifactId>lucene-backward-codecs</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.lucene</groupId>
|
||||||
|
<artifactId>lucene-sandbox</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.elasticsearch</groupId>
|
||||||
|
<artifactId>jna</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.hdrhistogram</groupId>
|
||||||
|
<artifactId>HdrHistogram</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||||
|
<plugins>
|
||||||
|
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.5.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.8.2</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
|
<version>3.7.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||||
|
<shadedClassifierName>shaded6</shadedClassifierName> <!-- Any name that makes sense -->
|
||||||
|
<relocations>
|
||||||
|
<relocation>
|
||||||
|
<pattern>com.carrotsearch.hppc</pattern>
|
||||||
|
<shadedPattern>com.shadehapi.carrotsearch.hppc</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.apache.logging.log4j</pattern>
|
||||||
|
<shadedPattern>org.shadehapi.apache.logging.log4j</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.apache.lucene</pattern>
|
||||||
|
<shadedPattern>org.shadehapi.apache.lucene</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.elasticsearch</pattern>
|
||||||
|
<shadedPattern>org.shadehapi.elasticsearch</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<reloaction>
|
||||||
|
<pattern>org.joda</pattern>
|
||||||
|
<shadedPattern>org.shadehapi.joda</shadedPattern>
|
||||||
|
</reloaction>
|
||||||
|
</relocations>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ca.uhn.fhir.fhir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hello world!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class App
|
||||||
|
{
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
System.out.println( "Hello World!" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ca.uhn.fhir.fhir;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for simple App.
|
||||||
|
*/
|
||||||
|
public class AppTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Rigorous Test :-)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldAnswerWithTrue()
|
||||||
|
{
|
||||||
|
assertTrue( true );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface IObservationIndexedCodeCodeableConceptSearchParamDao extends JpaRepository<ObservationIndexedCodeCodeableConceptEntity, Long> {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodingEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface IObservationIndexedCodeCodingSearchParamDao extends JpaRepository<ObservationIndexedCodeCodingEntity, Long> {
|
||||||
|
|
||||||
|
@Query("" +
|
||||||
|
"SELECT t.myCodeableConceptId FROM ObservationIndexedCodeCodingEntity t " +
|
||||||
|
"WHERE t.myCode = :code " +
|
||||||
|
"AND t.mySystem = :system " +
|
||||||
|
"")
|
||||||
|
String findForCodeAndSystem(@Param("code") String theCode, @Param("system") String theSystem);
|
||||||
|
|
||||||
|
|
||||||
|
@Query("" +
|
||||||
|
"SELECT t.myCodeableConceptId FROM ObservationIndexedCodeCodingEntity t " +
|
||||||
|
"WHERE t.myDisplay = :display" +
|
||||||
|
"")
|
||||||
|
String findForDisplay(@Param("display") String theDisplay);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface IObservationIndexedSearchParamLastNDao extends JpaRepository<ObservationIndexedSearchParamLastNEntity, Long>{
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedCodeCodingSearchParamDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.*;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedCodeCodeableConceptSearchParamDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedSearchParamLastNDao;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ObservationLastNIndexPersistSvc {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedSearchParamLastNDao myResourceIndexedObservationLastNDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedCodeCodeableConceptSearchParamDao myObservationIndexedCodeableConceptSearchParamDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedCodeCodingSearchParamDao myObservationIndexedCodeCodingSearchParamDao;
|
||||||
|
|
||||||
|
public void indexObservation(Observation theObservation) {
|
||||||
|
ObservationIndexedSearchParamLastNEntity indexedObservation = new ObservationIndexedSearchParamLastNEntity();
|
||||||
|
String resourcePID = theObservation.getId();
|
||||||
|
indexedObservation.setIdentifier(resourcePID);
|
||||||
|
// TODO: Need to improve this as there are multiple use cases involving subject.
|
||||||
|
String subjectId = "Patient/" + theObservation.getSubject().getReference();
|
||||||
|
indexedObservation.setSubject(subjectId);
|
||||||
|
Date effectiveDtm = theObservation.getEffectiveDateTimeType().getValue();
|
||||||
|
indexedObservation.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
// Build CodeableConcept entities for Observation.Category
|
||||||
|
Set<ObservationIndexedCategoryCodeableConceptEntity> categoryConcepts = new HashSet<>();
|
||||||
|
for(CodeableConcept categoryCodeableConcept : theObservation.getCategory()) {
|
||||||
|
// Build Coding entities for each category CodeableConcept
|
||||||
|
Set<ObservationIndexedCategoryCodingEntity> categoryCodingEntities = new HashSet<>();
|
||||||
|
ObservationIndexedCategoryCodeableConceptEntity categoryCodeableConceptEntity = new ObservationIndexedCategoryCodeableConceptEntity(categoryCodeableConcept.getText());
|
||||||
|
for(Coding categoryCoding : categoryCodeableConcept.getCoding()){
|
||||||
|
categoryCodingEntities.add(new ObservationIndexedCategoryCodingEntity(categoryCoding.getSystem(), categoryCoding.getCode(), categoryCoding.getDisplay()));
|
||||||
|
}
|
||||||
|
categoryCodeableConceptEntity.setObservationIndexedCategoryCodingEntitySet(categoryCodingEntities);
|
||||||
|
categoryConcepts.add(categoryCodeableConceptEntity);
|
||||||
|
}
|
||||||
|
indexedObservation.setCategoryCodeableConcepts(categoryConcepts);
|
||||||
|
|
||||||
|
// Build CodeableConcept entity for Observation.Code.
|
||||||
|
CodeableConcept codeCodeableConcept = theObservation.getCode();
|
||||||
|
String observationCodeNormalizedId = null;
|
||||||
|
|
||||||
|
// Determine if a Normalized ID was created previously for Observation Code
|
||||||
|
for (Coding codeCoding : codeCodeableConcept.getCoding()) {
|
||||||
|
if (codeCoding.hasCode() && codeCoding.hasSystem()) {
|
||||||
|
observationCodeNormalizedId = myObservationIndexedCodeCodingSearchParamDao.findForCodeAndSystem(codeCoding.getCode(), codeCoding.getSystem());
|
||||||
|
} else {
|
||||||
|
observationCodeNormalizedId = myObservationIndexedCodeCodingSearchParamDao.findForDisplay(codeCoding.getDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate a new a normalized ID if necessary
|
||||||
|
if (observationCodeNormalizedId == null) {
|
||||||
|
observationCodeNormalizedId = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create/update normalized Observation Code index record
|
||||||
|
ObservationIndexedCodeCodeableConceptEntity codeableConceptField = new ObservationIndexedCodeCodeableConceptEntity(codeCodeableConcept.getText(), observationCodeNormalizedId);
|
||||||
|
for (Coding codeCoding : codeCodeableConcept.getCoding()) {
|
||||||
|
codeableConceptField.addCoding(new ObservationIndexedCodeCodingEntity(codeCoding.getSystem(), codeCoding.getCode(), codeCoding.getDisplay(), observationCodeNormalizedId));
|
||||||
|
}
|
||||||
|
myObservationIndexedCodeableConceptSearchParamDao.save(codeableConceptField);
|
||||||
|
|
||||||
|
indexedObservation.setObservationCode(codeableConceptField);
|
||||||
|
indexedObservation.setCodeNormalizedId(observationCodeNormalizedId);
|
||||||
|
myResourceIndexedObservationLastNDao.save(indexedObservation);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.entity;
|
||||||
|
|
||||||
|
import org.hibernate.search.annotations.DocumentId;
|
||||||
|
import org.hibernate.search.annotations.Field;
|
||||||
|
import org.hibernate.search.annotations.IndexedEmbedded;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public class ObservationIndexedCategoryCodeableConceptEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@DocumentId(name = "category_concept_id")
|
||||||
|
@SequenceGenerator(name = "SEQ_CATEGORY_CONCEPT", sequenceName = "SEQ_CATEGORY_CONCEPT")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CATEGORY_CONCEPT")
|
||||||
|
@Column(name = "CATEGORY_CONCEPT_ID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Field(name = "text")
|
||||||
|
@Column(name = "CODEABLE_CONCEPT_TEXT", nullable = true)
|
||||||
|
private String myCodeableConceptText;
|
||||||
|
|
||||||
|
@IndexedEmbedded(depth=2, prefix = "coding")
|
||||||
|
@OneToMany(mappedBy = "myCodeableConceptId", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||||
|
private Set<ObservationIndexedCategoryCodingEntity> myObservationIndexedCategoryCodingEntitySet;
|
||||||
|
|
||||||
|
public ObservationIndexedCategoryCodeableConceptEntity(String theCodeableConceptText) {
|
||||||
|
setCodeableConceptText(theCodeableConceptText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObservationIndexedCategoryCodingEntitySet(Set<ObservationIndexedCategoryCodingEntity> theObservationIndexedCategoryCodingEntitySet) {
|
||||||
|
myObservationIndexedCategoryCodingEntitySet = theObservationIndexedCategoryCodingEntitySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeableConceptText(String theCodeableConceptText) {
|
||||||
|
myCodeableConceptText = theCodeableConceptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.entity;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.util.CodeSystemHash;
|
||||||
|
import org.hibernate.search.annotations.Analyze;
|
||||||
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public class ObservationIndexedCategoryCodingEntity {
|
||||||
|
|
||||||
|
@Field (name = "code", analyze = Analyze.NO)
|
||||||
|
private String myCode;
|
||||||
|
@Field (name = "system", analyze = Analyze.NO)
|
||||||
|
private String mySystem;
|
||||||
|
@Field (name = "code_system_hash", analyze = Analyze.NO)
|
||||||
|
private String myCodeSystemHash;
|
||||||
|
@Field (name = "display")
|
||||||
|
private String myDisplay;
|
||||||
|
|
||||||
|
public ObservationIndexedCategoryCodingEntity(String theSystem, String theCode, String theDisplay) {
|
||||||
|
myCode = theCode;
|
||||||
|
mySystem = theSystem;
|
||||||
|
myCodeSystemHash = String.valueOf(CodeSystemHash.hashCodeSystem(theSystem, theCode));
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.entity;
|
||||||
|
|
||||||
|
import org.hibernate.search.annotations.DocumentId;
|
||||||
|
import org.hibernate.search.annotations.Field;
|
||||||
|
import org.hibernate.search.annotations.Indexed;
|
||||||
|
import org.hibernate.search.annotations.IndexedEmbedded;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Indexed(index = "code_index")
|
||||||
|
@Embeddable
|
||||||
|
@Table(name = "HFJ_SPIDX_LASTN_CODEABLE_CONCEPT")
|
||||||
|
public class ObservationIndexedCodeCodeableConceptEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@DocumentId(name = "codeable_concept_id")
|
||||||
|
@Column(name="CODEABLE_CONCEPT_ID")
|
||||||
|
private String myCodeableConceptId;
|
||||||
|
|
||||||
|
@Field(name = "text")
|
||||||
|
@Column(name = "CODEABLE_CONCEPT_TEXT", nullable = true)
|
||||||
|
private String myCodeableConceptText;
|
||||||
|
|
||||||
|
@IndexedEmbedded(depth=2, prefix = "coding")
|
||||||
|
@OneToMany(mappedBy = "myCodeableConceptId", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||||
|
private Set<ObservationIndexedCodeCodingEntity> myObservationIndexedCodeCodingEntitySet;
|
||||||
|
|
||||||
|
public ObservationIndexedCodeCodeableConceptEntity() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservationIndexedCodeCodeableConceptEntity(String theCodeableConceptText, String theCodeableConceptId) {
|
||||||
|
setCodeableConceptText(theCodeableConceptText);
|
||||||
|
setCodeableConceptId(theCodeableConceptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCoding(ObservationIndexedCodeCodingEntity theObservationIndexedCodeCodingEntity) {
|
||||||
|
if (myObservationIndexedCodeCodingEntitySet == null) {
|
||||||
|
myObservationIndexedCodeCodingEntitySet = new HashSet<>();
|
||||||
|
}
|
||||||
|
myObservationIndexedCodeCodingEntitySet.add(theObservationIndexedCodeCodingEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeableConceptId() {
|
||||||
|
return myCodeableConceptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeableConceptId(String theCodeableConceptId) {
|
||||||
|
myCodeableConceptId = theCodeableConceptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeableConceptText() {
|
||||||
|
return myCodeableConceptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeableConceptText(String theCodeableConceptText) {
|
||||||
|
myCodeableConceptText = theCodeableConceptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.entity;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.util.CodeSystemHash;
|
||||||
|
import org.hibernate.search.annotations.Analyze;
|
||||||
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Embeddable
|
||||||
|
@Table(name = "HFJ_SPIDX_LASTN_CODING")
|
||||||
|
public class ObservationIndexedCodeCodingEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@SequenceGenerator(name = "SEQ_CODING_FIELD", sequenceName = "SEQ_CODING_FIELD")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODING_FIELD")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Column(name="CODEABLE_CONCEPT_ID")
|
||||||
|
private String myCodeableConceptId;
|
||||||
|
|
||||||
|
@Field (name = "code", analyze = Analyze.NO)
|
||||||
|
private String myCode;
|
||||||
|
|
||||||
|
@Field (name = "system", analyze = Analyze.NO)
|
||||||
|
private String mySystem;
|
||||||
|
|
||||||
|
@Field (name = "code_system_hash", analyze = Analyze.NO)
|
||||||
|
private String myCodeSystemHash;
|
||||||
|
|
||||||
|
@Field (name = "display")
|
||||||
|
private String myDisplay;
|
||||||
|
|
||||||
|
public ObservationIndexedCodeCodingEntity() {}
|
||||||
|
|
||||||
|
public ObservationIndexedCodeCodingEntity(String theSystem, String theCode, String theDisplay, String theCodeableConceptId) {
|
||||||
|
myCode = theCode;
|
||||||
|
mySystem = theSystem;
|
||||||
|
myCodeSystemHash = String.valueOf(CodeSystemHash.hashCodeSystem(theSystem, theCode));
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
myCodeableConceptId = theCodeableConceptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.entity;
|
||||||
|
|
||||||
|
import org.hibernate.search.annotations.*;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "HFJ_LASTN_OBSERVATION")
|
||||||
|
@Indexed(index = "observation_index")
|
||||||
|
public class ObservationIndexedSearchParamLastNEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@SequenceGenerator(name = "SEQ_LASTN", sequenceName = "SEQ_LASTN")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_LASTN")
|
||||||
|
@Column(name = "LASTN_ID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Field(name = "subject", analyze = Analyze.NO)
|
||||||
|
@Column(name = "LASTN_SUBJECT_ID", nullable = false)
|
||||||
|
private String mySubject;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "CODEABLE_CONCEPT_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_OBSERVATION_CODE_FK"))
|
||||||
|
@IndexedEmbedded(depth = 2, prefix = "codeconcept")
|
||||||
|
private ObservationIndexedCodeCodeableConceptEntity myObservationCode;
|
||||||
|
|
||||||
|
@Field(name = "codeconceptid", analyze = Analyze.NO)
|
||||||
|
@Column(name = "CODEABLE_CONCEPT_ID", nullable = false, updatable = false, insertable = false)
|
||||||
|
private String myCodeNormalizedId;
|
||||||
|
|
||||||
|
@IndexedEmbedded(depth = 2, prefix = "categoryconcept")
|
||||||
|
@Transient
|
||||||
|
private Set<ObservationIndexedCategoryCodeableConceptEntity> myCategoryCodeableConcepts;
|
||||||
|
|
||||||
|
@Field(name = "effectivedtm", analyze = Analyze.NO)
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "LASTN_EFFECTIVE_DATETIME", nullable = true)
|
||||||
|
private Date myEffectiveDtm;
|
||||||
|
|
||||||
|
@DocumentId(name = "identifier")
|
||||||
|
@Column(name = "RESOURCE_IDENTIFIER", nullable = false)
|
||||||
|
private String myIdentifier;
|
||||||
|
|
||||||
|
public ObservationIndexedSearchParamLastNEntity() {}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return mySubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String theSubject) {
|
||||||
|
mySubject = theSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return myIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentifier(String theIdentifier) {
|
||||||
|
myIdentifier = theIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEffectiveDtm(Date theEffectiveDtm) {
|
||||||
|
myEffectiveDtm = theEffectiveDtm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEffectiveDtm() {
|
||||||
|
return myEffectiveDtm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeNormalizedId(String theCodeNormalizedId) {
|
||||||
|
myCodeNormalizedId = theCodeNormalizedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeNormalizedId() {
|
||||||
|
return myCodeNormalizedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setObservationCode(ObservationIndexedCodeCodeableConceptEntity theObservationCode) {
|
||||||
|
myObservationCode = theObservationCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryCodeableConcepts(Set<ObservationIndexedCategoryCodeableConceptEntity> theCategoryCodeableConcepts) {
|
||||||
|
myCategoryCodeableConcepts = theCategoryCodeableConcepts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.HashFunction;
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
|
||||||
|
public class CodeSystemHash {
|
||||||
|
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
||||||
|
private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
|
static public long hashCodeSystem( String system, String code ) {
|
||||||
|
Hasher hasher = HASH_FUNCTION.newHasher();
|
||||||
|
addStringToHasher(hasher, system);
|
||||||
|
addStringToHasher(hasher, code);
|
||||||
|
|
||||||
|
HashCode hashCode = hasher.hash();
|
||||||
|
return hashCode.asLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void addStringToHasher(Hasher hasher, String next) {
|
||||||
|
if (next == null) {
|
||||||
|
hasher.putByte((byte) 0);
|
||||||
|
} else {
|
||||||
|
next = UrlUtil.escapeUrlParam(next);
|
||||||
|
byte[] bytes = next.getBytes(Charsets.UTF_8);
|
||||||
|
hasher.putBytes(bytes);
|
||||||
|
}
|
||||||
|
hasher.putBytes(DELIMITER_BYTES);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class FhirResourceDaoObservationR4 extends BaseHapiFhirResourceDao<Observation> {
|
||||||
|
|
||||||
|
private IBundleProvider doLastNOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
|
||||||
|
SearchParameterMap paramMap = new SearchParameterMap();
|
||||||
|
if (theCount != null) {
|
||||||
|
paramMap.setCount(theCount.getValue());
|
||||||
|
}
|
||||||
|
if (theContent != null) {
|
||||||
|
paramMap.add(Constants.PARAM_CONTENT, theContent);
|
||||||
|
}
|
||||||
|
if (theNarrative != null) {
|
||||||
|
paramMap.add(Constants.PARAM_TEXT, theNarrative);
|
||||||
|
}
|
||||||
|
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
|
||||||
|
paramMap.setEverythingMode(theId != null ? EverythingModeEnum.PATIENT_INSTANCE : EverythingModeEnum.PATIENT_TYPE);
|
||||||
|
paramMap.setSort(theSort);
|
||||||
|
paramMap.setLastUpdated(theLastUpdated);
|
||||||
|
if (theId != null) {
|
||||||
|
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPagingProviderDatabaseBacked(theRequest)) {
|
||||||
|
paramMap.setLoadSynchronous(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBundleProvider observationsLastN(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||||
|
return doLastNOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||||
|
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
||||||
|
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
|
||||||
|
|
||||||
|
if(thePerformIndexing) {
|
||||||
|
// Update indexes here for LastN operation.
|
||||||
|
Observation observation = (Observation)theResource;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
/*
|
||||||
|
import org.shadehapi.elasticsearch.action.DocWriteRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.bulk.BulkItemResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.bulk.BulkRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.bulk.BulkResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.index.IndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.client.RequestOptions;
|
||||||
|
import org.shadehapi.elasticsearch.client.RestHighLevelClient;
|
||||||
|
import org.shadehapi.elasticsearch.common.xcontent.XContentType;
|
||||||
|
*/
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ElasticsearchBulkIndexSvcImpl {
|
||||||
|
|
||||||
|
// RestHighLevelClient myRestHighLevelClient;
|
||||||
|
|
||||||
|
// BulkRequest myBulkRequest = null;
|
||||||
|
|
||||||
|
public ElasticsearchBulkIndexSvcImpl(String theHostname, int thePort, String theUsername, String thePassword) {
|
||||||
|
|
||||||
|
// myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(theHostname, thePort, theUsername,thePassword);
|
||||||
|
|
||||||
|
try {
|
||||||
|
createObservationIndexIfMissing();
|
||||||
|
createCodeIndexIfMissing();
|
||||||
|
} catch (IOException theE) {
|
||||||
|
throw new RuntimeException("Failed to create document index", theE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createObservationIndexIfMissing() throws IOException {
|
||||||
|
if(indexExists(IndexConstants.OBSERVATION_INDEX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String observationMapping = "{\n" +
|
||||||
|
" \"mappings\" : {\n" +
|
||||||
|
" \"ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity\" : {\n" +
|
||||||
|
" \"properties\" : {\n" +
|
||||||
|
" \"codeconceptid\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"norms\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconcepttext\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconcepttext\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"effectivedtm\" : {\n" +
|
||||||
|
" \"type\" : \"date\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"identifier\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"store\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"subject\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
if(!createIndex(IndexConstants.OBSERVATION_INDEX, observationMapping)) {
|
||||||
|
throw new RuntimeException("Failed to create observation index");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createCodeIndexIfMissing() throws IOException {
|
||||||
|
if(indexExists(IndexConstants.CODE_INDEX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String codeMapping = "{\n" +
|
||||||
|
" \"mappings\" : {\n" +
|
||||||
|
" \"ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity\" : {\n" +
|
||||||
|
" \"properties\" : {\n" +
|
||||||
|
" \"codeable_concept_id\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"store\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeable_concept_text\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
if (!createIndex(IndexConstants.CODE_INDEX, codeMapping)) {
|
||||||
|
throw new RuntimeException("Failed to create code index");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createIndex(String theIndexName, String theMapping) throws IOException {
|
||||||
|
/* CreateIndexRequest request = new CreateIndexRequest(theIndexName);
|
||||||
|
request.source(theMapping, XContentType.JSON);
|
||||||
|
CreateIndexResponse createIndexResponse = myRestHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
|
||||||
|
return createIndexResponse.isAcknowledged();
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean indexExists(String theIndexName) throws IOException {
|
||||||
|
/* GetIndexRequest request = new GetIndexRequest();
|
||||||
|
request.indices(theIndexName);
|
||||||
|
return myRestHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToBulkIndexRequest(String theIndexName, String theDocumentId, String theObservationDocument, String theDocumentType) {
|
||||||
|
/* IndexRequest request = new IndexRequest(theIndexName);
|
||||||
|
request.id(theDocumentId);
|
||||||
|
request.type(theDocumentType);
|
||||||
|
|
||||||
|
request.source(theObservationDocument, XContentType.JSON);
|
||||||
|
|
||||||
|
if (myBulkRequest == null) {
|
||||||
|
myBulkRequest = new BulkRequest();
|
||||||
|
}
|
||||||
|
myBulkRequest.add(request); */
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeBulkIndex() throws IOException {
|
||||||
|
/* if (myBulkRequest == null) {
|
||||||
|
throw new RuntimeException(("No index requests have been added to the bulk request"));
|
||||||
|
}
|
||||||
|
BulkResponse bulkResponse = myRestHighLevelClient.bulk(myBulkRequest, RequestOptions.DEFAULT);
|
||||||
|
for (BulkItemResponse bulkItemResponse : bulkResponse) {
|
||||||
|
if (bulkItemResponse.getOpType() != DocWriteRequest.OpType.CREATE && bulkItemResponse.getOpType() != DocWriteRequest.OpType.INDEX) {
|
||||||
|
throw new RuntimeException("Unexpected response for bulk index request: " + bulkItemResponse.getOpType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myBulkRequest = null; */
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean bulkRequestPending() {
|
||||||
|
// return (myBulkRequest != null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeClient() throws IOException {
|
||||||
|
// myRestHighLevelClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.auth.AuthScope;
|
||||||
|
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||||
|
import org.apache.http.client.CredentialsProvider;
|
||||||
|
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.shadehapi.elasticsearch.client.RestClient;
|
||||||
|
import org.shadehapi.elasticsearch.client.RestClientBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.client.RestHighLevelClient;
|
||||||
|
|
||||||
|
public class ElasticsearchRestClientFactory {
|
||||||
|
|
||||||
|
static public RestHighLevelClient createElasticsearchHighLevelRestClient(String theHostname, int thePort, String theUsername, String thePassword) {
|
||||||
|
final CredentialsProvider credentialsProvider =
|
||||||
|
new BasicCredentialsProvider();
|
||||||
|
credentialsProvider.setCredentials(AuthScope.ANY,
|
||||||
|
new UsernamePasswordCredentials(theUsername, thePassword));
|
||||||
|
|
||||||
|
RestClientBuilder clientBuilder = RestClient.builder(
|
||||||
|
new HttpHost(theHostname, thePort))
|
||||||
|
.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
|
||||||
|
.setDefaultCredentialsProvider(credentialsProvider));
|
||||||
|
|
||||||
|
Header[] defaultHeaders = new Header[]{new BasicHeader("Content-Type", "application/json")};
|
||||||
|
clientBuilder.setDefaultHeaders(defaultHeaders);
|
||||||
|
|
||||||
|
return new RestHighLevelClient(clientBuilder);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,550 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.CodeSystemHash;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
//import org.assertj.core.util.VisibleForTesting;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.DocWriteResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.index.IndexRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.index.IndexResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.shadehapi.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||||
|
import org.shadehapi.elasticsearch.client.RequestOptions;
|
||||||
|
import org.shadehapi.elasticsearch.client.RestHighLevelClient;
|
||||||
|
import org.shadehapi.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.shadehapi.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.index.query.QueryBuilders;
|
||||||
|
import org.shadehapi.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||||
|
import org.shadehapi.elasticsearch.search.SearchHit;
|
||||||
|
import org.shadehapi.elasticsearch.search.SearchHits;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.AggregationBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.AggregationBuilders;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.Aggregations;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.composite.*;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.metrics.tophits.ParsedTopHits;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.support.ValueType;
|
||||||
|
import org.shadehapi.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.sort.SortOrder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
//@Component
|
||||||
|
public class ElasticsearchSvcImpl implements IElasticsearchSvc {
|
||||||
|
|
||||||
|
RestHighLevelClient myRestHighLevelClient;
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
|
||||||
|
public ElasticsearchSvcImpl(String theHostname, int thePort, String theUsername, String thePassword) {
|
||||||
|
|
||||||
|
myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(theHostname, thePort, theUsername,thePassword);
|
||||||
|
|
||||||
|
try {
|
||||||
|
createObservationIndexIfMissing();
|
||||||
|
createCodeIndexIfMissing();
|
||||||
|
} catch (IOException theE) {
|
||||||
|
throw new RuntimeException("Failed to create document index", theE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createObservationIndexIfMissing() throws IOException {
|
||||||
|
if(indexExists(IndexConstants.OBSERVATION_INDEX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String observationMapping = "{\n" +
|
||||||
|
" \"mappings\" : {\n" +
|
||||||
|
" \"ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity\" : {\n" +
|
||||||
|
" \"properties\" : {\n" +
|
||||||
|
" \"codeconceptid\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"norms\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconcepttext\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeconceptcodingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconcepttext\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"categoryconceptcodingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"effectivedtm\" : {\n" +
|
||||||
|
" \"type\" : \"date\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"identifier\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"store\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"subject\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
if(!createIndex(IndexConstants.OBSERVATION_INDEX, observationMapping)) {
|
||||||
|
throw new RuntimeException("Failed to create observation index");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createCodeIndexIfMissing() throws IOException {
|
||||||
|
if(indexExists(IndexConstants.CODE_INDEX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String codeMapping = "{\n" +
|
||||||
|
" \"mappings\" : {\n" +
|
||||||
|
" \"ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity\" : {\n" +
|
||||||
|
" \"properties\" : {\n" +
|
||||||
|
" \"codeable_concept_id\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\",\n" +
|
||||||
|
" \"store\" : true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codeable_concept_text\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingcode\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingcode_system_hash\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingdisplay\" : {\n" +
|
||||||
|
" \"type\" : \"text\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"codingsystem\" : {\n" +
|
||||||
|
" \"type\" : \"keyword\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
if (!createIndex(IndexConstants.CODE_INDEX, codeMapping)) {
|
||||||
|
throw new RuntimeException("Failed to create code index");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createIndex(String theIndexName, String theMapping) throws IOException {
|
||||||
|
CreateIndexRequest request = new CreateIndexRequest(theIndexName);
|
||||||
|
request.source(theMapping, XContentType.JSON);
|
||||||
|
CreateIndexResponse createIndexResponse = myRestHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
|
||||||
|
return createIndexResponse.isAcknowledged();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean performIndex(String theIndexName, String theDocumentId, String theIndexDocument, String theDocumentType) throws IOException {
|
||||||
|
|
||||||
|
IndexResponse indexResponse = myRestHighLevelClient.index(createIndexRequest(theIndexName, theDocumentId,theIndexDocument,theDocumentType),
|
||||||
|
RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
return (indexResponse.getResult() == DocWriteResponse.Result.CREATED) || (indexResponse.getResult() == DocWriteResponse.Result.UPDATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexRequest createIndexRequest(String theIndexName, String theDocumentId, String theObservationDocument, String theDocumentType) {
|
||||||
|
IndexRequest request = new IndexRequest(theIndexName);
|
||||||
|
request.id(theDocumentId);
|
||||||
|
request.type(theDocumentType);
|
||||||
|
|
||||||
|
request.source(theObservationDocument, XContentType.JSON);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean indexExists(String theIndexName) throws IOException {
|
||||||
|
GetIndexRequest request = new GetIndexRequest();
|
||||||
|
request.indices(theIndexName);
|
||||||
|
return myRestHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchRequest buildObservationCodesSearchRequest(int theMaxResultSetSize) {
|
||||||
|
SearchRequest searchRequest = new SearchRequest(IndexConstants.CODE_INDEX);
|
||||||
|
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||||
|
// Query
|
||||||
|
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
|
||||||
|
searchSourceBuilder.size(theMaxResultSetSize);
|
||||||
|
searchRequest.source(searchSourceBuilder);
|
||||||
|
return searchRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompositeAggregationBuilder createCompositeAggregationBuilder(int theMaximumResultSetSize, int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
|
||||||
|
TermsAggregationBuilder observationCodeAggregationBuilder = new TermsAggregationBuilder("group_by_code", ValueType.STRING).field("codeconceptid");
|
||||||
|
// Top Hits Aggregation
|
||||||
|
observationCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
|
||||||
|
.sort("effectivedtm", SortOrder.DESC)
|
||||||
|
.fetchSource(theTopHitsInclude, null).size(theMaxNumberObservationsPerCode));
|
||||||
|
observationCodeAggregationBuilder.size(theMaximumResultSetSize);
|
||||||
|
CompositeValuesSourceBuilder<?> subjectValuesBuilder = new TermsValuesSourceBuilder("subject").field("subject");
|
||||||
|
List<CompositeValuesSourceBuilder<?>> compositeAggSubjectSources = new ArrayList();
|
||||||
|
compositeAggSubjectSources.add(subjectValuesBuilder);
|
||||||
|
CompositeAggregationBuilder compositeAggregationSubjectBuilder = new CompositeAggregationBuilder("group_by_subject", compositeAggSubjectSources);
|
||||||
|
compositeAggregationSubjectBuilder.subAggregation(observationCodeAggregationBuilder);
|
||||||
|
|
||||||
|
return compositeAggregationSubjectBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TermsAggregationBuilder createTermsAggregationBuilder(int theMaximumResultSetSize, int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
|
||||||
|
TermsAggregationBuilder observationCodeAggregationBuilder = new TermsAggregationBuilder("group_by_code", ValueType.STRING).field("codeconceptid");
|
||||||
|
// Top Hits Aggregation
|
||||||
|
observationCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
|
||||||
|
.sort("effectivedtm", SortOrder.DESC).fetchSource(theTopHitsInclude, null).size(theMaxNumberObservationsPerCode));
|
||||||
|
observationCodeAggregationBuilder.size(theMaximumResultSetSize);
|
||||||
|
TermsAggregationBuilder subjectsBuilder = new TermsAggregationBuilder("group_by_subject", ValueType.STRING).field("subject");
|
||||||
|
subjectsBuilder.subAggregation(observationCodeAggregationBuilder);
|
||||||
|
subjectsBuilder.size(theMaximumResultSetSize);
|
||||||
|
return subjectsBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException {
|
||||||
|
return myRestHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ObservationJson> buildObservationCompositeResults(SearchResponse theSearchResponse) throws IOException {
|
||||||
|
Aggregations responseAggregations = theSearchResponse.getAggregations();
|
||||||
|
ParsedComposite aggregatedSubjects = responseAggregations.get("group_by_subject");
|
||||||
|
List<ParsedComposite.ParsedBucket> subjectBuckets = aggregatedSubjects.getBuckets();
|
||||||
|
List<ObservationJson> codes = new ArrayList<>();
|
||||||
|
for(ParsedComposite.ParsedBucket subjectBucket : subjectBuckets) {
|
||||||
|
Aggregations observationCodeAggregations = subjectBucket.getAggregations();
|
||||||
|
ParsedTerms aggregatedObservationCodes = observationCodeAggregations.get("group_by_code");
|
||||||
|
List<? extends Terms.Bucket> observationCodeBuckets = aggregatedObservationCodes.getBuckets();
|
||||||
|
for (Terms.Bucket observationCodeBucket : observationCodeBuckets) {
|
||||||
|
Aggregations topHitObservationCodes = observationCodeBucket.getAggregations();
|
||||||
|
ParsedTopHits parsedTopHits = topHitObservationCodes.get("most_recent_effective");
|
||||||
|
SearchHit[] topHits = parsedTopHits.getHits().getHits();
|
||||||
|
for (SearchHit topHit : topHits) {
|
||||||
|
String sources = topHit.getSourceAsString();
|
||||||
|
ObservationJson code = objectMapper.readValue(sources, ObservationJson.class);
|
||||||
|
codes.add(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ObservationJson> buildObservationTermsResults(SearchResponse theSearchResponse) throws IOException {
|
||||||
|
Aggregations responseAggregations = theSearchResponse.getAggregations();
|
||||||
|
ParsedTerms aggregatedSubjects = responseAggregations.get("group_by_subject");
|
||||||
|
List<? extends Terms.Bucket> subjectBuckets = aggregatedSubjects.getBuckets();
|
||||||
|
List<ObservationJson> codes = new ArrayList<>();
|
||||||
|
for (Terms.Bucket subjectBucket : subjectBuckets) {
|
||||||
|
Aggregations observationCodeAggregations = subjectBucket.getAggregations();
|
||||||
|
ParsedTerms aggregatedObservationCodes = observationCodeAggregations.get("group_by_code");
|
||||||
|
List<? extends Terms.Bucket> observationCodeBuckets = aggregatedObservationCodes.getBuckets();
|
||||||
|
for (Terms.Bucket observationCodeBucket : observationCodeBuckets) {
|
||||||
|
Aggregations topHitObservationCodes = observationCodeBucket.getAggregations();
|
||||||
|
ParsedTopHits parsedTopHits = topHitObservationCodes.get("most_recent_effective");
|
||||||
|
SearchHit[] topHits = parsedTopHits.getHits().getHits();
|
||||||
|
for (SearchHit topHit : topHits) {
|
||||||
|
String sources = topHit.getSourceAsString();
|
||||||
|
ObservationJson code = objectMapper.readValue(sources,ObservationJson.class);
|
||||||
|
codes.add(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CodeJson> buildCodeResult(SearchResponse theSearchResponse) throws JsonProcessingException {
|
||||||
|
SearchHits codeHits = theSearchResponse.getHits();
|
||||||
|
List<CodeJson> codes = new ArrayList<>();
|
||||||
|
for (SearchHit codeHit : codeHits) {
|
||||||
|
CodeJson code = objectMapper.readValue(codeHit.getSourceAsString(), CodeJson.class);
|
||||||
|
codes.add(code);
|
||||||
|
}
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
public SearchRequest buildObservationAllFieldsCompositeSearchRequest(int maxResultSetSize, SearchParameterMap theSearchParameterMap, int theMaxObservationsPerCode) {
|
||||||
|
|
||||||
|
return buildObservationsSearchRequest(theSearchParameterMap, createCompositeAggregationBuilder(maxResultSetSize, theMaxObservationsPerCode, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchRequest buildObservationCompositeSearchRequest(int maxResultSetSize, SearchParameterMap theSearchParameterMap, int theMaxObservationsPerCode) {
|
||||||
|
// Return only identifiers
|
||||||
|
String[] topHitsInclude = {"identifier","subject","effectivedtm","codeconceptid"};
|
||||||
|
return buildObservationsSearchRequest(theSearchParameterMap, createCompositeAggregationBuilder(maxResultSetSize, theMaxObservationsPerCode, topHitsInclude));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
public SearchRequest buildObservationAllFieldsTermsSearchRequest(int maxResultSetSize, SearchParameterMap theSearchParameterMap, int theMaxObservationsPerCode) {
|
||||||
|
return buildObservationsSearchRequest(theSearchParameterMap, createTermsAggregationBuilder(maxResultSetSize, theMaxObservationsPerCode, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchRequest buildObservationTermsSearchRequest(int maxResultSetSize, SearchParameterMap theSearchParameterMap, int theMaxObservationsPerCode) {
|
||||||
|
// Return only identifiers
|
||||||
|
String[] topHitsInclude = {"identifier","subject","effectivedtm","codeconceptid"};
|
||||||
|
return buildObservationsSearchRequest(theSearchParameterMap, createTermsAggregationBuilder(maxResultSetSize, theMaxObservationsPerCode, topHitsInclude));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SearchRequest buildObservationsSearchRequest(SearchParameterMap theSearchParameterMap, AggregationBuilder theAggregationBuilder) {
|
||||||
|
SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||||
|
// Query
|
||||||
|
if(!searchParamsHaveLastNCriteria(theSearchParameterMap)) {
|
||||||
|
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
|
||||||
|
} else {
|
||||||
|
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
|
addSubjectsCriteria(boolQueryBuilder,theSearchParameterMap);
|
||||||
|
addCategoriesCriteria(boolQueryBuilder,theSearchParameterMap);
|
||||||
|
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap);
|
||||||
|
searchSourceBuilder.query(boolQueryBuilder);
|
||||||
|
}
|
||||||
|
searchSourceBuilder.size(0);
|
||||||
|
|
||||||
|
// Aggregation by order codes
|
||||||
|
searchSourceBuilder.aggregation(theAggregationBuilder);
|
||||||
|
searchRequest.source(searchSourceBuilder);
|
||||||
|
|
||||||
|
return searchRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean searchParamsHaveLastNCriteria(SearchParameterMap theSearchParameterMap) {
|
||||||
|
return theSearchParameterMap != null &&
|
||||||
|
(theSearchParameterMap.containsKey("patient") || theSearchParameterMap.containsKey("subject") ||
|
||||||
|
theSearchParameterMap.containsKey("category") || theSearchParameterMap.containsKey("code"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSubjectsCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
|
||||||
|
if (theSearchParameterMap.containsKey("patient") || theSearchParameterMap.containsKey("subject")) {
|
||||||
|
ArrayList<String> subjectReferenceCriteria = new ArrayList<>();
|
||||||
|
List<List<IQueryParameterType>> andOrParams = new ArrayList<>();
|
||||||
|
if (theSearchParameterMap.get("patient") != null) {
|
||||||
|
andOrParams.addAll(theSearchParameterMap.get("patient"));
|
||||||
|
}
|
||||||
|
if (theSearchParameterMap.get("subject") != null) {
|
||||||
|
andOrParams.addAll(theSearchParameterMap.get("subject"));
|
||||||
|
}
|
||||||
|
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
|
||||||
|
subjectReferenceCriteria.addAll(getReferenceValues(nextAnd));
|
||||||
|
}
|
||||||
|
if (subjectReferenceCriteria.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("subject", subjectReferenceCriteria));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getReferenceValues(List<? extends IQueryParameterType> referenceParams) {
|
||||||
|
ArrayList<String> referenceList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (IQueryParameterType nextOr : referenceParams) {
|
||||||
|
|
||||||
|
if (nextOr instanceof ReferenceParam) {
|
||||||
|
ReferenceParam ref = (ReferenceParam) nextOr;
|
||||||
|
if (isBlank(ref.getChain())) {
|
||||||
|
if (ref.getResourceType() != null) {
|
||||||
|
referenceList.add(ref.getResourceType() + "/" + ref.getValue());
|
||||||
|
} else {
|
||||||
|
referenceList.add(ref.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + nextOr.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return referenceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCategoriesCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
|
||||||
|
if (theSearchParameterMap.containsKey("category")) {
|
||||||
|
ArrayList<String> codeSystemHashList = new ArrayList<>();
|
||||||
|
ArrayList<String> codeOnlyList = new ArrayList<>();
|
||||||
|
ArrayList<String> systemOnlyList = new ArrayList<>();
|
||||||
|
ArrayList<String> textOnlyList = new ArrayList<>();
|
||||||
|
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get("category");
|
||||||
|
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
|
||||||
|
codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd));
|
||||||
|
codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd));
|
||||||
|
systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd));
|
||||||
|
textOnlyList.addAll(getCodingTextOnlyValues(nextAnd));
|
||||||
|
}
|
||||||
|
if (codeSystemHashList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("categoryconceptcodingcode_system_hash", codeSystemHashList));
|
||||||
|
}
|
||||||
|
if (codeOnlyList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("categoryconceptcodingcode", codeOnlyList));
|
||||||
|
}
|
||||||
|
if (systemOnlyList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("categoryconceptcodingsystem", systemOnlyList));
|
||||||
|
}
|
||||||
|
if (textOnlyList.size() > 0) {
|
||||||
|
BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
|
for (String textOnlyParam : textOnlyList) {
|
||||||
|
myTextBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("categoryconceptcodingdisplay", textOnlyParam));
|
||||||
|
myTextBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("categoryconcepttext", textOnlyParam));
|
||||||
|
}
|
||||||
|
theBoolQueryBuilder.must(myTextBoolQueryBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCodingCodeSystemValues(List<? extends IQueryParameterType> codeParams) {
|
||||||
|
ArrayList<String> codeSystemHashList = new ArrayList<>();
|
||||||
|
for (IQueryParameterType nextOr : codeParams ) {
|
||||||
|
if (nextOr instanceof TokenParam) {
|
||||||
|
TokenParam ref = (TokenParam) nextOr;
|
||||||
|
if (ref.getSystem() != null && ref.getValue() != null) {
|
||||||
|
codeSystemHashList.add(String.valueOf(CodeSystemHash.hashCodeSystem(ref.getSystem(), ref.getValue())));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid token type (expecting TokenParam): " + nextOr.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codeSystemHashList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCodingCodeOnlyValues(List<? extends IQueryParameterType> codeParams ) {
|
||||||
|
ArrayList<String> codeOnlyList = new ArrayList<>();
|
||||||
|
for (IQueryParameterType nextOr : codeParams) {
|
||||||
|
|
||||||
|
if (nextOr instanceof TokenParam) {
|
||||||
|
TokenParam ref = (TokenParam) nextOr;
|
||||||
|
if (ref.getValue() != null && ref.getSystem() == null && !ref.isText()) {
|
||||||
|
codeOnlyList.add(ref.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid token type (expecting TokenParam): " + nextOr.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codeOnlyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCodingSystemOnlyValues(List<? extends IQueryParameterType> codeParams ) {
|
||||||
|
ArrayList<String> systemOnlyList = new ArrayList<>();
|
||||||
|
for (IQueryParameterType nextOr : codeParams) {
|
||||||
|
|
||||||
|
if (nextOr instanceof TokenParam) {
|
||||||
|
TokenParam ref = (TokenParam) nextOr;
|
||||||
|
if (ref.getValue() == null && ref.getSystem() != null) {
|
||||||
|
systemOnlyList.add(ref.getSystem());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid token type (expecting TokenParam): " + nextOr.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return systemOnlyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCodingTextOnlyValues(List<? extends IQueryParameterType> codeParams ) {
|
||||||
|
ArrayList<String> textOnlyList = new ArrayList<>();
|
||||||
|
for (IQueryParameterType nextOr : codeParams ) {
|
||||||
|
|
||||||
|
if (nextOr instanceof TokenParam) {
|
||||||
|
TokenParam ref = (TokenParam) nextOr;
|
||||||
|
if (ref.isText() && ref.getValue() != null) {
|
||||||
|
textOnlyList.add(ref.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid token type (expecting TokenParam): " + nextOr.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textOnlyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addObservationCodeCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
|
||||||
|
if (theSearchParameterMap.containsKey("code")) {
|
||||||
|
ArrayList<String> codeSystemHashList = new ArrayList<>();
|
||||||
|
ArrayList<String> codeOnlyList = new ArrayList<>();
|
||||||
|
ArrayList<String> systemOnlyList = new ArrayList<>();
|
||||||
|
ArrayList<String> textOnlyList = new ArrayList<>();
|
||||||
|
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get("code");
|
||||||
|
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
|
||||||
|
codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd));
|
||||||
|
codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd));
|
||||||
|
systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd));
|
||||||
|
textOnlyList.addAll(getCodingTextOnlyValues(nextAnd));
|
||||||
|
}
|
||||||
|
if (codeSystemHashList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("codeconceptcodingcode_system_hash", codeSystemHashList));
|
||||||
|
}
|
||||||
|
if (codeOnlyList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("codeconceptcodingcode", codeOnlyList));
|
||||||
|
}
|
||||||
|
if (systemOnlyList.size() > 0) {
|
||||||
|
theBoolQueryBuilder.must(QueryBuilders.termsQuery("codeconceptcodingsystem", systemOnlyList));
|
||||||
|
}
|
||||||
|
if (textOnlyList.size() > 0) {
|
||||||
|
BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
|
for (String textOnlyParam : textOnlyList) {
|
||||||
|
myTextBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("codeconceptcodingdisplay", textOnlyParam));
|
||||||
|
myTextBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("codeconcepttext", textOnlyParam));
|
||||||
|
}
|
||||||
|
theBoolQueryBuilder.must(myTextBoolQueryBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
public boolean deleteIndex(String theIndexName) throws IOException {
|
||||||
|
DeleteIndexRequest request = new DeleteIndexRequest(theIndexName);
|
||||||
|
AcknowledgedResponse deleteIndexResponse = myRestHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
return deleteIndexResponse.isAcknowledged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
public void deleteAllDocuments(String theIndexName) throws IOException {
|
||||||
|
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(theIndexName);
|
||||||
|
deleteByQueryRequest.setQuery(QueryBuilders.matchAllQuery());
|
||||||
|
myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @Override
|
||||||
|
public IBundleProvider lastN(HttpServletRequest theServletRequest, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBundleProvider uniqueCodes(HttpServletRequest theServletRequest, RequestDetails theRequestDetails) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
//import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
|
||||||
|
public interface IElasticsearchSvc {
|
||||||
|
|
||||||
|
|
||||||
|
// IBundleProvider lastN(javax.servlet.http.HttpServletRequest theServletRequest, ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
// IBundleProvider uniqueCodes(javax.servlet.http.HttpServletRequest theServletRequest, ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
public class IndexConstants {
|
||||||
|
|
||||||
|
public static final String OBSERVATION_INDEX = "observation_index";
|
||||||
|
public static final String CODE_INDEX = "code_index";
|
||||||
|
public static final String OBSERVATION_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity";
|
||||||
|
public static final String CODE_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity";
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IdJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IndexJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MulticodeObservationsIntoElasticSearch {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MulticodeObservationsIntoElasticSearch.class);
|
||||||
|
|
||||||
|
private static final ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] theArgs) {
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 10 ; patientCount++) {
|
||||||
|
|
||||||
|
String subject = "Patient/"+UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 10 ; entryCount++ ) {
|
||||||
|
String nextResourceId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
IdJson id = new IdJson(nextResourceId);
|
||||||
|
IndexJson documentIndex = new IndexJson(id);
|
||||||
|
|
||||||
|
ObservationJson observationDocument = new ObservationJson();
|
||||||
|
observationDocument.setIdentifier(nextResourceId);
|
||||||
|
observationDocument.setSubject(subject);
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> category = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
Coding categoryCoding1_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test heart-rate");
|
||||||
|
Coding categoryCoding1_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test alternate heart-rate");
|
||||||
|
Coding categoryCoding1_3 = new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test second alternate heart-rate");
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept1.getCoding().add(categoryCoding1_1);
|
||||||
|
categoryCodeableConcept1.getCoding().add(categoryCoding1_2);
|
||||||
|
categoryCodeableConcept1.getCoding().add(categoryCoding1_3);
|
||||||
|
categoryCodeableConcept1.setText("Heart Rate Codeable Concept");
|
||||||
|
category.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
Coding categoryCoding2_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test vital signs");
|
||||||
|
Coding categoryCoding2_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test alternate vital signs");
|
||||||
|
Coding categoryCoding2_3 = new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test second alternate vital signs");
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept2.getCoding().add(categoryCoding2_1);
|
||||||
|
categoryCodeableConcept2.getCoding().add(categoryCoding2_2);
|
||||||
|
categoryCodeableConcept2.getCoding().add(categoryCoding2_3);
|
||||||
|
categoryCodeableConcept2.setText("Vital Signs Codeable Concept");
|
||||||
|
category.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
Coding categoryCoding3_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-vitals-panel", "test vital signs panel");
|
||||||
|
Coding categoryCoding3_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test alternate vital signs panel");
|
||||||
|
Coding categoryCoding3_3 = new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals-panel", "test second alternate vital signs panel");
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept3.getCoding().add(categoryCoding3_1);
|
||||||
|
categoryCodeableConcept3.getCoding().add(categoryCoding3_2);
|
||||||
|
categoryCodeableConcept3.getCoding().add(categoryCoding3_3);
|
||||||
|
categoryCodeableConcept3.setText("Vital Signs Panel Codeable Concept");
|
||||||
|
category.add(categoryCodeableConcept3);
|
||||||
|
observationDocument.setCategories(category);
|
||||||
|
|
||||||
|
Coding codeCoding1 = new Coding("http://mycodes.org/fhir/observation-code", "test-code", "test observation code");
|
||||||
|
Coding codeCoding2 = new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code", "test observation code");
|
||||||
|
Coding codeCoding3 = new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code", "test observation code");
|
||||||
|
CodeableConcept code = new CodeableConcept();
|
||||||
|
code.getCoding().add(codeCoding1);
|
||||||
|
code.getCoding().add(codeCoding2);
|
||||||
|
code.getCoding().add(codeCoding3);
|
||||||
|
code.setText("Observation code CodeableConcept");
|
||||||
|
observationDocument.setCode(code);
|
||||||
|
observationDocument.setCode_concept_id("multicode_test_normalized_code");
|
||||||
|
|
||||||
|
Date effectiveDtm = new Date();
|
||||||
|
observationDocument.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
|
||||||
|
File outputFile = new File("Observations_multiplecodes.json");
|
||||||
|
try {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(outputFile, true);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, documentIndex);
|
||||||
|
stringWriter.append('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, observationDocument);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, documentIndex);
|
||||||
|
outputStream.write('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, observationDocument);
|
||||||
|
outputStream.write('\n');
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Upload complete");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchBulkIndexSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.IndexConstants;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.SimpleStopWatch;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class OneMillionPatientsIntoElasticSearch {
|
||||||
|
// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OneMillionPatientsIntoElasticSearch.class);
|
||||||
|
|
||||||
|
private static final ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] theArgs) throws IOException {
|
||||||
|
|
||||||
|
ElasticsearchBulkIndexSvcImpl elasticsearchSvc = new ElasticsearchBulkIndexSvcImpl("localhost",9301, "elastic", "changeme");
|
||||||
|
try {
|
||||||
|
|
||||||
|
SimpleStopWatch stopwatch = new SimpleStopWatch();
|
||||||
|
|
||||||
|
List<String> observationCodeIds = new ArrayList<>();
|
||||||
|
List<CodeableConcept> observationCodes = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int codeCount = 0; codeCount < 1000; codeCount++) {
|
||||||
|
String code = RandomStringUtils.random(10,true,true);
|
||||||
|
Coding codeCoding = new Coding("http://mycodes.org/fhir/observation-code", code, "test observation code " + code);
|
||||||
|
CodeableConcept codeConcept = new CodeableConcept();
|
||||||
|
codeConcept.getCoding().add(codeCoding);
|
||||||
|
codeConcept.setText("test observation code concept " + code);
|
||||||
|
String codeableConceptId = UUID.randomUUID().toString();
|
||||||
|
observationCodeIds.add(codeableConceptId);
|
||||||
|
observationCodes.add(codeConcept);
|
||||||
|
CodeJson codeDocument = new CodeJson(codeConcept, codeableConceptId);
|
||||||
|
String printedObservationDocument = ourMapperNonPrettyPrint.writeValueAsString(codeDocument);
|
||||||
|
System.out.println(printedObservationDocument);
|
||||||
|
System.out.println(codeableConceptId);
|
||||||
|
elasticsearchSvc.addToBulkIndexRequest("code_index", codeableConceptId, printedObservationDocument, IndexConstants.CODE_DOCUMENT_TYPE);
|
||||||
|
if ((codeCount+1)%250 == 0) {
|
||||||
|
elasticsearchSvc.executeBulkIndex();
|
||||||
|
long elapsedTime = stopwatch.getElapsedTime();
|
||||||
|
stopwatch.restart();
|
||||||
|
System.out.println("Elapsed processing time = " + elapsedTime/1000 + "s");
|
||||||
|
System.out.println("Average processing time/code = " + elapsedTime/5000 + "ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 1000000 ; patientCount++) {
|
||||||
|
String subject = "Patient/"+UUID.randomUUID().toString();
|
||||||
|
ArrayList<CodeableConcept> observationCodesSubSet = new ArrayList<>();
|
||||||
|
ArrayList<String> observationCodeIdsSubSet = new ArrayList<>();
|
||||||
|
for (int observationCount = 0; observationCount < 15 ; observationCount++) {
|
||||||
|
int codeIndex = (int) (1000 * Math.random());
|
||||||
|
observationCodesSubSet.add(observationCodes.get(codeIndex));
|
||||||
|
observationCodeIdsSubSet.add(observationCodeIds.get(codeIndex));
|
||||||
|
}
|
||||||
|
int repeatedCodeIndex = (int) (1000 * Math.random());
|
||||||
|
CodeableConcept repeatedCoding = observationCodes.get(repeatedCodeIndex);
|
||||||
|
for (int observationCount = 0; observationCount < 10 ; observationCount++ ) {
|
||||||
|
observationCodesSubSet.add(repeatedCoding);
|
||||||
|
observationCodeIdsSubSet.add(observationCodeIds.get(repeatedCodeIndex));
|
||||||
|
}
|
||||||
|
int entryCount = 0;
|
||||||
|
for (int codingCount=0; codingCount<observationCodesSubSet.size(); codingCount++) {
|
||||||
|
String nextResourceId = UUID.randomUUID().toString();
|
||||||
|
ObservationJson observationDocument = new ObservationJson();
|
||||||
|
observationDocument.setIdentifier(nextResourceId);
|
||||||
|
observationDocument.setSubject(subject);
|
||||||
|
List<CodeableConcept> category = new ArrayList<>();
|
||||||
|
Coding categoryCoding = new Coding("http://mycodes.org/fhir/category-code", "test-category-code", "test category display");
|
||||||
|
CodeableConcept categoryCodeableConcept = new CodeableConcept();
|
||||||
|
categoryCodeableConcept.getCoding().add(categoryCoding);
|
||||||
|
categoryCodeableConcept.setText("Test Category CodeableConcept Text");
|
||||||
|
category.add(categoryCodeableConcept);
|
||||||
|
observationDocument.setCategories(category);
|
||||||
|
observationDocument.setCode(observationCodesSubSet.get(codingCount));
|
||||||
|
observationDocument.setCode_concept_id(observationCodeIdsSubSet.get(codingCount));
|
||||||
|
Calendar observationDate = new GregorianCalendar();
|
||||||
|
observationDate.add(Calendar.HOUR, -10 + entryCount);
|
||||||
|
entryCount++;
|
||||||
|
Date effectiveDtm = observationDate.getTime();
|
||||||
|
observationDocument.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
String printedObservationDocument = ourMapperNonPrettyPrint.writeValueAsString(observationDocument);
|
||||||
|
elasticsearchSvc.addToBulkIndexRequest("observation_index", nextResourceId, printedObservationDocument, IndexConstants.OBSERVATION_DOCUMENT_TYPE );
|
||||||
|
}
|
||||||
|
if ((patientCount+1)%100 == 0) {
|
||||||
|
System.out.println("Entries created = " + (patientCount+1)*25);
|
||||||
|
}
|
||||||
|
if ((patientCount+1)%250 == 0) {
|
||||||
|
elasticsearchSvc.executeBulkIndex();
|
||||||
|
long elapsedTime = stopwatch.getElapsedTime();
|
||||||
|
stopwatch.restart();
|
||||||
|
System.out.println("Elapsed processing time = " + elapsedTime/1000 + "s");
|
||||||
|
System.out.println("Average processing time/observation = " + elapsedTime/5000 + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elasticsearchSvc.bulkRequestPending()) {
|
||||||
|
elasticsearchSvc.executeBulkIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ourLog.info("Upload complete");
|
||||||
|
} finally {
|
||||||
|
elasticsearchSvc.closeClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IdJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IndexJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class OneThousandObservationsIntoElasticSearch {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OneThousandObservationsIntoElasticSearch.class);
|
||||||
|
|
||||||
|
private static final ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] theArgs) {
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 3 ; patientCount++) {
|
||||||
|
|
||||||
|
String subject = "Patient/"+UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 1100 ; entryCount++ ) {
|
||||||
|
String nextResourceId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
IdJson id = new IdJson(nextResourceId);
|
||||||
|
IndexJson documentIndex = new IndexJson(id);
|
||||||
|
|
||||||
|
ObservationJson observationDocument = new ObservationJson();
|
||||||
|
observationDocument.setIdentifier(nextResourceId);
|
||||||
|
observationDocument.setSubject(subject);
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> category = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
Coding categoryCoding1_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test heart-rate");
|
||||||
|
Coding categoryCoding1_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test alternate heart-rate");
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept1.getCoding().add(categoryCoding1_1);
|
||||||
|
categoryCodeableConcept1.getCoding().add(categoryCoding1_2);
|
||||||
|
categoryCodeableConcept1.setText("Heart Rate CodeableConcept");
|
||||||
|
category.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
Coding categoryCoding2_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test vital signs");
|
||||||
|
Coding categoryCoding2_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test alternate vital signs");
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept2.getCoding().add(categoryCoding2_1);
|
||||||
|
categoryCodeableConcept2.getCoding().add(categoryCoding2_2);
|
||||||
|
categoryCodeableConcept2.setText("Vital Signs CodeableConcept");
|
||||||
|
category.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
Coding categoryCoding3_1 = new Coding("http://mycodes.org/fhir/observation-category", "test-vitals-panel", "test vital signs panel");
|
||||||
|
Coding categoryCoding3_2 = new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test alternate vital signs panel");
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept();
|
||||||
|
categoryCodeableConcept3.getCoding().add(categoryCoding3_1);
|
||||||
|
categoryCodeableConcept3.getCoding().add(categoryCoding3_2);
|
||||||
|
categoryCodeableConcept3.setText("Vital Signs Panel CodeableConcept");
|
||||||
|
category.add(categoryCodeableConcept3);
|
||||||
|
observationDocument.setCategories(category);
|
||||||
|
|
||||||
|
Coding codeCoding1 = new Coding("http://mycodes.org/fhir/observation-code", "test-code_" + entryCount, "test observation code");
|
||||||
|
Coding codeCoding2 = new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code_" + entryCount, "test observation code");
|
||||||
|
Coding codeCoding3 = new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code" + entryCount, "test observation code");
|
||||||
|
CodeableConcept code = new CodeableConcept();
|
||||||
|
code.getCoding().add(codeCoding1);
|
||||||
|
code.getCoding().add(codeCoding2);
|
||||||
|
code.getCoding().add(codeCoding3);
|
||||||
|
code.setText("Observation code CodeableConcept " + entryCount);
|
||||||
|
observationDocument.setCode(code);
|
||||||
|
observationDocument.setCode_concept_id("multicode_test_normalized_code_" + entryCount);
|
||||||
|
|
||||||
|
Date effectiveDtm = new Date();
|
||||||
|
observationDocument.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
|
||||||
|
File outputFile = new File("one_thousand_observations.json");
|
||||||
|
try {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(outputFile, true);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, documentIndex);
|
||||||
|
stringWriter.append('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, observationDocument);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, documentIndex);
|
||||||
|
outputStream.write('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, observationDocument);
|
||||||
|
outputStream.write('\n');
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Upload complete");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IdJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.IndexJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class UploadSampleDatasetIntoElasticSearch {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadSampleDatasetIntoElasticSearch.class);
|
||||||
|
|
||||||
|
private static final ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] theArgs) {
|
||||||
|
|
||||||
|
FhirContext myFhirCtx = FhirContext.forR4();
|
||||||
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(120000);
|
||||||
|
|
||||||
|
PathMatchingResourcePatternResolver provider = new PathMatchingResourcePatternResolver();
|
||||||
|
final Resource[] bundleResources;
|
||||||
|
try {
|
||||||
|
bundleResources = provider.getResources("*.json.bz2");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unexpected error during transmission: " + e.toString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicInteger index = new AtomicInteger();
|
||||||
|
|
||||||
|
Arrays.stream(bundleResources).forEach(
|
||||||
|
resource -> {
|
||||||
|
index.incrementAndGet();
|
||||||
|
|
||||||
|
InputStream resIs = null;
|
||||||
|
String nextBundleString;
|
||||||
|
try {
|
||||||
|
resIs = resource.getInputStream();
|
||||||
|
resIs = new BZip2CompressorInputStream(resIs);
|
||||||
|
nextBundleString = IOUtils.toString(resIs, Charsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
ourLog.error("Failure reading: {}", resource.getFilename(), e);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (resIs != null) {
|
||||||
|
resIs.close();
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Uploading {}/{} - {} ({} bytes)", index, bundleResources.length, resource.getFilename(), nextBundleString.length());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SMART demo apps rely on the use of LOINC 3141-9 (Body Weight Measured)
|
||||||
|
* instead of LOINC 29463-7 (Body Weight)
|
||||||
|
*/
|
||||||
|
nextBundleString = nextBundleString.replace("\"29463-7\"", "\"3141-9\"");
|
||||||
|
|
||||||
|
IParser parser = myFhirCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler(false));
|
||||||
|
Bundle bundle = parser.parseResource(Bundle.class, nextBundleString);
|
||||||
|
|
||||||
|
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Synthea gives resources UUIDs with urn:uuid: prefix, which is only
|
||||||
|
* used for placeholders. We're going to use these as the actual resource
|
||||||
|
* IDs, so we strip the prefix.
|
||||||
|
*/
|
||||||
|
String nextResourceId = nextEntry.getFullUrl();
|
||||||
|
if (nextResourceId == null) {
|
||||||
|
nextResourceId = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextResourceId = nextResourceId.replace("urn:uuid:", "");
|
||||||
|
nextEntry.getResource().setId(nextResourceId);
|
||||||
|
nextEntry.setFullUrl(nextResourceId);
|
||||||
|
|
||||||
|
if (nextEntry.getResource().getResourceType().equals(ResourceType.Observation)) {
|
||||||
|
|
||||||
|
IdJson id = new IdJson(nextResourceId);
|
||||||
|
IndexJson documentIndex = new IndexJson(id);
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.Observation observation = (Observation) nextEntry.getResource();
|
||||||
|
ObservationJson observationDocument = new ObservationJson();
|
||||||
|
observationDocument.setIdentifier(nextResourceId);
|
||||||
|
String subject = "Patient/"+observation.getSubject().getReference();
|
||||||
|
observationDocument.setSubject(subject);
|
||||||
|
List<CodeableConcept> category = observation.getCategory();
|
||||||
|
observationDocument.setCategories(category);
|
||||||
|
observationDocument.setCode_concept_id(category.get(0).getCodingFirstRep().getSystem() + "/" + category.get(0).getCodingFirstRep().getCode());
|
||||||
|
CodeableConcept code = observation.getCode();
|
||||||
|
observationDocument.setCode(code);
|
||||||
|
Date effectiveDtm = observation.getEffectiveDateTimeType().getValue();
|
||||||
|
observationDocument.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
File outputFile = new File("Observations.json");
|
||||||
|
try {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(outputFile, true);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, documentIndex);
|
||||||
|
stringWriter.append('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(stringWriter, observationDocument);
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, documentIndex);
|
||||||
|
outputStream.write('\n');
|
||||||
|
ourMapperNonPrettyPrint.writeValue(outputStream, observationDocument);
|
||||||
|
outputStream.write('\n');
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println(stringWriter.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ourLog.info("Upload complete");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory")
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class ElasticsearchConfig {
|
||||||
|
|
||||||
|
private final String elasticsearchHost = "127.0.0.1";
|
||||||
|
private final Integer elasticsearchPort = 9301;
|
||||||
|
private final String elasticsearchUserId = "elastic";
|
||||||
|
private final String elasticsearchPassword = "changeme";
|
||||||
|
|
||||||
|
@Bean()
|
||||||
|
public ElasticsearchSvcImpl myElasticsearchSvc() throws IOException {
|
||||||
|
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUserId, elasticsearchPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.json;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Smile CDR - CDR
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2016 - 2019 Simpatico Intelligent Systems Inc
|
||||||
|
* %%
|
||||||
|
* All rights reserved.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.CodeSystemHash;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class CodeJson {
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeable_concept_id", required = false)
|
||||||
|
private String myCodeableConceptId;
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeable_concept_text", required = false)
|
||||||
|
private String myCodeableConceptText;
|
||||||
|
|
||||||
|
@JsonProperty(value = "codingcode", required = false)
|
||||||
|
private List<String> myCoding_code = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codingcode_system_hash", required = true)
|
||||||
|
private List<String> myCoding_code_system_hash = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codingdisplay", required = false)
|
||||||
|
private List<String> myCoding_display = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codingsystem", required = false)
|
||||||
|
private List<String> myCoding_system = new ArrayList<>();
|
||||||
|
|
||||||
|
public CodeJson(){
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeJson(CodeableConcept theCodeableConcept, String theCodeableConceptId) {
|
||||||
|
myCodeableConceptText = theCodeableConcept.getText();
|
||||||
|
myCodeableConceptId = theCodeableConceptId;
|
||||||
|
for (Coding theCoding : theCodeableConcept.getCoding()) {
|
||||||
|
myCoding_code.add(theCoding.getCode());
|
||||||
|
myCoding_system.add(theCoding.getSystem());
|
||||||
|
myCoding_display.add(theCoding.getDisplay());
|
||||||
|
myCoding_code_system_hash.add(String.valueOf(CodeSystemHash.hashCodeSystem(theCoding.getSystem(), theCoding.getCode())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeableConceptId() {
|
||||||
|
return myCodeableConceptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeableConceptText() {
|
||||||
|
return myCodeableConceptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCoding_code() {
|
||||||
|
return myCoding_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCoding_code_system_hash() {
|
||||||
|
return myCoding_code_system_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCoding_display() {
|
||||||
|
return myCoding_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCoding_system() {
|
||||||
|
return myCoding_system;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class IdJson {
|
||||||
|
|
||||||
|
@JsonProperty(value = "_id", required = true)
|
||||||
|
private String myId;
|
||||||
|
|
||||||
|
public IdJson(String theId) {
|
||||||
|
myId = theId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() { return myId; }
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class IndexJson {
|
||||||
|
|
||||||
|
@JsonProperty(value = "index", required = true)
|
||||||
|
private IdJson myIndex;
|
||||||
|
|
||||||
|
public IndexJson(IdJson theIndex) {
|
||||||
|
myIndex = theIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdJson getId() { return myIndex; }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.json;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Smile CDR - CDR
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2016 - 2019 Simpatico Intelligent Systems Inc
|
||||||
|
* %%
|
||||||
|
* All rights reserved.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.CodeSystemHash;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class ObservationJson {
|
||||||
|
|
||||||
|
@JsonProperty(value = "identifier", required = true)
|
||||||
|
private String myIdentifier;
|
||||||
|
|
||||||
|
@JsonProperty(value = "subject", required = true)
|
||||||
|
private String mySubject;
|
||||||
|
|
||||||
|
@JsonProperty(value = "categoryconcepttext", required = false)
|
||||||
|
private List<String> myCategory_concept_text = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "categoryconceptcodingcode", required = false)
|
||||||
|
private List<List<String>> myCategory_coding_code = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "categoryconceptcodingcode_system_hash", required = false)
|
||||||
|
private List<List<String>> myCategory_coding_code_system_hash = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "categoryconceptcodingdisplay", required = false)
|
||||||
|
private List<List<String>> myCategory_coding_display = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "categoryconceptcodingsystem", required = false)
|
||||||
|
private List<List<String>> myCategory_coding_system = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconceptid", required = false)
|
||||||
|
private String myCode_concept_id;
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconcepttext", required = false)
|
||||||
|
private String myCode_concept_text;
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconceptcodingcode", required = false)
|
||||||
|
private List<String> myCode_coding_code = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconceptcodingcode_system_hash", required = false)
|
||||||
|
private List<String> myCode_coding_code_system_hash = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconceptcodingdisplay", required = false)
|
||||||
|
private List<String> myCode_coding_display = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "codeconceptcodingsystem", required = false)
|
||||||
|
private List<String> myCode_coding_system = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty(value = "effectivedtm", required = true)
|
||||||
|
private Date myEffectiveDtm;
|
||||||
|
|
||||||
|
public ObservationJson() {}
|
||||||
|
|
||||||
|
public void setIdentifier(String theIdentifier) {
|
||||||
|
myIdentifier = theIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String theSubject) {
|
||||||
|
mySubject = theSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategories(List<CodeableConcept> theCategories) {
|
||||||
|
for (CodeableConcept theConcept : theCategories) {
|
||||||
|
myCategory_concept_text.add(theConcept.getText());
|
||||||
|
List<String> coding_code_system_hashes = new ArrayList<>();
|
||||||
|
List<String> coding_codes = new ArrayList<>();
|
||||||
|
List<String> coding_displays = new ArrayList<>();
|
||||||
|
List<String> coding_systems = new ArrayList<>();
|
||||||
|
for (Coding theCategoryCoding : theConcept.getCoding()) {
|
||||||
|
coding_code_system_hashes.add(String.valueOf(CodeSystemHash.hashCodeSystem(theCategoryCoding.getSystem(), theCategoryCoding.getCode())));
|
||||||
|
coding_codes.add(theCategoryCoding.getCode());
|
||||||
|
coding_displays.add(theCategoryCoding.getDisplay());
|
||||||
|
coding_systems.add(theCategoryCoding.getSystem());
|
||||||
|
}
|
||||||
|
myCategory_coding_code_system_hash.add(coding_code_system_hashes);
|
||||||
|
myCategory_coding_code.add(coding_codes);
|
||||||
|
myCategory_coding_display.add(coding_displays);
|
||||||
|
myCategory_coding_system.add(coding_systems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCategory_concept_text() {
|
||||||
|
return myCategory_concept_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getCategory_coding_code_system_hash() {
|
||||||
|
return myCategory_coding_code_system_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getCategory_coding_code() {
|
||||||
|
return myCategory_coding_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getCategory_coding_display() {
|
||||||
|
return myCategory_coding_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getCategory_coding_system() {
|
||||||
|
return myCategory_coding_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(CodeableConcept theCode) {
|
||||||
|
myCode_concept_text = theCode.getText();
|
||||||
|
for(Coding theCodeCoding : theCode.getCoding()) {
|
||||||
|
myCode_coding_code_system_hash.add(String.valueOf(CodeSystemHash.hashCodeSystem(theCodeCoding.getSystem(), theCodeCoding.getCode())));
|
||||||
|
myCode_coding_code.add(theCodeCoding.getCode());
|
||||||
|
myCode_coding_display.add(theCodeCoding.getDisplay());
|
||||||
|
myCode_coding_system.add(theCodeCoding.getSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode_concept_text() {
|
||||||
|
return myCode_concept_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCode_coding_code_system_hash() {
|
||||||
|
return myCode_coding_code_system_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCode_coding_code() {
|
||||||
|
return myCode_coding_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCode_coding_display() {
|
||||||
|
return myCode_coding_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCode_coding_system() {
|
||||||
|
return myCode_coding_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode_concept_id(String theCodeId) {
|
||||||
|
myCode_concept_id = theCodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode_concept_id() {
|
||||||
|
return myCode_concept_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEffectiveDtm(Date theEffectiveDtm) {
|
||||||
|
myEffectiveDtm = theEffectiveDtm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEffectiveDtm() {
|
||||||
|
return myEffectiveDtm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return mySubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return myIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.HashFunction;
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
|
||||||
|
public class CodeSystemHash {
|
||||||
|
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
||||||
|
private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
|
static public long hashCodeSystem( String system, String code ) {
|
||||||
|
Hasher hasher = HASH_FUNCTION.newHasher();
|
||||||
|
addStringToHasher(hasher, system);
|
||||||
|
addStringToHasher(hasher, code);
|
||||||
|
|
||||||
|
HashCode hashCode = hasher.hash();
|
||||||
|
return hashCode.asLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void addStringToHasher(Hasher hasher, String next) {
|
||||||
|
if (next == null) {
|
||||||
|
hasher.putByte((byte) 0);
|
||||||
|
} else {
|
||||||
|
next = UrlUtil.escapeUrlParam(next);
|
||||||
|
byte[] bytes = next.getBytes(Charsets.UTF_8);
|
||||||
|
hasher.putBytes(bytes);
|
||||||
|
}
|
||||||
|
hasher.putBytes(DELIMITER_BYTES);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.util;
|
||||||
|
|
||||||
|
public class SimpleStopWatch {
|
||||||
|
private long myStarted = System.currentTimeMillis();
|
||||||
|
|
||||||
|
public SimpleStopWatch() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getElapsedTime() {
|
||||||
|
return System.currentTimeMillis() - myStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restart() {
|
||||||
|
myStarted = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.config.TestIntegratedObservationIndexSearchConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedCodeCodeableConceptSearchParamDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedSearchParamLastNDao;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestIntegratedObservationIndexSearchConfig.class })
|
||||||
|
public class IntegratedObservationIndexedSearchParamLastNTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedSearchParamLastNDao myResourceIndexedObservationLastNDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedCodeCodeableConceptSearchParamDao myCodeableConceptIndexedSearchParamNormalizedDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ElasticsearchSvcImpl elasticsearchSvc;
|
||||||
|
|
||||||
|
final String RESOURCEPID = "123";
|
||||||
|
final String SUBJECTID = "4567";
|
||||||
|
final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category";
|
||||||
|
final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate";
|
||||||
|
|
||||||
|
final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code";
|
||||||
|
final String CODEFIRSTCODINGCODE = "test-code";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
|
||||||
|
myResourceIndexedObservationLastNDao.deleteAll();
|
||||||
|
myCodeableConceptIndexedSearchParamNormalizedDao.deleteAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
|
||||||
|
myResourceIndexedObservationLastNDao.deleteAll();
|
||||||
|
myCodeableConceptIndexedSearchParamNormalizedDao.deleteAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexObservationSingle() throws IOException {
|
||||||
|
|
||||||
|
Observation myObservation = new Observation();
|
||||||
|
String resourcePID = "123";
|
||||||
|
myObservation.setId(resourcePID);
|
||||||
|
Reference subjectId = new Reference("4567");
|
||||||
|
myObservation.setSubject(subjectId);
|
||||||
|
Date effectiveDtm = new Date();
|
||||||
|
myObservation.setEffective(new DateTimeType(effectiveDtm));
|
||||||
|
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> categoryConcepts = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for for second category");
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
List<Coding> category3 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText("Test Codeable Concept Field for third category");
|
||||||
|
category3.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vitals-panel", "test-vitals-panel display"));
|
||||||
|
category3.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test-alt-vitals-panel display"));
|
||||||
|
category3.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals-panel", "test-2nd-alt-vitals-panel display"));
|
||||||
|
categoryCodeableConcept3.setCoding(category3);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept3);
|
||||||
|
myObservation.setCategory(categoryConcepts);
|
||||||
|
|
||||||
|
// Create CodeableConcept for Code with three codings.
|
||||||
|
String observationCodeText = "Test Codeable Concept Field for Code";
|
||||||
|
CodeableConcept codeableConceptField = new CodeableConcept().setText(observationCodeText);
|
||||||
|
codeableConceptField.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code", "test-code display"));
|
||||||
|
codeableConceptField.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code", "test-alt-code display"));
|
||||||
|
codeableConceptField.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code", "test-second-alt-code display"));
|
||||||
|
myObservation.setCode(codeableConceptField);
|
||||||
|
|
||||||
|
myObservationLastNIndexPersistSvc.indexObservation(myObservation);
|
||||||
|
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", SUBJECTID);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
// execute Observation ID search - Terms Aggregation
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationAllFieldsTermsSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(1, observationIdsOnly.size());
|
||||||
|
ObservationJson observationIdOnly = observationIdsOnly.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observationIdOnly.getIdentifier());
|
||||||
|
|
||||||
|
// execute Observation ID search - Composite Aggregation
|
||||||
|
// searchRequestIdsOnly = elasticsearchSvc.buildObservationAllFieldsCompositeSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
// responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
// observationIdsOnly = elasticsearchSvc.buildObservationCompositeResults(responseIds);
|
||||||
|
|
||||||
|
// assertEquals(1, observationIdsOnly.size());
|
||||||
|
// observationIdOnly = observationIdsOnly.get(0);
|
||||||
|
// assertEquals(RESOURCEPID, observationIdOnly.getIdentifier());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testIndexObservationMultiple() {
|
||||||
|
|
||||||
|
// Create two CodeableConcept values each for a Code with three codings.
|
||||||
|
CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code");
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-1", "test-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display"));
|
||||||
|
|
||||||
|
CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code");
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-2", "test-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display"));
|
||||||
|
|
||||||
|
// Create two CodeableConcept entities for category, each with three codings.
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
List<CodeableConcept> categoryConcepts1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts1.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
List<CodeableConcept> categoryConcepts2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category");
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts2.add(categoryCodeableConcept2);
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 10 ; patientCount++) {
|
||||||
|
|
||||||
|
String subjectId = String.valueOf(patientCount);
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 10 ; entryCount++ ) {
|
||||||
|
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.setId(String.valueOf(entryCount + patientCount*10));
|
||||||
|
Reference subject = new Reference(subjectId);
|
||||||
|
observation.setSubject(subject);
|
||||||
|
|
||||||
|
if (entryCount%2 == 1) {
|
||||||
|
observation.setCategory(categoryConcepts1);
|
||||||
|
observation.setCode(codeableConceptField1);
|
||||||
|
} else {
|
||||||
|
observation.setCategory(categoryConcepts2);
|
||||||
|
observation.setCode(codeableConceptField2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar observationDate = new GregorianCalendar();
|
||||||
|
observationDate.add(Calendar.HOUR, -10 + entryCount);
|
||||||
|
Date effectiveDtm = observationDate.getTime();
|
||||||
|
observation.setEffective(new DateTimeType(effectiveDtm));
|
||||||
|
|
||||||
|
myObservationLastNIndexPersistSvc.indexObservation(observation);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(100, myResourceIndexedObservationLastNDao.count());
|
||||||
|
assertEquals(2, myCodeableConceptIndexedSearchParamNormalizedDao.count());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.config.TestObservationIndexSearchConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedCodeCodeableConceptSearchParamDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity;
|
||||||
|
import ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IObservationIndexedSearchParamLastNDao;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestObservationIndexSearchConfig.class })
|
||||||
|
public class PersistObservationIndexedSearchParamLastNTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedSearchParamLastNDao myResourceIndexedObservationLastNDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IObservationIndexedCodeCodeableConceptSearchParamDao myCodeableConceptIndexedSearchParamNormalizedDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
|
||||||
|
myResourceIndexedObservationLastNDao.deleteAll();
|
||||||
|
myCodeableConceptIndexedSearchParamNormalizedDao.deleteAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
|
||||||
|
myResourceIndexedObservationLastNDao.deleteAll();
|
||||||
|
myCodeableConceptIndexedSearchParamNormalizedDao.deleteAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexObservationSingle() {
|
||||||
|
|
||||||
|
Observation myObservation = new Observation();
|
||||||
|
String resourcePID = "123";
|
||||||
|
myObservation.setId(resourcePID);
|
||||||
|
Reference subjectId = new Reference("4567");
|
||||||
|
myObservation.setSubject(subjectId);
|
||||||
|
Date effectiveDtm = new Date();
|
||||||
|
myObservation.setEffective(new DateTimeType(effectiveDtm));
|
||||||
|
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> categoryConcepts = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for for second category");
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
List<Coding> category3 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText("Test Codeable Concept Field for third category");
|
||||||
|
category3.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vitals-panel", "test-vitals-panel display"));
|
||||||
|
category3.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test-alt-vitals-panel display"));
|
||||||
|
category3.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals-panel", "test-2nd-alt-vitals-panel display"));
|
||||||
|
categoryCodeableConcept3.setCoding(category3);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept3);
|
||||||
|
myObservation.setCategory(categoryConcepts);
|
||||||
|
|
||||||
|
// Create CodeableConcept for Code with three codings.
|
||||||
|
String observationCodeText = "Test Codeable Concept Field for Code";
|
||||||
|
CodeableConcept codeableConceptField = new CodeableConcept().setText(observationCodeText);
|
||||||
|
codeableConceptField.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code", "test-code display"));
|
||||||
|
codeableConceptField.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code", "test-alt-code display"));
|
||||||
|
codeableConceptField.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code", "test-second-alt-code display"));
|
||||||
|
myObservation.setCode(codeableConceptField);
|
||||||
|
|
||||||
|
myObservationLastNIndexPersistSvc.indexObservation(myObservation);
|
||||||
|
|
||||||
|
List<ObservationIndexedSearchParamLastNEntity> persistedObservationEntities = myResourceIndexedObservationLastNDao.findAll();
|
||||||
|
assertEquals(1, persistedObservationEntities.size());
|
||||||
|
ObservationIndexedSearchParamLastNEntity persistedObservationEntity = persistedObservationEntities.get(0);
|
||||||
|
assertEquals("Patient/"+subjectId.getReference(), persistedObservationEntity.getSubject());
|
||||||
|
assertEquals(resourcePID, persistedObservationEntity.getIdentifier());
|
||||||
|
assertEquals(effectiveDtm, persistedObservationEntity.getEffectiveDtm());
|
||||||
|
|
||||||
|
String observationCodeNormalizedId = persistedObservationEntity.getCodeNormalizedId();
|
||||||
|
|
||||||
|
List<ObservationIndexedCodeCodeableConceptEntity> persistedObservationCodes = myCodeableConceptIndexedSearchParamNormalizedDao.findAll();
|
||||||
|
assertEquals(1, persistedObservationCodes.size());
|
||||||
|
ObservationIndexedCodeCodeableConceptEntity persistedObservationCode = persistedObservationCodes.get(0);
|
||||||
|
assertEquals(observationCodeNormalizedId, persistedObservationCode.getCodeableConceptId());
|
||||||
|
assertEquals(observationCodeText, persistedObservationCode.getCodeableConceptText());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexObservationMultiple() {
|
||||||
|
|
||||||
|
// Create two CodeableConcept values each for a Code with three codings.
|
||||||
|
CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code");
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-1", "test-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display"));
|
||||||
|
|
||||||
|
CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code");
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-2", "test-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display"));
|
||||||
|
|
||||||
|
// Create two CodeableConcept entities for category, each with three codings.
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
List<CodeableConcept> categoryConcepts1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts1.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
List<CodeableConcept> categoryConcepts2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category");
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts2.add(categoryCodeableConcept2);
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 10 ; patientCount++) {
|
||||||
|
|
||||||
|
String subjectId = String.valueOf(patientCount);
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 10 ; entryCount++ ) {
|
||||||
|
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.setId(String.valueOf(entryCount + patientCount*10));
|
||||||
|
Reference subject = new Reference(subjectId);
|
||||||
|
observation.setSubject(subject);
|
||||||
|
|
||||||
|
if (entryCount%2 == 1) {
|
||||||
|
observation.setCategory(categoryConcepts1);
|
||||||
|
observation.setCode(codeableConceptField1);
|
||||||
|
} else {
|
||||||
|
observation.setCategory(categoryConcepts2);
|
||||||
|
observation.setCode(codeableConceptField2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar observationDate = new GregorianCalendar();
|
||||||
|
observationDate.add(Calendar.HOUR, -10 + entryCount);
|
||||||
|
Date effectiveDtm = observationDate.getTime();
|
||||||
|
observation.setEffective(new DateTimeType(effectiveDtm));
|
||||||
|
|
||||||
|
myObservationLastNIndexPersistSvc.indexObservation(observation);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(100, myResourceIndexedObservationLastNDao.count());
|
||||||
|
assertEquals(2, myCodeableConceptIndexedSearchParamNormalizedDao.count());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
|
||||||
|
basePackages = {"ca.uhn.fhir.jpa.dao"})
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class TestIntegratedObservationIndexSearchConfig extends TestObservationIndexSearchConfig {
|
||||||
|
|
||||||
|
@Bean()
|
||||||
|
public ElasticsearchSvcImpl myElasticsearchSvc() throws IOException {
|
||||||
|
int elasticsearchPort = embeddedElasticSearch().getHttpPort();
|
||||||
|
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUserId, elasticsearchPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.lastn.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||||
|
import ca.uhn.fhir.jpa.config.r4.BaseR4Config;
|
||||||
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
|
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus;
|
||||||
|
import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.PopularProperties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
|
||||||
|
basePackages = {"ca.uhn.fhir.jpa.dao"})
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class TestObservationIndexSearchConfig extends TestR4Config {
|
||||||
|
|
||||||
|
final String elasticsearchHost = "127.0.0.1";
|
||||||
|
final String elasticsearchUserId = "";
|
||||||
|
final String elasticsearchPassword = "";
|
||||||
|
|
||||||
|
private static final String ELASTIC_VERSION = "6.5.4";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties jpaProperties() {
|
||||||
|
|
||||||
|
Properties extraProperties = new Properties();
|
||||||
|
extraProperties.put("hibernate.dialect", org.hibernate.dialect.H2Dialect.class);
|
||||||
|
extraProperties.put("hibernate.format_sql", "false");
|
||||||
|
extraProperties.put("hibernate.show_sql", "false");
|
||||||
|
extraProperties.put(AvailableSettings.HBM2DDL_AUTO, "update");
|
||||||
|
extraProperties.put("hibernate.jdbc.batch_size", "5000");
|
||||||
|
extraProperties.put("hibernate.cache.use_query_cache", "false");
|
||||||
|
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
|
||||||
|
extraProperties.put("hibernate.cache.use_structured_entries", "false");
|
||||||
|
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
|
||||||
|
extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
|
||||||
|
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
|
||||||
|
extraProperties.put("hibernate.search.default.worker.execution", "sync");
|
||||||
|
|
||||||
|
int elasticsearchPort = embeddedElasticSearch().getHttpPort();
|
||||||
|
new ElasticsearchHibernatePropertiesBuilder()
|
||||||
|
.setDebugRefreshAfterWrite(true)
|
||||||
|
.setDebugPrettyPrintJsonLog(true)
|
||||||
|
.setIndexSchemaManagementStrategy(IndexSchemaManagementStrategy.CREATE)
|
||||||
|
.setIndexManagementWaitTimeoutMillis(10000)
|
||||||
|
.setRequiredIndexStatus(ElasticsearchIndexStatus.YELLOW)
|
||||||
|
.setRestUrl("http://" + elasticsearchHost + ":" + elasticsearchPort)
|
||||||
|
.setUsername(elasticsearchUserId)
|
||||||
|
.setPassword(elasticsearchPassword)
|
||||||
|
.apply(extraProperties);
|
||||||
|
|
||||||
|
extraProperties.setProperty("hibernate.search.default.elasticsearch.refresh_after_write", "true");
|
||||||
|
return extraProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EmbeddedElastic embeddedElasticSearch() {
|
||||||
|
EmbeddedElastic embeddedElastic;
|
||||||
|
try {
|
||||||
|
embeddedElastic = EmbeddedElastic.builder()
|
||||||
|
.withElasticVersion(ELASTIC_VERSION)
|
||||||
|
.withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.HTTP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID())
|
||||||
|
.withStartTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
.start();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return embeddedElastic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.SimpleStopWatch;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
/*
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.shadehapi.elasticsearch.index.query.QueryBuilders;
|
||||||
|
import org.shadehapi.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.SearchHit;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.AggregationBuilders;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.Aggregations;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.metrics.tophits.ParsedTopHits;
|
||||||
|
import org.shadehapi.elasticsearch.search.aggregations.support.ValueType;
|
||||||
|
import org.shadehapi.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.shadehapi.elasticsearch.search.sort.SortOrder;
|
||||||
|
*/
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
public class ElasticsearchPerformanceTests {
|
||||||
|
|
||||||
|
private ElasticsearchSvcImpl elasticsearchSvc = new ElasticsearchSvcImpl("localhost", 9301, "elastic", "changeme");
|
||||||
|
private List<String> patientIds = new ArrayList<>();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
/* SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||||
|
RandomScoreFunctionBuilder randomScoreFunctionBuilder = new RandomScoreFunctionBuilder();
|
||||||
|
Date now = new Date();
|
||||||
|
randomScoreFunctionBuilder.seed(now.getTime());
|
||||||
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(randomScoreFunctionBuilder);
|
||||||
|
searchSourceBuilder.query(functionScoreQueryBuilder);
|
||||||
|
searchSourceBuilder.size(0);
|
||||||
|
|
||||||
|
// Aggregation by order codes
|
||||||
|
TermsAggregationBuilder observationCodeValuesBuilder = new TermsAggregationBuilder("group_by_patient", ValueType.STRING).field("subject");
|
||||||
|
observationCodeValuesBuilder.size(1000);
|
||||||
|
// Top Hits Aggregation
|
||||||
|
String[] topHitsInclude = {"subject"};
|
||||||
|
observationCodeValuesBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
|
||||||
|
.sort("effectivedtm", SortOrder.DESC)
|
||||||
|
.fetchSource(topHitsInclude, null).size(1));
|
||||||
|
|
||||||
|
searchSourceBuilder.aggregation(observationCodeValuesBuilder);
|
||||||
|
searchRequest.source(searchSourceBuilder);
|
||||||
|
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
patientIds = buildResults(response);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testObservationCodesQueryPerformance() throws IOException {
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationCodesSearchRequest(10000);
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(int i=0; i<1000; i++) {
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// List<CodeJson> codes = elasticsearchSvc.buildCodeResult(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
// assertEquals(1000L, codes.size());
|
||||||
|
}
|
||||||
|
System.out.println("Codes query Average elapsed time = " + totalElapsedTimes/1000 + " ms");
|
||||||
|
System.out.println("Codes query Average elapsed search and build time = " + totalElapsedSearchAndBuildTimes/1000 + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObservationsQueryPerformance() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(String patientId : patientIds) {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", patientId);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// List<ObservationJson> observations = elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
// assertEquals(25, observations.size());
|
||||||
|
}
|
||||||
|
System.out.println("Observations query Average elapsed time = " + totalElapsedTimes/(patientIds.size()) + " ms");
|
||||||
|
System.out.println("Observations query Average elapsed search and build time = " + totalElapsedSearchAndBuildTimes/(patientIds.size()) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testSingleObservationsQuery() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "5b2091a5-a50e-447b-aff4-c34b69eb43d5");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationCompositeSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// List<ObservationJson> observations = elasticsearchSvc.buildObservationCompositeResults(response);
|
||||||
|
// assertEquals(25, observations.size());
|
||||||
|
System.out.println("Average elapsed time = " + totalElapsedTimes/1000 + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testLastNObservationsQueryTermsPerformance() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(String patientId : patientIds) {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", patientId);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 5);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
}
|
||||||
|
System.out.println("LastN Average elapsed time = " + totalElapsedTimes/(patientIds.size()) + " ms");
|
||||||
|
System.out.println("LastN Average elapsed search and Build time = " + totalElapsedSearchAndBuildTimes/(patientIds.size()) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testLastNObservationsQueryCompPerformance() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(String patientId : patientIds) {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", patientId);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationCompositeSearchRequest(1000, searchParameterMap, 5);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// elasticsearchSvc.buildObservationCompositeResults(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
}
|
||||||
|
System.out.println("LastN Average elapsed time = " + totalElapsedTimes/(patientIds.size()) + " ms");
|
||||||
|
System.out.println("LastN Average elapsed search and Build time = " + totalElapsedSearchAndBuildTimes/(patientIds.size()) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* private List<String> buildResults(SearchResponse theSearchResponse) throws IOException {
|
||||||
|
Aggregations responseAggregations = theSearchResponse.getAggregations();
|
||||||
|
ParsedTerms aggregatedObservationCodes = responseAggregations.get("group_by_patient");
|
||||||
|
aggregatedObservationCodes.getBuckets();
|
||||||
|
List<? extends Terms.Bucket> observationCodeBuckets = aggregatedObservationCodes.getBuckets();
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
List<String> identifiers = new ArrayList<>(10000);
|
||||||
|
for (Terms.Bucket observationCodeBucket : observationCodeBuckets) {
|
||||||
|
Aggregations topHitObservationCodes = observationCodeBucket.getAggregations();
|
||||||
|
ParsedTopHits parsedTopHits = topHitObservationCodes.get("most_recent_effective");
|
||||||
|
SearchHit[] topHits = parsedTopHits.getHits().getHits();
|
||||||
|
for (SearchHit topHit : topHits) {
|
||||||
|
String sources = topHit.getSourceAsString();
|
||||||
|
ObservationJson code = objectMapper.readValue(sources,ObservationJson.class);
|
||||||
|
identifiers.add(code.getSubject().substring(8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.SimpleStopWatch;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
|
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder;
|
||||||
|
import org.elasticsearch.search.SearchHit;
|
||||||
|
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||||
|
import org.elasticsearch.search.aggregations.Aggregations;
|
||||||
|
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
|
||||||
|
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||||
|
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||||
|
import org.elasticsearch.search.aggregations.metrics.tophits.ParsedTopHits;
|
||||||
|
import org.elasticsearch.search.aggregations.support.ValueType;
|
||||||
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
public class ElasticsearchV5PerformanceTests {
|
||||||
|
|
||||||
|
private ElasticsearchV5SvcImpl elasticsearchSvc = new ElasticsearchV5SvcImpl("localhost", 9301, "elastic", "changeme");
|
||||||
|
private List<String> patientIds = new ArrayList<>();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||||
|
RandomScoreFunctionBuilder randomScoreFunctionBuilder = new RandomScoreFunctionBuilder();
|
||||||
|
Date now = new Date();
|
||||||
|
randomScoreFunctionBuilder.seed(now.getTime());
|
||||||
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(randomScoreFunctionBuilder);
|
||||||
|
searchSourceBuilder.query(functionScoreQueryBuilder);
|
||||||
|
searchSourceBuilder.size(0);
|
||||||
|
|
||||||
|
// Aggregation by order codes
|
||||||
|
TermsAggregationBuilder observationCodeValuesBuilder = new TermsAggregationBuilder("group_by_patient", ValueType.STRING).field("subject");
|
||||||
|
observationCodeValuesBuilder.size(1000);
|
||||||
|
// Top Hits Aggregation
|
||||||
|
String[] topHitsInclude = {"subject"};
|
||||||
|
observationCodeValuesBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
|
||||||
|
.sort("effectivedtm", SortOrder.DESC)
|
||||||
|
.fetchSource(topHitsInclude, null).size(1));
|
||||||
|
|
||||||
|
searchSourceBuilder.aggregation(observationCodeValuesBuilder);
|
||||||
|
searchRequest.source(searchSourceBuilder);
|
||||||
|
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
// patientIds = buildResults(response);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObservationCodesQueryPerformance() throws IOException {
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationCodesSearchRequest(10000);
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(int i=0; i<1000; i++) {
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// List<CodeJson> codes = elasticsearchSvc.buildCodeResult(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
// assertEquals(1000L, codes.size());
|
||||||
|
}
|
||||||
|
System.out.println("Codes query Average elapsed time = " + totalElapsedTimes/1000 + " ms");
|
||||||
|
System.out.println("Codes query Average elapsed search and build time = " + totalElapsedSearchAndBuildTimes/1000 + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObservationsQueryPerformance() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(String patientId : patientIds) {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", patientId);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// List<ObservationJson> observations = elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
// assertEquals(25, observations.size());
|
||||||
|
}
|
||||||
|
System.out.println("Observations query Average elapsed time = " + totalElapsedTimes/(patientIds.size()) + " ms");
|
||||||
|
System.out.println("Observations query Average elapsed search and build time = " + totalElapsedSearchAndBuildTimes/(patientIds.size()) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNObservationsQueryTermsPerformance() throws IOException {
|
||||||
|
long totalElapsedTimes = 0L;
|
||||||
|
long totalElapsedSearchAndBuildTimes = 0L;
|
||||||
|
for(String patientId : patientIds) {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", patientId);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/category-code", "test-category-code");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
|
||||||
|
// SearchRequest searchRequest = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 5);
|
||||||
|
SimpleStopWatch stopWatch = new SimpleStopWatch();
|
||||||
|
// SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
totalElapsedTimes += stopWatch.getElapsedTime();
|
||||||
|
// elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
totalElapsedSearchAndBuildTimes += stopWatch.getElapsedTime();
|
||||||
|
}
|
||||||
|
System.out.println("LastN Average elapsed time = " + totalElapsedTimes/(patientIds.size()) + " ms");
|
||||||
|
System.out.println("LastN Average elapsed search and Build time = " + totalElapsedSearchAndBuildTimes/(patientIds.size()) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildResults(SearchResponse theSearchResponse) throws IOException {
|
||||||
|
Aggregations responseAggregations = theSearchResponse.getAggregations();
|
||||||
|
ParsedTerms aggregatedObservationCodes = responseAggregations.get("group_by_patient");
|
||||||
|
aggregatedObservationCodes.getBuckets();
|
||||||
|
List<? extends Terms.Bucket> observationCodeBuckets = aggregatedObservationCodes.getBuckets();
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
List<String> identifiers = new ArrayList<>(10000);
|
||||||
|
for (Terms.Bucket observationCodeBucket : observationCodeBuckets) {
|
||||||
|
Aggregations topHitObservationCodes = observationCodeBucket.getAggregations();
|
||||||
|
ParsedTopHits parsedTopHits = topHitObservationCodes.get("most_recent_effective");
|
||||||
|
SearchHit[] topHits = parsedTopHits.getHits().getHits();
|
||||||
|
for (SearchHit topHit : topHits) {
|
||||||
|
String sources = topHit.getSourceAsString();
|
||||||
|
ObservationJson code = objectMapper.readValue(sources,ObservationJson.class);
|
||||||
|
identifiers.add(code.getSubject().substring(8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,323 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchConfig;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.search.lastn.IndexConstants.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestElasticsearchConfig.class } )
|
||||||
|
public class LastNElasticsearchSvcMultipleObservationsTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ElasticsearchSvcImpl elasticsearchSvc;
|
||||||
|
|
||||||
|
private static ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
private Map<String,Map<String,List<Date>>> createdPatientObservationMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
createMultiplePatientsAndObservations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(CODE_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNNoCriteriaQuery() throws IOException {
|
||||||
|
|
||||||
|
// execute Observation ID search (Terms Aggregation) last 3 observations for each patient
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, null, 3);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
validateQueryResponse(observationIdsOnly);
|
||||||
|
|
||||||
|
// execute Observation ID search (Terms Aggregation) last 3 observations for each patient
|
||||||
|
searchRequestIdsOnly = elasticsearchSvc.buildObservationCompositeSearchRequest(1000, null, 3);
|
||||||
|
responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
observationIdsOnly = elasticsearchSvc.buildObservationCompositeResults(responseIds);
|
||||||
|
|
||||||
|
validateQueryResponse(observationIdsOnly);
|
||||||
|
|
||||||
|
// Retrieve all Observation codes
|
||||||
|
SearchRequest searchRequest = elasticsearchSvc.buildObservationCodesSearchRequest(1000);
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
List<CodeJson> codes = elasticsearchSvc.buildCodeResult(response);
|
||||||
|
assertEquals(2, codes.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateQueryResponse(List<ObservationJson> observationIdsOnly) {
|
||||||
|
assertEquals(60, observationIdsOnly.size());
|
||||||
|
|
||||||
|
// Observation documents should be grouped by subject, then by observation code, and then sorted by effective date/time
|
||||||
|
// within each observation code. Verify the grouping by creating a nested Map.
|
||||||
|
Map<String,Map<String, List<Date>>> queriedPatientObservationMap = new HashMap<>();
|
||||||
|
ObservationJson previousObservationJson = null;
|
||||||
|
for (ObservationJson observationJson : observationIdsOnly) {
|
||||||
|
assertNotNull(observationJson.getIdentifier());
|
||||||
|
assertNotNull(observationJson.getSubject());
|
||||||
|
assertNotNull(observationJson.getCode_concept_id());
|
||||||
|
assertNotNull(observationJson.getEffectiveDtm());
|
||||||
|
if (previousObservationJson == null) {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap);
|
||||||
|
} else if (observationJson.getSubject().equals(previousObservationJson.getSubject())) {
|
||||||
|
if (observationJson.getCode_concept_id().equals(previousObservationJson.getCode_concept_id())) {
|
||||||
|
queriedPatientObservationMap.get(observationJson.getSubject()).get(observationJson.getCode_concept_id()).
|
||||||
|
add(observationJson.getEffectiveDtm());
|
||||||
|
} else {
|
||||||
|
Map<String, List<Date>> codeObservationDateMap = queriedPatientObservationMap.get(observationJson.getSubject());
|
||||||
|
// Ensure that code concept was not already retrieved out of order for this subject/patient.
|
||||||
|
assertFalse(codeObservationDateMap.containsKey(observationJson.getCode_concept_id()));
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
codeObservationDateMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ensure that subject/patient was not already retrieved out of order
|
||||||
|
assertFalse(queriedPatientObservationMap.containsKey(observationJson.getSubject()));
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap);
|
||||||
|
}
|
||||||
|
previousObservationJson = observationJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally check that only the most recent effective date/time values were returned and in the correct order.
|
||||||
|
for(String subjectId : queriedPatientObservationMap.keySet()) {
|
||||||
|
Map<String, List<Date>> queriedObservationCodeMap = queriedPatientObservationMap.get(subjectId);
|
||||||
|
Map<String, List<Date>> createdObservationCodeMap = createdPatientObservationMap.get(subjectId);
|
||||||
|
for(String observationCode : queriedObservationCodeMap.keySet()) {
|
||||||
|
List<Date> queriedObservationDates = queriedObservationCodeMap.get(observationCode);
|
||||||
|
List<Date> createdObservationDates = createdObservationCodeMap.get(observationCode);
|
||||||
|
for (int dateIdx=0; dateIdx<queriedObservationDates.size(); dateIdx++) {
|
||||||
|
assertEquals(createdObservationDates.get(dateIdx), queriedObservationDates.get(dateIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNMultiPatientMultiCodeHashMultiCategoryHash() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
ReferenceParam patientParam = new ReferenceParam("Patient", "", "8");
|
||||||
|
searchParameterMap.add("patient", patientParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(10, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeCodeOnlyCategoryCodeOnly() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam ("test-heart-rate");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("test-code-1");
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(5, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeSystemOnlyCategorySystemOnly() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", null);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", null);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(10, observationIdsOnly.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeCodeTextCategoryTextOnly() throws IOException {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("test-heart-rate display");
|
||||||
|
categoryParam.setModifier(TokenParamModifier.TEXT);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("test-code-1 display");
|
||||||
|
codeParam.setModifier(TokenParamModifier.TEXT);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(5, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMultiplePatientsAndObservations() throws IOException {
|
||||||
|
// Create CodeableConcepts for two Codes, each with three codings.
|
||||||
|
String codeableConceptId1 = UUID.randomUUID().toString();
|
||||||
|
CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code");
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-1", "test-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display"));
|
||||||
|
CodeJson codeJson1 = new CodeJson(codeableConceptField1, codeableConceptId1);
|
||||||
|
String codeJson1Document = ourMapperNonPrettyPrint.writeValueAsString(codeJson1);
|
||||||
|
|
||||||
|
String codeableConceptId2 = UUID.randomUUID().toString();
|
||||||
|
CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code");
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-2", "test-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display"));
|
||||||
|
CodeJson codeJson2 = new CodeJson(codeableConceptField2, codeableConceptId2);
|
||||||
|
String codeJson2Document = ourMapperNonPrettyPrint.writeValueAsString(codeJson2);
|
||||||
|
|
||||||
|
// Create CodeableConcepts for two categories, each with three codings.
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
List<CodeableConcept> categoryConcepts1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts1.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
List<CodeableConcept> categoryConcepts2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category");
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts2.add(categoryCodeableConcept2);
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 10 ; patientCount++) {
|
||||||
|
|
||||||
|
String subject = "Patient/"+patientCount;
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 10 ; entryCount++ ) {
|
||||||
|
|
||||||
|
ObservationJson observationJson = new ObservationJson();
|
||||||
|
String identifier = String.valueOf((entryCount + patientCount*10));
|
||||||
|
observationJson.setIdentifier(identifier);
|
||||||
|
observationJson.setSubject(subject);
|
||||||
|
|
||||||
|
if (entryCount%2 == 1) {
|
||||||
|
observationJson.setCategories(categoryConcepts1);
|
||||||
|
observationJson.setCode(codeableConceptField1);
|
||||||
|
observationJson.setCode_concept_id(codeableConceptId1);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(CODE_INDEX, codeableConceptId1, codeJson1Document, CODE_DOCUMENT_TYPE));
|
||||||
|
} else {
|
||||||
|
observationJson.setCategories(categoryConcepts2);
|
||||||
|
observationJson.setCode(codeableConceptField2);
|
||||||
|
observationJson.setCode_concept_id(codeableConceptId2);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(CODE_INDEX, codeableConceptId2, codeJson2Document, CODE_DOCUMENT_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar observationDate = new GregorianCalendar();
|
||||||
|
observationDate.add(Calendar.HOUR, -10 + entryCount);
|
||||||
|
Date effectiveDtm = observationDate.getTime();
|
||||||
|
observationJson.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(observationJson);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(OBSERVATION_INDEX, identifier,observationDocument, OBSERVATION_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
if (createdPatientObservationMap.containsKey(subject)) {
|
||||||
|
Map<String, List<Date>> observationCodeMap = createdPatientObservationMap.get(subject);
|
||||||
|
if (observationCodeMap.containsKey(observationJson.getCode_concept_id())) {
|
||||||
|
List<Date> observationDates = observationCodeMap.get(observationJson.getCode_concept_id());
|
||||||
|
// Want dates to be sorted in descending order
|
||||||
|
observationDates.add(0, effectiveDtm);
|
||||||
|
// Only keep the three most recent dates for later check.
|
||||||
|
if(observationDates.size() > 3) {
|
||||||
|
observationDates.remove(3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(effectiveDtm);
|
||||||
|
observationCodeMap.put(observationJson.getCode_concept_id(), observationDates);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(effectiveDtm);
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(), observationDates);
|
||||||
|
createdPatientObservationMap.put(subject, codeObservationMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000L);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchConfig;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.CodeSystemHash;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.shadehapi.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestElasticsearchConfig.class } )
|
||||||
|
public class LastNElasticsearchSvcSingleObservationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ElasticsearchSvcImpl elasticsearchSvc;
|
||||||
|
|
||||||
|
static ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
final String RESOURCEPID = "123";
|
||||||
|
final String SUBJECTID = "4567";
|
||||||
|
final String SUBJECTTYPEANDID = "Patient/4567";
|
||||||
|
final Date EFFECTIVEDTM = new Date();
|
||||||
|
final String FIRSTCATEGORYTEXT = "Test Codeable Concept Field for first category";
|
||||||
|
final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category";
|
||||||
|
final String CATEGORYSECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-category";
|
||||||
|
final String CATEGORYTHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-category";
|
||||||
|
final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate";
|
||||||
|
final String FIRSTCATEGORYFIRSTCODINGDISPLAY = "test-heart-rate display";
|
||||||
|
final String FIRSTCATEGORYSECONDCODINGCODE = "test-alt-heart-rate";
|
||||||
|
final String FIRSTCATEGORYSECONDCODINGDISPLAY = "test-alt-heart-rate display";
|
||||||
|
final String FIRSTCATEGORYTHIRDCODINGCODE = "test-2nd-alt-heart-rate";
|
||||||
|
final String FIRSTCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-heart-rate display";
|
||||||
|
final String SECONDCATEGORYTEXT = "Test Codeable Concept Field for for second category";
|
||||||
|
final String SECONDCATEGORYFIRSTCODINGCODE = "test-vital-signs";
|
||||||
|
final String SECONDCATEGORYFIRSTCODINGDISPLAY = "test-vital-signs display";
|
||||||
|
final String SECONDCATEGORYSECONDCODINGCODE = "test-alt-vitals";
|
||||||
|
final String SECONDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display";
|
||||||
|
final String SECONDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals";
|
||||||
|
final String SECONDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals display";
|
||||||
|
final String THIRDCATEGORYTEXT = "Test Codeable Concept Field for third category";
|
||||||
|
final String THIRDCATEGORYFIRSTCODINGCODE = "test-vital-panel";
|
||||||
|
final String THIRDCATEGORYFIRSTCODINGDISPLAY = "test-vitals-panel display";
|
||||||
|
final String THIRDCATEGORYSECONDCODINGCODE = "test-alt-vitals-panel";
|
||||||
|
final String THIRDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display";
|
||||||
|
final String THIRDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals-panel";
|
||||||
|
final String THIRDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals-panel display";
|
||||||
|
|
||||||
|
final String OBSERVATIONSINGLECODEID = UUID.randomUUID().toString();
|
||||||
|
final String OBSERVATIONCODETEXT = "Test Codeable Concept Field for Code";
|
||||||
|
final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code";
|
||||||
|
final String CODEFIRSTCODINGCODE = "test-code";
|
||||||
|
final String CODEFIRSTCODINGDISPLAY = "test-code display";
|
||||||
|
final String CODESECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-code";
|
||||||
|
final String CODESECONDCODINGCODE = "test-alt-code";
|
||||||
|
final String CODESECONDCODINGDISPLAY = "test-alt-code display";
|
||||||
|
final String CODETHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-code";
|
||||||
|
final String CODETHIRDCODINGCODE = "test-second-alt-code";
|
||||||
|
final String CODETHIRDCODINGDISPLAY = "test-second-alt-code display";
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleObservationQuery() throws IOException {
|
||||||
|
|
||||||
|
createSingleObservation();
|
||||||
|
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", SUBJECTID);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
// execute Observation ID search - Terms Aggregation
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(1, observationIdsOnly.size());
|
||||||
|
ObservationJson observationIdOnly = observationIdsOnly.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observationIdOnly.getIdentifier());
|
||||||
|
|
||||||
|
// execute Observation ID search - Composite Aggregation
|
||||||
|
searchRequestIdsOnly = elasticsearchSvc.buildObservationCompositeSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
observationIdsOnly = elasticsearchSvc.buildObservationCompositeResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(1, observationIdsOnly.size());
|
||||||
|
observationIdOnly = observationIdsOnly.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observationIdOnly.getIdentifier());
|
||||||
|
|
||||||
|
// execute full Observation search - Terms Aggregation
|
||||||
|
SearchRequest searchRequestAllFields = elasticsearchSvc.buildObservationAllFieldsTermsSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequestAllFields);
|
||||||
|
List<ObservationJson> observations = elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
|
||||||
|
validateFullObservationSearch(observations);
|
||||||
|
|
||||||
|
// execute full Observation search - Composite Aggregation
|
||||||
|
searchRequestAllFields= elasticsearchSvc.buildObservationAllFieldsCompositeSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
response = elasticsearchSvc.executeSearchRequest(searchRequestAllFields);
|
||||||
|
observations = elasticsearchSvc.buildObservationCompositeResults(response);
|
||||||
|
|
||||||
|
validateFullObservationSearch(observations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateFullObservationSearch(List<ObservationJson> observations) throws IOException {
|
||||||
|
|
||||||
|
assertEquals(1, observations.size());
|
||||||
|
ObservationJson observation = observations.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observation.getIdentifier());
|
||||||
|
|
||||||
|
assertEquals(SUBJECTTYPEANDID, observation.getSubject());
|
||||||
|
assertEquals(RESOURCEPID, observation.getIdentifier());
|
||||||
|
assertEquals(EFFECTIVEDTM, observation.getEffectiveDtm());
|
||||||
|
assertEquals(OBSERVATIONSINGLECODEID, observation.getCode_concept_id());
|
||||||
|
|
||||||
|
List<String> category_concept_text_values = observation.getCategory_concept_text();
|
||||||
|
assertEquals(3,category_concept_text_values.size());
|
||||||
|
assertEquals(FIRSTCATEGORYTEXT, category_concept_text_values.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYTEXT, category_concept_text_values.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTEXT, category_concept_text_values.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_systems = observation.getCategory_coding_system();
|
||||||
|
assertEquals(3,category_codings_systems.size());
|
||||||
|
List<String> category_coding_systems = category_codings_systems.get(0);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
category_coding_systems = category_codings_systems.get(1);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
category_coding_systems = category_codings_systems.get(2);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_codes = observation.getCategory_coding_code();
|
||||||
|
assertEquals(3, category_codings_codes.size());
|
||||||
|
List<String> category_coding_codes = category_codings_codes.get(0);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(FIRSTCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(FIRSTCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(FIRSTCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
category_coding_codes = category_codings_codes.get(1);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(SECONDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(SECONDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
category_coding_codes = category_codings_codes.get(2);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(THIRDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(THIRDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_displays = observation.getCategory_coding_display();
|
||||||
|
assertEquals(3, category_codings_displays.size());
|
||||||
|
List<String> category_coding_displays = category_codings_displays.get(0);
|
||||||
|
assertEquals(FIRSTCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(FIRSTCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(FIRSTCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
category_coding_displays = category_codings_displays.get(1);
|
||||||
|
assertEquals(3, category_coding_displays.size());
|
||||||
|
assertEquals(SECONDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(SECONDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
category_coding_displays = category_codings_displays.get(2);
|
||||||
|
assertEquals(3, category_coding_displays.size());
|
||||||
|
assertEquals(THIRDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(THIRDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_code_system_hashes = observation.getCategory_coding_code_system_hash();
|
||||||
|
assertEquals(3, category_codings_code_system_hashes.size());
|
||||||
|
List<String> category_coding_code_system_hashes = category_codings_code_system_hashes.get(0);
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
category_coding_code_system_hashes = category_codings_code_system_hashes.get(1);
|
||||||
|
assertEquals(3, category_coding_code_system_hashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
category_coding_code_system_hashes = category_codings_code_system_hashes.get(2);
|
||||||
|
assertEquals(3, category_coding_code_system_hashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
|
||||||
|
String code_concept_text_values = observation.getCode_concept_text();
|
||||||
|
assertEquals(OBSERVATIONCODETEXT, code_concept_text_values);
|
||||||
|
|
||||||
|
List<String> code_coding_systems = observation.getCode_coding_system();
|
||||||
|
assertEquals(3,code_coding_systems.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGSYSTEM, code_coding_systems.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGSYSTEM, code_coding_systems.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGSYSTEM, code_coding_systems.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_codes = observation.getCode_coding_code();
|
||||||
|
assertEquals(3, code_coding_codes.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGCODE, code_coding_codes.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGCODE, code_coding_codes.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGCODE, code_coding_codes.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_display = observation.getCode_coding_display();
|
||||||
|
assertEquals(3, code_coding_display.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGDISPLAY, code_coding_display.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGDISPLAY, code_coding_display.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGDISPLAY, code_coding_display.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_code_system_hash = observation.getCode_coding_code_system_hash();
|
||||||
|
assertEquals(3, code_coding_code_system_hash.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), code_coding_code_system_hash.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE)), code_coding_code_system_hash.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE)), code_coding_code_system_hash.get(2));
|
||||||
|
|
||||||
|
// Retrieve all Observation codes
|
||||||
|
SearchRequest searchRequest = elasticsearchSvc.buildObservationCodesSearchRequest(1000);
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
List<CodeJson> codes = elasticsearchSvc.buildCodeResult(response);
|
||||||
|
assertEquals(1, codes.size());
|
||||||
|
CodeJson persistedObservationCode = codes.get(0);
|
||||||
|
|
||||||
|
String persistedCodeConceptID = persistedObservationCode.getCodeableConceptId();
|
||||||
|
assertEquals(OBSERVATIONSINGLECODEID, persistedCodeConceptID);
|
||||||
|
String persistedCodeConceptText = persistedObservationCode.getCodeableConceptText();
|
||||||
|
assertEquals(OBSERVATIONCODETEXT, persistedCodeConceptText);
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingSystems = persistedObservationCode.getCoding_system();
|
||||||
|
assertEquals(3,persistedCodeCodingSystems.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGSYSTEM, persistedCodeCodingSystems.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGSYSTEM, persistedCodeCodingSystems.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGSYSTEM, persistedCodeCodingSystems.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingCodes = persistedObservationCode.getCoding_code();
|
||||||
|
assertEquals(3, persistedCodeCodingCodes.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGCODE, persistedCodeCodingCodes.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGCODE, persistedCodeCodingCodes.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGCODE, persistedCodeCodingCodes.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingDisplays = persistedObservationCode.getCoding_display();
|
||||||
|
assertEquals(3, persistedCodeCodingDisplays.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGDISPLAY, persistedCodeCodingDisplays.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGDISPLAY, persistedCodeCodingDisplays.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGDISPLAY, persistedCodeCodingDisplays.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingCodeSystemHashes = persistedObservationCode.getCoding_code_system_hash();
|
||||||
|
assertEquals(3, persistedCodeCodingCodeSystemHashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(2));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSingleObservation() throws IOException {
|
||||||
|
ObservationJson indexedObservation = new ObservationJson();
|
||||||
|
indexedObservation.setIdentifier(RESOURCEPID);
|
||||||
|
indexedObservation.setSubject(SUBJECTTYPEANDID);
|
||||||
|
indexedObservation.setEffectiveDtm(EFFECTIVEDTM);
|
||||||
|
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> categoryConcepts = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText(FIRSTCATEGORYTEXT);
|
||||||
|
category1.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, FIRSTCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category1.add(new Coding(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE, FIRSTCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category1.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE, FIRSTCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText(SECONDCATEGORYTEXT);
|
||||||
|
category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE, SECONDCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category2.add(new Coding(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE, SECONDCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category2.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE, SECONDCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
List<Coding> category3 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText(THIRDCATEGORYTEXT);
|
||||||
|
category3.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE, THIRDCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category3.add(new Coding(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE, THIRDCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category3.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE, THIRDCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept3.setCoding(category3);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept3);
|
||||||
|
indexedObservation.setCategories(categoryConcepts);
|
||||||
|
|
||||||
|
// Create CodeableConcept for Code with three codings.
|
||||||
|
indexedObservation.setCode_concept_id(OBSERVATIONSINGLECODEID);
|
||||||
|
CodeableConcept codeableConceptField = new CodeableConcept().setText(OBSERVATIONCODETEXT);
|
||||||
|
codeableConceptField.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY));
|
||||||
|
codeableConceptField.addCoding(new Coding(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE, CODESECONDCODINGDISPLAY));
|
||||||
|
codeableConceptField.addCoding(new Coding(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE, CODETHIRDCODINGDISPLAY));
|
||||||
|
indexedObservation.setCode(codeableConceptField);
|
||||||
|
|
||||||
|
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(indexedObservation);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.OBSERVATION_INDEX, RESOURCEPID, observationDocument, IndexConstants.OBSERVATION_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
CodeJson observationCode = new CodeJson(codeableConceptField, OBSERVATIONSINGLECODEID);
|
||||||
|
String codeDocument = ourMapperNonPrettyPrint.writeValueAsString(observationCode);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.CODE_INDEX, OBSERVATIONSINGLECODEID, codeDocument, IndexConstants.CODE_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000L);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchV5Config;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestElasticsearchV5Config.class } )
|
||||||
|
public class LastNElasticsearchV5SvcMultipleObservationsTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ElasticsearchV5SvcImpl elasticsearchSvc;
|
||||||
|
|
||||||
|
private static ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
private Map<String,Map<String,List<Date>>> createdPatientObservationMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
// createMultiplePatientsAndObservations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
@Test
|
||||||
|
public void testLastNNoCriteriaQuery() throws IOException {
|
||||||
|
|
||||||
|
// execute Observation ID search (Terms Aggregation) last 3 observations for each patient
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, null, 3);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
validateQueryResponse(observationIdsOnly);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateQueryResponse(List<ObservationJson> observationIdsOnly) {
|
||||||
|
assertEquals(60, observationIdsOnly.size());
|
||||||
|
|
||||||
|
// Observation documents should be grouped by subject, then by observation code, and then sorted by effective date/time
|
||||||
|
// within each observation code. Verify the grouping by creating a nested Map.
|
||||||
|
Map<String,Map<String, List<Date>>> queriedPatientObservationMap = new HashMap<>();
|
||||||
|
ObservationJson previousObservationJson = null;
|
||||||
|
for (ObservationJson observationJson : observationIdsOnly) {
|
||||||
|
assertNotNull(observationJson.getIdentifier());
|
||||||
|
assertNotNull(observationJson.getSubject());
|
||||||
|
assertNotNull(observationJson.getCode_concept_id());
|
||||||
|
assertNotNull(observationJson.getEffectiveDtm());
|
||||||
|
if (previousObservationJson == null) {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap);
|
||||||
|
} else if (observationJson.getSubject().equals(previousObservationJson.getSubject())) {
|
||||||
|
if (observationJson.getCode_concept_id().equals(previousObservationJson.getCode_concept_id())) {
|
||||||
|
queriedPatientObservationMap.get(observationJson.getSubject()).get(observationJson.getCode_concept_id()).
|
||||||
|
add(observationJson.getEffectiveDtm());
|
||||||
|
} else {
|
||||||
|
Map<String, List<Date>> codeObservationDateMap = queriedPatientObservationMap.get(observationJson.getSubject());
|
||||||
|
// Ensure that code concept was not already retrieved out of order for this subject/patient.
|
||||||
|
assertFalse(codeObservationDateMap.containsKey(observationJson.getCode_concept_id()));
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
codeObservationDateMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ensure that subject/patient was not already retrieved out of order
|
||||||
|
assertFalse(queriedPatientObservationMap.containsKey(observationJson.getSubject()));
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(observationJson.getEffectiveDtm());
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(),observationDates);
|
||||||
|
queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap);
|
||||||
|
}
|
||||||
|
previousObservationJson = observationJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally check that only the most recent effective date/time values were returned and in the correct order.
|
||||||
|
for(String subjectId : queriedPatientObservationMap.keySet()) {
|
||||||
|
Map<String, List<Date>> queriedObservationCodeMap = queriedPatientObservationMap.get(subjectId);
|
||||||
|
Map<String, List<Date>> createdObservationCodeMap = createdPatientObservationMap.get(subjectId);
|
||||||
|
for(String observationCode : queriedObservationCodeMap.keySet()) {
|
||||||
|
List<Date> queriedObservationDates = queriedObservationCodeMap.get(observationCode);
|
||||||
|
List<Date> createdObservationDates = createdObservationCodeMap.get(observationCode);
|
||||||
|
for (int dateIdx=0; dateIdx<queriedObservationDates.size(); dateIdx++) {
|
||||||
|
assertEquals(createdObservationDates.get(dateIdx), queriedObservationDates.get(dateIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNMultiPatientMultiCodeHashMultiCategoryHash() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
ReferenceParam patientParam = new ReferenceParam("Patient", "", "8");
|
||||||
|
searchParameterMap.add("patient", patientParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(10, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeCodeOnlyCategoryCodeOnly() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam ("test-heart-rate");
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("test-code-1");
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(5, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeSystemOnlyCategorySystemOnly() throws IOException {
|
||||||
|
// Include subject and patient
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", null);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", null);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(10, observationIdsOnly.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLastNCodeCodeTextCategoryTextOnly() throws IOException {
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3");
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam("test-heart-rate display");
|
||||||
|
categoryParam.setModifier(TokenParamModifier.TEXT);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam("test-code-1 display");
|
||||||
|
codeParam.setModifier(TokenParamModifier.TEXT);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 100);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(5, observationIdsOnly.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMultiplePatientsAndObservations() throws IOException {
|
||||||
|
// Create CodeableConcepts for two Codes, each with three codings.
|
||||||
|
String codeableConceptId1 = UUID.randomUUID().toString();
|
||||||
|
CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code");
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-1", "test-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display"));
|
||||||
|
codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display"));
|
||||||
|
CodeJson codeJson1 = new CodeJson(codeableConceptField1, codeableConceptId1);
|
||||||
|
String codeJson1Document = ourMapperNonPrettyPrint.writeValueAsString(codeJson1);
|
||||||
|
|
||||||
|
String codeableConceptId2 = UUID.randomUUID().toString();
|
||||||
|
CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code");
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mycodes.org/fhir/observation-code", "test-code-2", "test-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display"));
|
||||||
|
codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display"));
|
||||||
|
CodeJson codeJson2 = new CodeJson(codeableConceptField2, codeableConceptId2);
|
||||||
|
String codeJson2Document = ourMapperNonPrettyPrint.writeValueAsString(codeJson2);
|
||||||
|
|
||||||
|
// Create CodeableConcepts for two categories, each with three codings.
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
category1.add(new Coding("http://mycodes.org/fhir/observation-category", "test-heart-rate", "test-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display"));
|
||||||
|
category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display"));
|
||||||
|
List<CodeableConcept> categoryConcepts1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category");
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts1.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
category2.add(new Coding("http://mycodes.org/fhir/observation-category", "test-vital-signs", "test-vital-signs display"));
|
||||||
|
category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display"));
|
||||||
|
category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display"));
|
||||||
|
List<CodeableConcept> categoryConcepts2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category");
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts2.add(categoryCodeableConcept2);
|
||||||
|
|
||||||
|
for (int patientCount = 0; patientCount < 10 ; patientCount++) {
|
||||||
|
|
||||||
|
String subject = "Patient/"+patientCount;
|
||||||
|
|
||||||
|
for ( int entryCount = 0; entryCount < 10 ; entryCount++ ) {
|
||||||
|
|
||||||
|
ObservationJson observationJson = new ObservationJson();
|
||||||
|
String identifier = String.valueOf((entryCount + patientCount*10));
|
||||||
|
observationJson.setIdentifier(identifier);
|
||||||
|
observationJson.setSubject(subject);
|
||||||
|
|
||||||
|
if (entryCount%2 == 1) {
|
||||||
|
observationJson.setCategories(categoryConcepts1);
|
||||||
|
observationJson.setCode(codeableConceptField1);
|
||||||
|
observationJson.setCode_concept_id(codeableConceptId1);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.CODE_INDEX, codeableConceptId1, codeJson1Document, IndexConstants.CODE_DOCUMENT_TYPE));
|
||||||
|
} else {
|
||||||
|
observationJson.setCategories(categoryConcepts2);
|
||||||
|
observationJson.setCode(codeableConceptField2);
|
||||||
|
observationJson.setCode_concept_id(codeableConceptId2);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.CODE_INDEX, codeableConceptId2, codeJson2Document, IndexConstants.CODE_DOCUMENT_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar observationDate = new GregorianCalendar();
|
||||||
|
observationDate.add(Calendar.HOUR, -10 + entryCount);
|
||||||
|
Date effectiveDtm = observationDate.getTime();
|
||||||
|
observationJson.setEffectiveDtm(effectiveDtm);
|
||||||
|
|
||||||
|
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(observationJson);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.OBSERVATION_INDEX, identifier,observationDocument, IndexConstants.OBSERVATION_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
if (createdPatientObservationMap.containsKey(subject)) {
|
||||||
|
Map<String, List<Date>> observationCodeMap = createdPatientObservationMap.get(subject);
|
||||||
|
if (observationCodeMap.containsKey(observationJson.getCode_concept_id())) {
|
||||||
|
List<Date> observationDates = observationCodeMap.get(observationJson.getCode_concept_id());
|
||||||
|
// Want dates to be sorted in descending order
|
||||||
|
observationDates.add(0, effectiveDtm);
|
||||||
|
// Only keep the three most recent dates for later check.
|
||||||
|
if(observationDates.size() > 3) {
|
||||||
|
observationDates.remove(3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(effectiveDtm);
|
||||||
|
observationCodeMap.put(observationJson.getCode_concept_id(), observationDates);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayList<Date> observationDates = new ArrayList<>();
|
||||||
|
observationDates.add(effectiveDtm);
|
||||||
|
Map<String, List<Date>> codeObservationMap = new HashMap<>();
|
||||||
|
codeObservationMap.put(observationJson.getCode_concept_id(), observationDates);
|
||||||
|
createdPatientObservationMap.put(subject, codeObservationMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000L);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
|
@ -0,0 +1,346 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchV5Config;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.util.CodeSystemHash;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestElasticsearchV5Config.class } )
|
||||||
|
public class LastNElasticsearchV5SvcSingleObservationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ElasticsearchV5SvcImpl elasticsearchSvc;
|
||||||
|
|
||||||
|
static ObjectMapper ourMapperNonPrettyPrint;
|
||||||
|
|
||||||
|
final String RESOURCEPID = "123";
|
||||||
|
final String SUBJECTID = "4567";
|
||||||
|
final String SUBJECTTYPEANDID = "Patient/4567";
|
||||||
|
final Date EFFECTIVEDTM = new Date();
|
||||||
|
final String FIRSTCATEGORYTEXT = "Test Codeable Concept Field for first category";
|
||||||
|
final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category";
|
||||||
|
final String CATEGORYSECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-category";
|
||||||
|
final String CATEGORYTHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-category";
|
||||||
|
final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate";
|
||||||
|
final String FIRSTCATEGORYFIRSTCODINGDISPLAY = "test-heart-rate display";
|
||||||
|
final String FIRSTCATEGORYSECONDCODINGCODE = "test-alt-heart-rate";
|
||||||
|
final String FIRSTCATEGORYSECONDCODINGDISPLAY = "test-alt-heart-rate display";
|
||||||
|
final String FIRSTCATEGORYTHIRDCODINGCODE = "test-2nd-alt-heart-rate";
|
||||||
|
final String FIRSTCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-heart-rate display";
|
||||||
|
final String SECONDCATEGORYTEXT = "Test Codeable Concept Field for for second category";
|
||||||
|
final String SECONDCATEGORYFIRSTCODINGCODE = "test-vital-signs";
|
||||||
|
final String SECONDCATEGORYFIRSTCODINGDISPLAY = "test-vital-signs display";
|
||||||
|
final String SECONDCATEGORYSECONDCODINGCODE = "test-alt-vitals";
|
||||||
|
final String SECONDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display";
|
||||||
|
final String SECONDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals";
|
||||||
|
final String SECONDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals display";
|
||||||
|
final String THIRDCATEGORYTEXT = "Test Codeable Concept Field for third category";
|
||||||
|
final String THIRDCATEGORYFIRSTCODINGCODE = "test-vital-panel";
|
||||||
|
final String THIRDCATEGORYFIRSTCODINGDISPLAY = "test-vitals-panel display";
|
||||||
|
final String THIRDCATEGORYSECONDCODINGCODE = "test-alt-vitals-panel";
|
||||||
|
final String THIRDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display";
|
||||||
|
final String THIRDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals-panel";
|
||||||
|
final String THIRDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals-panel display";
|
||||||
|
|
||||||
|
final String OBSERVATIONSINGLECODEID = UUID.randomUUID().toString();
|
||||||
|
final String OBSERVATIONCODETEXT = "Test Codeable Concept Field for Code";
|
||||||
|
final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code";
|
||||||
|
final String CODEFIRSTCODINGCODE = "test-code";
|
||||||
|
final String CODEFIRSTCODINGDISPLAY = "test-code display";
|
||||||
|
final String CODESECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-code";
|
||||||
|
final String CODESECONDCODINGCODE = "test-alt-code";
|
||||||
|
final String CODESECONDCODINGDISPLAY = "test-alt-code display";
|
||||||
|
final String CODETHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-code";
|
||||||
|
final String CODETHIRDCODINGCODE = "test-second-alt-code";
|
||||||
|
final String CODETHIRDCODINGDISPLAY = "test-second-alt-code display";
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
ourMapperNonPrettyPrint = new ObjectMapper();
|
||||||
|
ourMapperNonPrettyPrint.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
ourMapperNonPrettyPrint.disable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
ourMapperNonPrettyPrint.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Before
|
||||||
|
public void before() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws IOException {
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
|
||||||
|
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testSingleObservationQuery() throws IOException {
|
||||||
|
|
||||||
|
createSingleObservation();
|
||||||
|
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
ReferenceParam subjectParam = new ReferenceParam("Patient", "", SUBJECTID);
|
||||||
|
searchParameterMap.add("subject", subjectParam);
|
||||||
|
TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("category", categoryParam);
|
||||||
|
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
|
||||||
|
searchParameterMap.add("code", codeParam);
|
||||||
|
|
||||||
|
// execute Observation ID search - Terms Aggregation
|
||||||
|
/* SearchRequest searchRequestIdsOnly = elasticsearchSvc.buildObservationTermsSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
SearchResponse responseIds = elasticsearchSvc.executeSearchRequest(searchRequestIdsOnly);
|
||||||
|
List<ObservationJson> observationIdsOnly = elasticsearchSvc.buildObservationTermsResults(responseIds);
|
||||||
|
|
||||||
|
assertEquals(1, observationIdsOnly.size());
|
||||||
|
ObservationJson observationIdOnly = observationIdsOnly.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observationIdOnly.getIdentifier());
|
||||||
|
|
||||||
|
// execute full Observation search - Terms Aggregation
|
||||||
|
SearchRequest searchRequestAllFields = elasticsearchSvc.buildObservationAllFieldsTermsSearchRequest(1000, searchParameterMap, 3);
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequestAllFields);
|
||||||
|
List<ObservationJson> observations = elasticsearchSvc.buildObservationTermsResults(response);
|
||||||
|
|
||||||
|
validateFullObservationSearch(observations);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateFullObservationSearch(List<ObservationJson> observations) throws IOException {
|
||||||
|
|
||||||
|
assertEquals(1, observations.size());
|
||||||
|
ObservationJson observation = observations.get(0);
|
||||||
|
assertEquals(RESOURCEPID, observation.getIdentifier());
|
||||||
|
|
||||||
|
assertEquals(SUBJECTTYPEANDID, observation.getSubject());
|
||||||
|
assertEquals(RESOURCEPID, observation.getIdentifier());
|
||||||
|
assertEquals(EFFECTIVEDTM, observation.getEffectiveDtm());
|
||||||
|
assertEquals(OBSERVATIONSINGLECODEID, observation.getCode_concept_id());
|
||||||
|
|
||||||
|
List<String> category_concept_text_values = observation.getCategory_concept_text();
|
||||||
|
assertEquals(3,category_concept_text_values.size());
|
||||||
|
assertEquals(FIRSTCATEGORYTEXT, category_concept_text_values.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYTEXT, category_concept_text_values.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTEXT, category_concept_text_values.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_systems = observation.getCategory_coding_system();
|
||||||
|
assertEquals(3,category_codings_systems.size());
|
||||||
|
List<String> category_coding_systems = category_codings_systems.get(0);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
category_codings_systems.get(1);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
category_codings_systems.get(2);
|
||||||
|
assertEquals(3, category_coding_systems.size());
|
||||||
|
assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0));
|
||||||
|
assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1));
|
||||||
|
assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_codes = observation.getCategory_coding_code();
|
||||||
|
assertEquals(3, category_codings_codes.size());
|
||||||
|
List<String> category_coding_codes = category_codings_codes.get(0);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(FIRSTCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(FIRSTCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(FIRSTCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
category_coding_codes = category_codings_codes.get(1);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(SECONDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(SECONDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
category_coding_codes = category_codings_codes.get(2);
|
||||||
|
assertEquals(3, category_coding_codes.size());
|
||||||
|
assertEquals(THIRDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0));
|
||||||
|
assertEquals(THIRDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_displays = observation.getCategory_coding_display();
|
||||||
|
assertEquals(3, category_codings_displays.size());
|
||||||
|
List<String> category_coding_displays = category_codings_displays.get(0);
|
||||||
|
assertEquals(FIRSTCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(FIRSTCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(FIRSTCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
category_coding_displays = category_codings_displays.get(1);
|
||||||
|
assertEquals(3, category_coding_displays.size());
|
||||||
|
assertEquals(SECONDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(SECONDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(SECONDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
category_coding_displays = category_codings_displays.get(2);
|
||||||
|
assertEquals(3, category_coding_displays.size());
|
||||||
|
assertEquals(THIRDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0));
|
||||||
|
assertEquals(THIRDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1));
|
||||||
|
assertEquals(THIRDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2));
|
||||||
|
|
||||||
|
List<List<String>> category_codings_code_system_hashes = observation.getCategory_coding_code_system_hash();
|
||||||
|
assertEquals(3, category_codings_code_system_hashes.size());
|
||||||
|
List<String> category_coding_code_system_hashes = category_codings_code_system_hashes.get(0);
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
category_coding_code_system_hashes = category_codings_code_system_hashes.get(1);
|
||||||
|
assertEquals(3, category_coding_code_system_hashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
category_coding_code_system_hashes = category_codings_code_system_hashes.get(2);
|
||||||
|
assertEquals(3, category_coding_code_system_hashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2));
|
||||||
|
|
||||||
|
String code_concept_text_values = observation.getCode_concept_text();
|
||||||
|
assertEquals(OBSERVATIONCODETEXT, code_concept_text_values);
|
||||||
|
|
||||||
|
List<String> code_coding_systems = observation.getCode_coding_system();
|
||||||
|
assertEquals(3,code_coding_systems.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGSYSTEM, code_coding_systems.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGSYSTEM, code_coding_systems.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGSYSTEM, code_coding_systems.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_codes = observation.getCode_coding_code();
|
||||||
|
assertEquals(3, code_coding_codes.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGCODE, code_coding_codes.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGCODE, code_coding_codes.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGCODE, code_coding_codes.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_display = observation.getCode_coding_display();
|
||||||
|
assertEquals(3, code_coding_display.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGDISPLAY, code_coding_display.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGDISPLAY, code_coding_display.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGDISPLAY, code_coding_display.get(2));
|
||||||
|
|
||||||
|
List<String> code_coding_code_system_hash = observation.getCode_coding_code_system_hash();
|
||||||
|
assertEquals(3, code_coding_code_system_hash.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), code_coding_code_system_hash.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE)), code_coding_code_system_hash.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE)), code_coding_code_system_hash.get(2));
|
||||||
|
|
||||||
|
// Retrieve all Observation codes
|
||||||
|
/* SearchRequest searchRequest = elasticsearchSvc.buildObservationCodesSearchRequest(1000);
|
||||||
|
SearchResponse response = elasticsearchSvc.executeSearchRequest(searchRequest);
|
||||||
|
List<CodeJson> codes = elasticsearchSvc.buildCodeResult(response);
|
||||||
|
assertEquals(1, codes.size());
|
||||||
|
CodeJson persistedObservationCode = codes.get(0);
|
||||||
|
|
||||||
|
String persistedCodeConceptID = persistedObservationCode.getCodeableConceptId();
|
||||||
|
assertEquals(OBSERVATIONSINGLECODEID, persistedCodeConceptID);
|
||||||
|
String persistedCodeConceptText = persistedObservationCode.getCodeableConceptText();
|
||||||
|
assertEquals(OBSERVATIONCODETEXT, persistedCodeConceptText);
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingSystems = persistedObservationCode.getCoding_system();
|
||||||
|
assertEquals(3,persistedCodeCodingSystems.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGSYSTEM, persistedCodeCodingSystems.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGSYSTEM, persistedCodeCodingSystems.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGSYSTEM, persistedCodeCodingSystems.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingCodes = persistedObservationCode.getCoding_code();
|
||||||
|
assertEquals(3, persistedCodeCodingCodes.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGCODE, persistedCodeCodingCodes.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGCODE, persistedCodeCodingCodes.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGCODE, persistedCodeCodingCodes.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingDisplays = persistedObservationCode.getCoding_display();
|
||||||
|
assertEquals(3, persistedCodeCodingDisplays.size());
|
||||||
|
assertEquals(CODEFIRSTCODINGDISPLAY, persistedCodeCodingDisplays.get(0));
|
||||||
|
assertEquals(CODESECONDCODINGDISPLAY, persistedCodeCodingDisplays.get(1));
|
||||||
|
assertEquals(CODETHIRDCODINGDISPLAY, persistedCodeCodingDisplays.get(2));
|
||||||
|
|
||||||
|
List<String> persistedCodeCodingCodeSystemHashes = persistedObservationCode.getCoding_code_system_hash();
|
||||||
|
assertEquals(3, persistedCodeCodingCodeSystemHashes.size());
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(0));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(1));
|
||||||
|
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(2));
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSingleObservation() throws IOException {
|
||||||
|
ObservationJson indexedObservation = new ObservationJson();
|
||||||
|
indexedObservation.setIdentifier(RESOURCEPID);
|
||||||
|
indexedObservation.setSubject(SUBJECTTYPEANDID);
|
||||||
|
indexedObservation.setEffectiveDtm(EFFECTIVEDTM);
|
||||||
|
|
||||||
|
// Add three CodeableConcepts for category
|
||||||
|
List<CodeableConcept> categoryConcepts = new ArrayList<>();
|
||||||
|
// Create three codings and first category CodeableConcept
|
||||||
|
List<Coding> category1 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText(FIRSTCATEGORYTEXT);
|
||||||
|
category1.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, FIRSTCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category1.add(new Coding(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE, FIRSTCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category1.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE, FIRSTCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept1.setCoding(category1);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept1);
|
||||||
|
// Create three codings and second category CodeableConcept
|
||||||
|
List<Coding> category2 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText(SECONDCATEGORYTEXT);
|
||||||
|
category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE, SECONDCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category2.add(new Coding(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE, SECONDCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category2.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE, SECONDCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept2.setCoding(category2);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept2);
|
||||||
|
// Create three codings and third category CodeableConcept
|
||||||
|
List<Coding> category3 = new ArrayList<>();
|
||||||
|
CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText(THIRDCATEGORYTEXT);
|
||||||
|
category3.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE, THIRDCATEGORYFIRSTCODINGDISPLAY));
|
||||||
|
category3.add(new Coding(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE, THIRDCATEGORYSECONDCODINGDISPLAY));
|
||||||
|
category3.add(new Coding(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE, THIRDCATEGORYTHIRDCODINGDISPLAY));
|
||||||
|
categoryCodeableConcept3.setCoding(category3);
|
||||||
|
categoryConcepts.add(categoryCodeableConcept3);
|
||||||
|
indexedObservation.setCategories(categoryConcepts);
|
||||||
|
|
||||||
|
// Create CodeableConcept for Code with three codings.
|
||||||
|
indexedObservation.setCode_concept_id(OBSERVATIONSINGLECODEID);
|
||||||
|
CodeableConcept codeableConceptField = new CodeableConcept().setText(OBSERVATIONCODETEXT);
|
||||||
|
codeableConceptField.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY));
|
||||||
|
codeableConceptField.addCoding(new Coding(CODESECONDCODINGSYSTEM, CODESECONDCODINGCODE, CODESECONDCODINGDISPLAY));
|
||||||
|
codeableConceptField.addCoding(new Coding(CODETHIRDCODINGSYSTEM, CODETHIRDCODINGCODE, CODETHIRDCODINGDISPLAY));
|
||||||
|
indexedObservation.setCode(codeableConceptField);
|
||||||
|
|
||||||
|
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(indexedObservation);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.OBSERVATION_INDEX, RESOURCEPID, observationDocument, IndexConstants.OBSERVATION_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
CodeJson observationCode = new CodeJson(codeableConceptField, OBSERVATIONSINGLECODEID);
|
||||||
|
String codeDocument = ourMapperNonPrettyPrint.writeValueAsString(observationCode);
|
||||||
|
assertTrue(elasticsearchSvc.performIndex(IndexConstants.CODE_INDEX, OBSERVATIONSINGLECODEID, codeDocument, IndexConstants.CODE_DOCUMENT_TYPE));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000L);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.PopularProperties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory")
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class TestElasticsearchConfig {
|
||||||
|
|
||||||
|
private final String elasticsearchHost = "127.0.0.1";
|
||||||
|
private final String elasticsearchUserId = "";
|
||||||
|
private final String elasticsearchPassword = "";
|
||||||
|
|
||||||
|
private static final String ELASTIC_VERSION = "6.5.4";
|
||||||
|
|
||||||
|
|
||||||
|
@Bean()
|
||||||
|
public ElasticsearchSvcImpl myElasticsearchSvc() throws IOException {
|
||||||
|
int elasticsearchPort = embeddedElasticSearch().getHttpPort();
|
||||||
|
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUserId, elasticsearchPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EmbeddedElastic embeddedElasticSearch() {
|
||||||
|
EmbeddedElastic embeddedElastic = null;
|
||||||
|
try {
|
||||||
|
embeddedElastic = EmbeddedElastic.builder()
|
||||||
|
.withElasticVersion(ELASTIC_VERSION)
|
||||||
|
.withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.HTTP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID())
|
||||||
|
.withStartTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
.start();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return embeddedElastic;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.lastn.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchV5SvcImpl;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic;
|
||||||
|
import pl.allegro.tech.embeddedelasticsearch.PopularProperties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory")
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class TestElasticsearchV5Config {
|
||||||
|
|
||||||
|
private final String elasticsearchHost = "127.0.0.1";
|
||||||
|
private final String elasticsearchUserId = "";
|
||||||
|
private final String elasticsearchPassword = "";
|
||||||
|
|
||||||
|
private static final String ELASTIC_VERSION = "5.6.16";
|
||||||
|
|
||||||
|
|
||||||
|
@Bean()
|
||||||
|
public ElasticsearchV5SvcImpl myElasticsearchSvc() throws IOException {
|
||||||
|
int elasticsearchPort = embeddedElasticSearch().getHttpPort();
|
||||||
|
return new ElasticsearchV5SvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUserId, elasticsearchPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EmbeddedElastic embeddedElasticSearch() {
|
||||||
|
EmbeddedElastic embeddedElastic = null;
|
||||||
|
try {
|
||||||
|
embeddedElastic = EmbeddedElastic.builder()
|
||||||
|
.withElasticVersion(ELASTIC_VERSION)
|
||||||
|
.withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.HTTP_PORT, 0)
|
||||||
|
.withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID())
|
||||||
|
.withStartTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
.start();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return embeddedElastic;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
pom.xml
3
pom.xml
|
@ -1561,7 +1561,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.basepom.maven</groupId>
|
<groupId>org.basepom.maven</groupId>
|
||||||
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.4.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>de.jpdigital</groupId>
|
<groupId>de.jpdigital</groupId>
|
||||||
|
@ -2466,6 +2466,7 @@
|
||||||
<module>hapi-fhir-structures-r5</module>
|
<module>hapi-fhir-structures-r5</module>
|
||||||
<module>hapi-fhir-validation-resources-r5</module>
|
<module>hapi-fhir-validation-resources-r5</module>
|
||||||
<module>hapi-fhir-igpacks</module>
|
<module>hapi-fhir-igpacks</module>
|
||||||
|
<module>hapi-fhir-elasticsearch-6</module>
|
||||||
<module>hapi-fhir-jpaserver-model</module>
|
<module>hapi-fhir-jpaserver-model</module>
|
||||||
<module>hapi-fhir-jpaserver-searchparam</module>
|
<module>hapi-fhir-jpaserver-searchparam</module>
|
||||||
<module>hapi-fhir-jpaserver-subscription</module>
|
<module>hapi-fhir-jpaserver-subscription</module>
|
||||||
|
|
Loading…
Reference in New Issue