Skip to content

Commit

Permalink
#287 - Elastic search connection
Browse files Browse the repository at this point in the history
- add selectable attribute
- adapt to context being an object instead of a string
- convert entries between the model in elastic search and the api format
- add (clumsy) queries considering filters (this is not solved in a sophisticated manor)
- add endpoint to query for relatives of entries
- add endpoint to retrieve criteria profile data (uiprofile, context and termcodes)
  • Loading branch information
michael-82 committed Jun 7, 2024
1 parent ece4b9a commit 234faef
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package de.numcodex.feasibility_gui_backend.terminology;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.numcodex.feasibility_gui_backend.terminology.api.CategoryEntry;
import de.numcodex.feasibility_gui_backend.terminology.api.CriteriaProfileData;
import de.numcodex.feasibility_gui_backend.terminology.api.TerminologyEntry;
import de.numcodex.feasibility_gui_backend.terminology.persistence.*;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand All @@ -31,6 +33,9 @@ public class TerminologyService {

private String uiProfilePath;

@NonNull
private ObjectMapper jsonUtil;

@Value("${app.ontologyOrder}")
private List<String> sortedCategories;
private Map<UUID, TerminologyEntry> terminologyEntries = new HashMap<>();
Expand All @@ -42,7 +47,8 @@ public TerminologyService(@Value("${app.ontologyFolder}") String uiProfilePath,
UiProfileRepository uiProfileRepository,
TermCodeRepository termCodeRepository,
ContextualizedTermCodeRepository contextualizedTermCodeRepository,
MappingRepository mappingRepository) throws IOException {
MappingRepository mappingRepository,
ObjectMapper jsonUtil) throws IOException {
this.uiProfilePath = uiProfilePath;
readInTerminologyEntries();
generateTerminologyEntriesWithoutDirectChildren();
Expand All @@ -51,6 +57,7 @@ public TerminologyService(@Value("${app.ontologyFolder}") String uiProfilePath,
this.termCodeRepository = termCodeRepository;
this.contextualizedTermCodeRepository = contextualizedTermCodeRepository;
this.mappingRepository = mappingRepository;
this.jsonUtil = jsonUtil;
}

private void readInTerminologyEntries() throws IOException {
Expand Down Expand Up @@ -204,41 +211,41 @@ public List<String> getIntersection(String criteriaSetUrl, List<String> contextT
return contextualizedTermCodeRepository.filterByCriteriaSetUrl(criteriaSetUrl, contextTermCodeHashList);
}

public List<CriteriaProfileData> getCriteriaProfileData(List<String> criterionIds) {
public List<CriteriaProfileData> getCriteriaProfileData(List<String> criteriaIds) {
List<CriteriaProfileData> results = new ArrayList<>();

for (String id : criterionIds) {
CriteriaProfileData cse = new CriteriaProfileData();
for (String id : criteriaIds) {
CriteriaProfileData criteriaProfileData = new CriteriaProfileData();
TermCode tc = termCodeRepository.findTermCodeByContextualizedTermcodeHash(id).orElse(null);
Context c = termCodeRepository.findContextByContextualizedTermcodeHash(id).orElse(null);
cse.setId(id);
criteriaProfileData.setId(id);
try {
cse.setUiProfile(getUiProfile(id));
} catch (UiProfileNotFoundException e) {
cse.setUiProfile(null);
de.numcodex.feasibility_gui_backend.terminology.api.UiProfile uip = jsonUtil.readValue(getUiProfile(id), de.numcodex.feasibility_gui_backend.terminology.api.UiProfile.class);
criteriaProfileData.setUiProfile(uip);
} catch (UiProfileNotFoundException | JsonProcessingException e) {
criteriaProfileData.setUiProfile(null);
}
if (c != null) {
cse.setContext(de.numcodex.feasibility_gui_backend.common.api.TermCode.builder()
criteriaProfileData.setContext(de.numcodex.feasibility_gui_backend.common.api.TermCode.builder()
.code(c.getCode())
.display(c.getDisplay())
.system(c.getSystem())
.version(c.getVersion())
.build());
} else {
cse.setContext(null);
criteriaProfileData.setContext(null);
}
if (tc != null) {
cse.setTermCode(de.numcodex.feasibility_gui_backend.common.api.TermCode.builder()
criteriaProfileData.setTermCode(de.numcodex.feasibility_gui_backend.common.api.TermCode.builder()
.code(tc.getCode())
.display(tc.getDisplay())
.system(tc.getSystem())
.version(tc.getVersion())
.build());
} else {
cse.setTermCode(null);
criteriaProfileData.setTermCode(null);
}

results.add(cse);
results.add(criteriaProfileData);
}

return results;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public class CriteriaProfileData {
@EqualsAndHashCode.Include
private TermCode termCode ;
@JsonProperty("uiProfile")
private String uiProfile;
private UiProfile uiProfile;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class EsSearchResultEntry {
private String terminology;
private String termcode;
private String kdsModule;
private boolean selectable;

public static EsSearchResultEntry of(OntologyListItemDocument ontologyListItemDocument) {
return EsSearchResultEntry.builder()
Expand All @@ -24,6 +25,7 @@ public static EsSearchResultEntry of(OntologyListItemDocument ontologyListItemDo
.terminology(ontologyListItemDocument.getTerminology())
.termcode(ontologyListItemDocument.getTermcode())
.kdsModule(ontologyListItemDocument.getKdsModule())
.selectable(ontologyListItemDocument.isSelectable())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.numcodex.feasibility_gui_backend.terminology.api;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

import java.util.List;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UiProfile {
private String name;
private boolean timeRestrictionAllowed;
private String valueDefinition;
private List<AttributeDefinition> attributeDefinitions;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations;
Expand All @@ -19,14 +21,15 @@
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.util.Pair;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.*;

@Service
@Slf4j
@ConditionalOnExpression("${app.elastic.enabled}")
public class TerminologyEsService {
private ElasticsearchOperations operations;

Expand All @@ -44,8 +47,9 @@ public TerminologyEsService(ElasticsearchOperations operations, OntologyItemEsRe
this.ontologyListItemEsRepository = ontologyListItemEsRepository;
}

public OntologyItemDocument getOntologyItemByHash(String hash) {
return ontologyItemEsRepository.findById(hash).orElseThrow(OntologyItemNotFoundException::new);
public EsSearchResultEntry getSearchResultEntryByHash(String hash) {
var ontologyItem = ontologyListItemEsRepository.findById(hash).orElseThrow(OntologyItemNotFoundException::new);
return EsSearchResultEntry.of(ontologyItem);
}

public List<TermFilter> getAvailableFilters() {
Expand Down Expand Up @@ -85,6 +89,9 @@ public OntologySearchResult performOntologySearchWithRepo(String keyword,
.build();
}

/*
I know this is kinda stupid, but it works for now (availability has to be included though).
*/
public EsSearchResult performOntologySearchWithRepoAndPaging(String keyword,
@Nullable List<String> context,
@Nullable List<String> kdsModule,
Expand All @@ -93,8 +100,61 @@ public EsSearchResult performOntologySearchWithRepoAndPaging(String keyword,
@Nullable int pageSize,
@Nullable int page) {

// var searchHitPage = ontologyListItemEsRepository.findByNameContainingIgnoreCaseOrTermcodeContainingIgnoreCase(keyword, keyword, PageRequest.of(page, pageSize));
var searchHitPage = ontologyListItemEsRepository.findByNameOrTermcodeMultiMatch(keyword, PageRequest.of(page, pageSize));

List<Pair<String, List<String>>> filterList = new ArrayList<>();
if (context != null) {
filterList.add(Pair.of("context.code", context));
}
if (kdsModule != null) {
filterList.add(Pair.of("kds_module", kdsModule));
}
if (terminology != null) {
filterList.add(Pair.of("terminology", terminology));
}

Page<OntologyListItemDocument> searchHitPage;

switch (filterList.size()) {
case 0 -> {
searchHitPage = ontologyListItemEsRepository
.findByNameOrTermcodeMultiMatch0Filters(keyword,
PageRequest.of(page, pageSize));
}
case 1 -> {searchHitPage = ontologyListItemEsRepository
.findByNameOrTermcodeMultiMatch1Filter(keyword,
filterList.get(0).getFirst(),
filterList.get(0).getSecond(),
PageRequest.of(page, pageSize));
}
case 2 -> {searchHitPage = ontologyListItemEsRepository
.findByNameOrTermcodeMultiMatch2Filters(keyword,
filterList.get(0).getFirst(),
filterList.get(0).getSecond(),
filterList.get(1).getFirst(),
filterList.get(1).getSecond(),
PageRequest.of(page, pageSize));
}
case 3 -> {searchHitPage = ontologyListItemEsRepository
.findByNameOrTermcodeMultiMatch3Filters(keyword,
filterList.get(0).getFirst(),
filterList.get(0).getSecond(),
filterList.get(1).getFirst(),
filterList.get(1).getSecond(),
filterList.get(2).getFirst(),
filterList.get(2).getSecond(),
PageRequest.of(page, pageSize));
}
default -> {searchHitPage = ontologyListItemEsRepository
.findByNameOrTermcodeMultiMatch3Filters(keyword,
filterList.get(0).getFirst(),
filterList.get(0).getSecond(),
filterList.get(1).getFirst(),
filterList.get(1).getSecond(),
filterList.get(2).getFirst(),
filterList.get(2).getSecond(),
PageRequest.of(page, pageSize));
}
}
List<EsSearchResultEntry> ontologyItems = new ArrayList<>();

searchHitPage.getContent().forEach(hit -> ontologyItems.add(EsSearchResultEntry.of(hit)));
Expand Down Expand Up @@ -219,4 +279,14 @@ private NativeQuery buildNativeQuery(String keyword,
.build();
return query;
}

public OntologyItemRelationsDocument getOntologyItemRelationsByHash(String hash) {
var ontologyItem = ontologyItemEsRepository.findById(hash).orElseThrow(OntologyItemNotFoundException::new);
return OntologyItemRelationsDocument.builder()
.translations(ontologyItem.getTranslations())
.parents(ontologyItem.getParents())
.children(ontologyItem.getChildren())
.relatedTerms(ontologyItem.getRelatedTerms())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.numcodex.feasibility_gui_backend.terminology.es.model;

import de.numcodex.feasibility_gui_backend.common.api.TermCode;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Data;
Expand All @@ -17,7 +18,7 @@ public class OntologyItemDocument {
private @Id String id;
private String name;
private int availability;
private String context;
private TermCode context;
private String terminology;
private String termcode;
@Field(name = "kds_module") private String kdsModule;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.numcodex.feasibility_gui_backend.terminology.es.model;

import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Collection;

@Data
@Builder
@Document(indexName = "ontology")
public class OntologyItemRelationsDocument {
@Field(type = FieldType.Nested, includeInParent = true, name = "translations")
private Collection<Translation> translations;
@Field(type = FieldType.Nested, includeInParent = true, name = "parents")
private Collection<Relative> parents;
@Field(type = FieldType.Nested, includeInParent = true, name = "children")
private Collection<Relative> children;
@Field(type = FieldType.Nested, includeInParent = true, name = "related_terms")
private Collection<Relative> relatedTerms;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public class OntologyListItemDocument {
private String termcode;
@Field(name = "kds_module")
private String kdsModule;
private boolean selectable;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import lombok.Builder;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Field;

@Data
@Builder
public class Relative {

private String name;
@Field(name = "contextualized_termcode_hash")
private String contextualizedTermcodeHash;
}
Loading

0 comments on commit 234faef

Please sign in to comment.