use of com.b2international.snowowl.snomed.core.domain.SnomedConcepts in project snow-owl by b2ihealthcare.
the class SnomedDescendantsExpander method expand.
@Override
protected void expand(List<SnomedConcept> results, final Set<String> conceptIds, Options descendantExpandOptions, boolean direct) {
try {
final int limit = getLimit(descendantExpandOptions);
final ExpressionBuilder expression = Expressions.builder();
expression.filter(active());
final ExpressionBuilder descendantFilter = Expressions.builder();
if (stated) {
descendantFilter.should(statedParents(conceptIds));
if (!direct) {
descendantFilter.should(statedAncestors(conceptIds));
}
} else {
descendantFilter.should(parents(conceptIds));
if (!direct) {
descendantFilter.should(ancestors(conceptIds));
}
}
expression.filter(descendantFilter.build());
final Query<SnomedConceptDocument> query = Query.select(SnomedConceptDocument.class).where(expression.build()).limit((conceptIds.size() == 1 && limit == 0) ? limit : Integer.MAX_VALUE).build();
final RevisionSearcher searcher = context().service(RevisionSearcher.class);
final Hits<SnomedConceptDocument> hits = searcher.search(query);
if (hits.getTotal() < 1) {
final SnomedConcepts descendants = new SnomedConcepts(0, 0);
for (SnomedConcept concept : results) {
if (stated) {
concept.setStatedDescendants(descendants);
} else {
concept.setDescendants(descendants);
}
}
return;
}
// XXX won't work if number of results is greater than one, either use custom ConceptSearch or figure out how to expand descendants effectively
if (conceptIds.size() == 1 && limit == 0) {
for (SnomedConcept concept : results) {
final SnomedConcepts descendants = new SnomedConcepts(0, hits.getTotal());
if (stated) {
concept.setStatedDescendants(descendants);
} else {
concept.setDescendants(descendants);
}
}
return;
}
final Multimap<String, String> descendantsByAncestor = TreeMultimap.create();
for (SnomedConceptDocument hit : hits) {
final Set<String> parentsAndAncestors = newHashSet();
if (stated) {
parentsAndAncestors.addAll(LongSets.toStringSet(hit.getStatedParents()));
if (!direct) {
parentsAndAncestors.addAll(LongSets.toStringSet(hit.getStatedAncestors()));
}
} else {
parentsAndAncestors.addAll(LongSets.toStringSet(hit.getParents()));
if (!direct) {
parentsAndAncestors.addAll(LongSets.toStringSet(hit.getAncestors()));
}
}
parentsAndAncestors.retainAll(conceptIds);
for (String ancestor : parentsAndAncestors) {
descendantsByAncestor.put(ancestor, hit.getId());
}
}
final Collection<String> componentIds = newHashSet(descendantsByAncestor.values());
if (limit > 0 && !componentIds.isEmpty()) {
// query descendants again
final SnomedConcepts descendants = SnomedRequests.prepareSearchConcept().all().filterByIds(componentIds).setLocales(locales()).setExpand(descendantExpandOptions.get("expand", Options.class)).build().execute(context());
final Map<String, SnomedConcept> descendantsById = newHashMap();
descendantsById.putAll(Maps.uniqueIndex(descendants, SnomedConcept::getId));
for (SnomedConcept concept : results) {
final Collection<String> descendantIds = descendantsByAncestor.get(concept.getId());
final List<SnomedConcept> currentDescendants = FluentIterable.from(descendantIds).limit(limit).transform(Functions.forMap(descendantsById)).toList();
final SnomedConcepts descendantConcepts = new SnomedConcepts(currentDescendants, null, limit, descendantIds.size());
if (stated) {
concept.setStatedDescendants(descendantConcepts);
} else {
concept.setDescendants(descendantConcepts);
}
}
} else {
for (SnomedConcept concept : results) {
final Collection<String> descendantIds = descendantsByAncestor.get(concept.getId());
final SnomedConcepts descendants = new SnomedConcepts(limit, descendantIds.size());
if (stated) {
concept.setStatedDescendants(descendants);
} else {
concept.setDescendants(descendants);
}
}
}
} catch (IOException e) {
throw SnowowlRuntimeException.wrap(e);
}
}
use of com.b2international.snowowl.snomed.core.domain.SnomedConcepts in project snow-owl by b2ihealthcare.
the class SnomedConceptCreateRequest method isValidParentage.
private boolean isValidParentage(TransactionContext context, String requiredSuperType, Collection<String> parents) {
// first check if the requiredSuperType is specified in the parents collection
if (parents.contains(requiredSuperType)) {
return true;
}
// if not, then check if any of the specified parents is subTypeOf the requiredSuperType
final long superTypeIdLong = Long.parseLong(requiredSuperType);
final SnomedConcepts parentConcepts = SnomedRequests.prepareSearchConcept().setLimit(parents.size()).filterByIds(parents).build().execute(context);
for (SnomedConcept parentConcept : parentConcepts) {
if (parentConcept.getParentIds() != null) {
if (PrimitiveSets.newLongOpenHashSet(parentConcept.getParentIds()).contains(superTypeIdLong)) {
return true;
}
}
if (parentConcept.getAncestorIds() != null) {
if (PrimitiveSets.newLongOpenHashSet(parentConcept.getAncestorIds()).contains(superTypeIdLong)) {
return true;
}
}
}
return false;
}
use of com.b2international.snowowl.snomed.core.domain.SnomedConcepts in project snow-owl by b2ihealthcare.
the class SnomedQueryOptimizer method optimize.
@Override
public QueryExpressionDiffs optimize(BranchContext context, Options params) {
final Collection<QueryExpression> inclusions = params.getCollection(QueryOptimizer.OptionKey.INCLUSIONS, QueryExpression.class);
final List<ExtendedLocale> locales = params.getList(QueryOptimizer.OptionKey.LOCALES, ExtendedLocale.class);
final EclParser eclParser = context.service(EclParser.class);
final LoadingCache<String, ExpressionConstraint> eclCache = CacheBuilder.newBuilder().build(CacheLoader.from(eclParser::parse));
final Multimap<String, QueryExpression> singleConceptInclusions = FluentIterable.from(inclusions).filter(ex -> isSingleConceptExpression(eclCache, ex.getQuery())).index(ex -> toSingleConceptId(eclCache, ex.getQuery()));
// if there are no single concept inclusions to optimize, exit early
if (singleConceptInclusions.isEmpty()) {
return new QueryExpressionDiffs(Collections.emptyList());
}
// Record the ancestors (both direct and indirect) of each single concept inclusion
final Multimap<String, QueryExpression> membersByAncestor = HashMultimap.create();
SnomedRequests.prepareSearchConcept().filterByIds(singleConceptInclusions.keySet()).setLimit(singleConceptInclusions.keySet().size()).stream(context).flatMap(SnomedConcepts::stream).forEach(child -> {
final Collection<QueryExpression> childExpressions = singleConceptInclusions.get(child.getId());
final List<String> parentIds = child.getParentIdsAsString();
final List<String> ancestorIds = child.getAncestorIdsAsString();
parentIds.forEach(parentId -> {
if (!IComponent.ROOT_ID.equals(parentId) && !Concepts.ROOT_CONCEPT.equals(parentId)) {
membersByAncestor.putAll(parentId, childExpressions);
}
});
ancestorIds.forEach(ancestorId -> {
if (!IComponent.ROOT_ID.equals(ancestorId) && !Concepts.ROOT_CONCEPT.equals(ancestorId)) {
membersByAncestor.putAll(ancestorId, childExpressions);
}
});
});
// Get number of referenced descendants (taking possible duplicates into account)
final Map<String, Long> uniqueDescendantsByParent = ImmutableMap.copyOf(Maps.transformValues(membersByAncestor.asMap(), descendants -> descendants.stream().map(QueryExpression::getQuery).distinct().count()));
final ImmutableList.Builder<QueryExpressionDiff> diffs = ImmutableList.builder();
// references can be replaced with a single << expression.
for (Entry<String, Long> uniqueDescendantsByParentEntry : uniqueDescendantsByParent.entrySet()) {
SnomedConcept parent = SnomedRequests.prepareGetConcept(uniqueDescendantsByParentEntry.getKey()).setLocales(locales).setExpand("pt(),descendants(direct:false,limit:0)").build().execute(context);
final String parentId = parent.getId();
final int referencedDescendants = Ints.checkedCast(uniqueDescendantsByParent.get(parentId));
final int totalDescendants = parent.getDescendants().getTotal();
if (totalDescendants == referencedDescendants) {
final List<QueryExpression> remove = List.copyOf(membersByAncestor.get(parentId).stream().filter(ex -> !ex.isPinned()).collect(Collectors.toList()));
// The optimization is a "net win" if we can remove at least two clauses from the original
if (remove.size() > 1) {
final QueryExpression replacement = new QueryExpression(IDs.base64UUID(), String.format("<%s%s", parent.getId(), getTerm(parent)), false);
final List<QueryExpression> addToInclusion = List.of(replacement);
final List<QueryExpression> addToExclusion = List.of();
final QueryExpressionDiff diff = new QueryExpressionDiff(addToInclusion, addToExclusion, remove);
diffs.add(diff);
}
}
}
return new QueryExpressionDiffs(diffs.build());
}
use of com.b2international.snowowl.snomed.core.domain.SnomedConcepts in project snow-owl by b2ihealthcare.
the class SnomedSimpleTypeRefSetDSVExporter method writeValues.
private void writeValues(BufferedWriter writer, SnomedConcepts chunk) throws IOException {
List<String> dataRow = newArrayList();
for (SnomedConcept concept : chunk) {
dataRow.clear();
for (AbstractSnomedDsvExportItem exportItem : exportItems) {
switch(exportItem.getType()) {
case DESCRIPTION:
{
final ComponentIdSnomedDsvExportItem descriptionItem = (ComponentIdSnomedDsvExportItem) exportItem;
final String typeId = descriptionItem.getComponentId();
int occurrences = descriptionCount.get(typeId);
final Map<String, String> termsById = concept.getDescriptions().stream().filter(d -> typeId.equals(d.getTypeId())).collect(Collectors.toMap(SnomedDescription::getId, SnomedDescription::getTerm));
addCells(dataRow, occurrences, includeDescriptionId, termsById);
break;
}
case RELATIONSHIP:
{
final ComponentIdSnomedDsvExportItem relationshipItem = (ComponentIdSnomedDsvExportItem) exportItem;
for (Integer propertyGroup : propertyCountByGroup.keySet()) {
final String typeId = relationshipItem.getComponentId();
final Map<String, Integer> groupOccurrences = propertyCountByGroup.getOrDefault(propertyGroup, NO_OCCURRENCES);
final int occurrences = groupOccurrences.getOrDefault(typeId, 0);
concept.getRelationships().stream().filter(r -> typeId.equals(r.getTypeId()) && Objects.equals(r.getRelationshipGroup(), propertyGroup) && (Concepts.INFERRED_RELATIONSHIP.equals(r.getCharacteristicTypeId()) || Concepts.ADDITIONAL_RELATIONSHIP.equals(r.getCharacteristicTypeId()))).forEach(relationship -> {
if (relationship.hasValue()) {
addCells(dataRow, occurrences, includeRelationshipId, ImmutableMap.of(relationship.getValue(), ""));
} else {
addCells(dataRow, occurrences, includeRelationshipId, ImmutableMap.of(relationship.getDestinationId(), getPreferredTerm(relationship.getDestination())));
}
});
}
break;
}
case DATAYPE:
{
final DatatypeSnomedDsvExportItem datatypeItem = (DatatypeSnomedDsvExportItem) exportItem;
for (Integer propertyGroup : propertyCountByGroup.keySet()) {
Map<String, Integer> groupedOccurrences = propertyCountByGroup.getOrDefault(propertyGroup, NO_OCCURRENCES);
final String typeId = datatypeItem.getComponentId();
int occurrences = groupedOccurrences.getOrDefault(typeId, 0);
if (occurrences < 1) {
break;
}
final List<String> properties = concept.getMembers().stream().filter(m -> SnomedRefSetType.CONCRETE_DATA_TYPE.equals(m.type()) && m.isActive() && typeId.equals(m.getProperties().get(SnomedRf2Headers.FIELD_TYPE_ID)) && Objects.equals(m.getProperties().get(SnomedRf2Headers.FIELD_RELATIONSHIP_GROUP), propertyGroup) && (Concepts.INFERRED_RELATIONSHIP.equals(m.getProperties().get(SnomedRf2Headers.FIELD_CHARACTERISTIC_TYPE_ID)) || Concepts.ADDITIONAL_RELATIONSHIP.equals(m.getProperties().get(SnomedRf2Headers.FIELD_CHARACTERISTIC_TYPE_ID)))).map(m -> m.getProperties().get(SnomedRf2Headers.FIELD_VALUE)).map(p -> {
if (datatypeItem.isBooleanDatatype()) {
return "1".equals(p) ? "Yes" : "No";
} else {
return p.toString();
}
}).sorted().collect(Collectors.toList());
for (String value : properties) {
dataRow.add(value);
occurrences--;
}
while (occurrences > 0) {
dataRow.add("");
occurrences--;
}
}
break;
}
case PREFERRED_TERM:
if (includeDescriptionId) {
dataRow.add(getPreferredTermId(concept));
dataRow.add(getPreferredTerm(concept));
} else {
dataRow.add(getPreferredTerm(concept));
}
break;
case CONCEPT_ID:
dataRow.add(concept.getId());
break;
case MODULE:
dataRow.add(concept.getModuleId());
break;
case EFFECTIVE_TIME:
dataRow.add(EffectiveTimes.format(concept.getEffectiveTime()));
break;
case STATUS_LABEL:
dataRow.add(concept.isActive() ? "Active" : "Inactive");
break;
case DEFINITION_STATUS:
dataRow.add(concept.getDefinitionStatusId());
break;
default:
break;
}
}
writer.write(joiner.join(dataRow));
writer.write(lineSeparator);
}
}
use of com.b2international.snowowl.snomed.core.domain.SnomedConcepts in project snow-owl by b2ihealthcare.
the class SnomedSimpleTypeRefSetDSVExporter method computeHeader.
private void computeHeader(SnomedConcepts chunk) {
for (SnomedConcept concept : chunk) {
for (AbstractSnomedDsvExportItem exportItem : exportItems) {
switch(exportItem.getType()) {
case DESCRIPTION:
ComponentIdSnomedDsvExportItem descriptionItem = (ComponentIdSnomedDsvExportItem) exportItem;
String descriptionTypeId = descriptionItem.getComponentId();
Integer matchingDescriptions = concept.getDescriptions().stream().filter(d -> descriptionTypeId.equals(d.getTypeId())).collect(Collectors.reducing(0, description -> 1, Integer::sum));
descriptionCount.merge(descriptionTypeId, matchingDescriptions, Math::max);
break;
case RELATIONSHIP:
ComponentIdSnomedDsvExportItem relationshipItem = (ComponentIdSnomedDsvExportItem) exportItem;
String relationshipTypeId = relationshipItem.getComponentId();
Map<Integer, Integer> matchingRelationships = concept.getRelationships().stream().filter(r -> relationshipTypeId.equals(r.getTypeId()) && (Concepts.INFERRED_RELATIONSHIP.equals(r.getCharacteristicTypeId()) || Concepts.ADDITIONAL_RELATIONSHIP.equals(r.getCharacteristicTypeId()))).collect(Collectors.groupingBy(SnomedRelationship::getRelationshipGroup, Collectors.reducing(0, relationship -> 1, Integer::sum)));
matchingRelationships.entrySet().stream().forEach(entry -> {
propertyCountByGroup.compute(entry.getKey(), (key, oldValue) -> {
Map<String, Integer> propertyCountForGroup = ofNullable(oldValue).orElseGet(HashMap::new);
propertyCountForGroup.merge(relationshipTypeId, entry.getValue(), Math::max);
return propertyCountForGroup;
});
});
break;
case DATAYPE:
ComponentIdSnomedDsvExportItem dataTypeItem = (ComponentIdSnomedDsvExportItem) exportItem;
String dataTypeId = dataTypeItem.getComponentId();
Map<Integer, Integer> matchingMembers = concept.getMembers().stream().filter(m -> SnomedRefSetType.CONCRETE_DATA_TYPE.equals(m.type()) && m.isActive() && dataTypeId.equals(m.getProperties().get(SnomedRf2Headers.FIELD_TYPE_ID)) && (Concepts.INFERRED_RELATIONSHIP.equals(m.getProperties().get(SnomedRf2Headers.FIELD_CHARACTERISTIC_TYPE_ID)) || Concepts.ADDITIONAL_RELATIONSHIP.equals(m.getProperties().get(SnomedRf2Headers.FIELD_CHARACTERISTIC_TYPE_ID)))).collect(Collectors.groupingBy(m -> (Integer) m.getProperties().get(SnomedRf2Headers.FIELD_RELATIONSHIP_GROUP), Collectors.reducing(0, relationship -> 1, Integer::sum)));
matchingMembers.entrySet().stream().forEach(entry -> {
propertyCountByGroup.compute(entry.getKey(), (key, oldValue) -> {
Map<String, Integer> propertyCountForGroup = ofNullable(oldValue).orElseGet(HashMap::new);
propertyCountForGroup.merge(dataTypeId, entry.getValue(), Math::max);
return propertyCountForGroup;
});
});
break;
default:
// Single-use fields don't need to be counted in advance
break;
}
}
}
}
Aggregations