Skip to content

Commit

Permalink
MAINT-2145 Add Identiiers to concept JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
QuyenLy87 authored and dmcgihtsdo committed Mar 30, 2023
1 parent b034a9c commit 6a82af5
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 11 deletions.
23 changes: 22 additions & 1 deletion src/main/java/org/snomed/snowstorm/core/data/domain/Concept.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.snomed.snowstorm.core.data.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.Sets;
Expand All @@ -24,7 +25,7 @@

@Document(indexName = "concept")
@JsonPropertyOrder({"conceptId", "descendantCount", "fsn", "pt", "active", "effectiveTime", "released", "releasedEffectiveTime", "inactivationIndicator", "associationTargets",
"moduleId", "definitionStatus", "definitionStatusId", "descriptions", "classAxioms", "gciAxioms", "relationships", "validationResults"})
"moduleId", "definitionStatus", "definitionStatusId", "descriptions", "classAxioms", "gciAxioms", "relationships", "alternateIdentifiers", "validationResults"})
public class Concept extends SnomedComponent<Concept> implements ConceptView, SnomedComponentWithInactivationIndicator, SnomedComponentWithAssociations {

public interface Fields extends SnomedComponent.Fields {
Expand Down Expand Up @@ -81,6 +82,9 @@ public interface Fields extends SnomedComponent.Fields {
@Transient
private Long descendantCount;

@Transient
private List<Identifier> identifiers;

@Transient
private List<InvalidContent> validationResults;

Expand All @@ -93,6 +97,7 @@ public Concept() {
classAxioms = new HashSet<>();
generalConceptInclusionAxioms = new HashSet<>();
inactivationIndicatorMembers = new ArrayList<>();
identifiers = new ArrayList<>();
validationResults = new ArrayList<>();
}

Expand Down Expand Up @@ -230,6 +235,11 @@ public Concept addDescription(Description description) {
return this;
}

public Concept addIdentifier(Identifier identifier) {
identifiers.add(identifier);
return this;
}

public Concept addRelationship(String typeId, String destinationId) {
return addRelationship(new Relationship(typeId, destinationId));
}
Expand Down Expand Up @@ -427,6 +437,17 @@ public void setValidationResults(final List<InvalidContent> validationResults) {
this.validationResults = validationResults;
}

@JsonProperty("alternateIdentifiers")
@JsonView(value = View.Component.class)
@Override
public List<Identifier> getIdentifiers() {
return identifiers;
}

public void setIdentifiers(List<Identifier> identifiers) {
this.identifiers = identifiers;
}

public void clone(Concept concept) {
setConceptId(concept.getConceptId());
setEffectiveTimeI(concept.getEffectiveTimeI());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ public interface ConceptView {

Set<Axiom> getGciAxioms();

List<Identifier> getIdentifiers();

List<InvalidContent> getValidationResults();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonView;
import org.snomed.snowstorm.rest.View;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
Expand All @@ -14,7 +14,7 @@
import java.util.Objects;

@Document(indexName = "identifier")
@JsonPropertyOrder({"alternateIdentifier", "effectiveTime", "active", "moduleId", "identifierSchemeId", "referencedComponentId", "released", "releasedEffectiveTime"})
@JsonPropertyOrder({"alternateIdentifier", "effectiveTime", "active", "moduleId", "identifierSchemeId", "identifierScheme", "referencedComponentId", "released", "releasedEffectiveTime"})
public class Identifier extends SnomedComponent<Identifier> {

public interface Fields extends SnomedComponent.Fields {
Expand Down Expand Up @@ -44,6 +44,9 @@ public interface Fields extends SnomedComponent.Fields {
@Size(min = 5, max = 18)
private String referencedComponentId;

@Transient
private ConceptMini identifierScheme;

public Identifier() {
active = true;
setModuleId(Concepts.CORE_MODULE);
Expand Down Expand Up @@ -100,6 +103,15 @@ public String getIdentifierSchemeId() {
return identifierSchemeId;
}

public void setIdentifierScheme(ConceptMini identifierScheme) {
this.identifierScheme = identifierScheme;
}

@JsonView(value = View.Component.class)
public ConceptMini getIdentifierScheme() {
return identifierScheme;
}

@Override
protected Object[] getReleaseHashObjects() {
return new Object[]{alternateIdentifier, active, getModuleId(), identifierSchemeId, referencedComponentId};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public class ConceptService extends ComponentService {
@Autowired
private DescriptionService descriptionService;

@Autowired
private IdentifierComponentService identifierComponentService;

@Autowired
@Lazy
private ReferenceSetMemberService referenceSetMemberService;
Expand Down Expand Up @@ -159,7 +162,7 @@ public Collection<Concept> find(BranchCriteria branchCriteria, String path, Coll
if (isEmpty(conceptIds)) {
return Collections.emptySet();
}
return doFind(conceptIds, languageDialects, branchCriteria, PageRequest.of(0, conceptIds.size()), true, true, path).getContent();
return doFind(conceptIds, languageDialects, branchCriteria, PageRequest.of(0, conceptIds.size()), true, true, true, path).getContent();
}

public Page<Concept> find(List<Long> conceptIds, List<LanguageDialect> languageDialects, String path, PageRequest pageRequest) {
Expand Down Expand Up @@ -271,7 +274,7 @@ public Page<Concept> findAll(String path, List<LanguageDialect> languageDialects

private Page<Concept> doFind(Collection<?> conceptIds, List<LanguageDialect> languageDialects, BranchTimepoint branchTimepoint, PageRequest pageRequest) {
final BranchCriteria branchCriteria = getBranchCriteria(branchTimepoint);
return doFind(conceptIds, languageDialects, branchCriteria, pageRequest, true, true, branchTimepoint.getBranchPath());
return doFind(conceptIds, languageDialects, branchCriteria, pageRequest, true, true, true, branchTimepoint.getBranchPath());
}

protected BranchCriteria getBranchCriteria(String branchPath) {
Expand Down Expand Up @@ -323,7 +326,7 @@ private ResultMapPage<String, ConceptMini> findConceptMinis(BranchCriteria branc
if (conceptIds != null && conceptIds.isEmpty()) {
return new ResultMapPage<>(new HashMap<>(), 0);
}
Page<Concept> concepts = doFind(conceptIds, languageDialects, branchCriteria, pageRequest, false, false, null);
Page<Concept> concepts = doFind(conceptIds, languageDialects, branchCriteria, pageRequest, false, false, false, null);
Map<String, Concept> conceptMap = new HashMap<>();
for (Concept concept : concepts) {
String id = concept.getId();
Expand All @@ -343,7 +346,7 @@ private ResultMapPage<String, ConceptMini> findConceptMinis(BranchCriteria branc
private void populateConceptMinis(BranchCriteria branchCriteria, Map<String, ConceptMini> minisToPopulate, List<LanguageDialect> languageDialects) {
if (!minisToPopulate.isEmpty()) {
Set<String> conceptIds = minisToPopulate.keySet();
Page<Concept> concepts = doFind(conceptIds, languageDialects, branchCriteria, PageRequest.of(0, conceptIds.size()), false, false, null);
Page<Concept> concepts = doFind(conceptIds, languageDialects, branchCriteria, PageRequest.of(0, conceptIds.size()), false, false, false, null);
concepts.getContent().forEach(c -> {
ConceptMini conceptMini = minisToPopulate.get(c.getConceptId());
conceptMini.setDefinitionStatus(c.getDefinitionStatus());
Expand All @@ -359,6 +362,7 @@ private Page<Concept> doFind(
PageRequest pageRequest,
boolean includeRelationships,
boolean includeDescriptionInactivationInfo,
boolean includeIdentifiers,
String branchPath) {

final TimerUtil timer = new TimerUtil("Find concept", Level.DEBUG);
Expand Down Expand Up @@ -422,6 +426,10 @@ private Page<Concept> doFind(
timer.checkpoint("get axioms " + getFetchCount(conceptIdMap.size()));
}

if (includeIdentifiers) {
identifierComponentService.joinIdentifiers(branchCriteria, conceptIdMap, conceptMiniMap, languageDialects, timer);
}

// Fetch ConceptMini definition statuses
for (List<String> conceptIds : Iterables.partition(conceptMiniMap.keySet(), CLAUSE_LIMIT)) {
queryBuilder.withQuery(boolQuery()
Expand Down Expand Up @@ -744,7 +752,7 @@ Map<String, Concept> getExistingConceptsForSave(Collection<Concept> concepts, Co
if (!conceptIds.isEmpty()) {
for (List<String> conceptIdPartition : Iterables.partition(conceptIds, 500)) {
final BranchCriteria branchCriteria = versionControlHelper.getBranchCriteriaIncludingOpenCommit(commit);
final List<Concept> existingConcepts = doFind(conceptIdPartition, DEFAULT_LANGUAGE_DIALECTS, branchCriteria, PageRequest.of(0, conceptIds.size()), true, true, null).getContent();
final List<Concept> existingConcepts = doFind(conceptIdPartition, DEFAULT_LANGUAGE_DIALECTS, branchCriteria, PageRequest.of(0, conceptIds.size()), true, true, true, null).getContent();
for (Concept existingConcept : existingConcepts) {
existingConceptsMap.put(existingConcept.getConceptId(), existingConcept);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.snomed.snowstorm.core.data.services;

import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import io.kaicode.elasticvc.api.BranchCriteria;
import io.kaicode.elasticvc.api.BranchService;
import io.kaicode.elasticvc.api.ComponentService;
Expand All @@ -12,10 +13,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snomed.snowstorm.config.Config;
import org.snomed.snowstorm.core.data.domain.Concepts;
import org.snomed.snowstorm.core.data.domain.Identifier;
import org.snomed.snowstorm.core.data.domain.*;
import org.snomed.snowstorm.core.data.repositories.IdentifierRepository;
import org.snomed.snowstorm.core.data.services.pojo.IdentifierSearchRequest;
import org.snomed.snowstorm.core.pojo.LanguageDialect;
import org.snomed.snowstorm.core.util.TimerUtil;
import org.snomed.snowstorm.ecl.ECLQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
Expand All @@ -24,6 +26,7 @@
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsIterator;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -150,4 +153,45 @@ private QueryBuilder buildIdentifierQuery(IdentifierSearchRequest searchRequest,

return query;
}
void joinIdentifiers(BranchCriteria branchCriteria, Map<String, Concept> conceptIdMap, Map<String, ConceptMini> conceptMiniMap, List<LanguageDialect> languageDialects, TimerUtil timer) {
final NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

final Set<String> allConceptIds = new HashSet<>();
if (conceptIdMap != null) {
allConceptIds.addAll(conceptIdMap.keySet());
}
if (allConceptIds.isEmpty()) {
return;
}

// Fetch Identifier
for (List<String> conceptIds : Iterables.partition(allConceptIds, CLAUSE_LIMIT)) {
queryBuilder.withQuery(boolQuery()
.must(branchCriteria.getEntityBranchCriteria(Identifier.class))
.must(termQuery(Identifier.Fields.ACTIVE, true))
.must(termsQuery(Identifier.Fields.REFERENCED_COMPONENT_ID, conceptIds)))
.withPageable(LARGE_PAGE);
try (final SearchHitsIterator<Identifier> identifiers = elasticsearchTemplate.searchForStream(queryBuilder.build(), Identifier.class)) {
identifiers.forEachRemaining(hit -> {
Identifier identifier = hit.getContent();
identifier.setIdentifierScheme(getConceptMini(conceptMiniMap, identifier.getIdentifierSchemeId(), languageDialects));

// Join Identifiers to concepts for loading whole concepts use case.
final String referencedComponentId = identifier.getReferencedComponentId();
if (conceptIdMap != null) {
final Concept concept = conceptIdMap.get(referencedComponentId);
if (concept != null) {
concept.addIdentifier(identifier);
}
}
});
}
}
if (timer != null) timer.checkpoint("get Identifier " + getFetchCount(allConceptIds.size()));
}

private static ConceptMini getConceptMini(Map<String, ConceptMini> conceptMiniMap, String id, List<LanguageDialect> languageDialects) {
if (id == null) return new ConceptMini((String)null, languageDialects);
return conceptMiniMap.computeIfAbsent(id, i -> new ConceptMini(id, languageDialects));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ void testConceptEndpointFields() throws IOException, ServiceException {
checkFields(responseBody);
LinkedHashMap<String, Object> properties = objectMapper.readValue(responseBody, LinkedHashMap.class);
assertEquals("[conceptId, fsn, pt, active, effectiveTime, released, releasedEffectiveTime, moduleId, definitionStatus, " +
"descriptions, classAxioms, gciAxioms, relationships, validationResults]", properties.keySet().toString());
"descriptions, classAxioms, gciAxioms, relationships, alternateIdentifiers, validationResults]", properties.keySet().toString());
Object fsn = properties.get("fsn");
assertEquals("LinkedHashMap", fsn.getClass().getSimpleName());
assertEquals("{term=Wallace \"69\" side-to-end anastomosis - action (qualifier value), lang=en}", fsn.toString());
Expand Down

0 comments on commit 6a82af5

Please sign in to comment.