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>about.html</ignoredResource>
|
||||
<ignoredResource>changelog.xml</ignoredResource>
|
||||
<ignoredResource>.*/favicon.ico$</ignoredResource>
|
||||
<ignoredResource>Log4j-charsets.properties</ignoredResource>
|
||||
</ignoredResourcePatterns>
|
||||
</configuration>
|
||||
</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>
|
||||
<groupId>org.basepom.maven</groupId>
|
||||
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>de.jpdigital</groupId>
|
||||
|
@ -2466,6 +2466,7 @@
|
|||
<module>hapi-fhir-structures-r5</module>
|
||||
<module>hapi-fhir-validation-resources-r5</module>
|
||||
<module>hapi-fhir-igpacks</module>
|
||||
<module>hapi-fhir-elasticsearch-6</module>
|
||||
<module>hapi-fhir-jpaserver-model</module>
|
||||
<module>hapi-fhir-jpaserver-searchparam</module>
|
||||
<module>hapi-fhir-jpaserver-subscription</module>
|
||||
|
|
Loading…
Reference in New Issue