use of com.b2international.snomed.ecl.ecl.ExpressionConstraint in project snow-owl by b2ihealthcare.
the class SnomedEclLabelerRequest method execute.
@Override
public LabeledEclExpressions execute(BranchContext context) {
final EclSerializer eclSerializer = context.service(EclSerializer.class);
final EclParser eclParser = context.service(EclParser.class);
final Set<String> conceptIdsToLabel = Sets.newHashSetWithExpectedSize(expressions.size());
final Map<String, ExpressionConstraint> queries = Maps.newHashMapWithExpectedSize(expressions.size());
final LinkedHashMap<String, Object> errors = Maps.newLinkedHashMap();
for (String expression : expressions) {
if (Strings.isNullOrEmpty(expression)) {
continue;
}
try {
ExpressionConstraint query = queries.computeIfAbsent(expression, (key) -> eclParser.parse(key));
conceptIdsToLabel.addAll(collect(query));
} catch (ApiException e) {
if (e instanceof SyntaxException) {
errors.put(expression, List.copyOf(((SyntaxException) e).getAdditionalInfo().values()));
} else if (e instanceof BadRequestException) {
errors.put(expression, e.getMessage());
} else {
throw e;
}
}
}
if (!errors.isEmpty()) {
BadRequestException badRequestException = new BadRequestException("One or more ECL syntax errors");
badRequestException.withAdditionalInfo("erroneousExpressions", errors);
throw badRequestException;
}
// fetch all concept labels
final Map<String, String> labels = SnomedRequests.prepareSearchConcept().filterByIds(conceptIdsToLabel).setLimit(conceptIdsToLabel.size()).setExpand(descriptionType.toLowerCase() + "()").setLocales(locales()).build().execute(context).stream().collect(Collectors.toMap(SnomedConcept::getId, this::extractLabel));
// expand all queries with labels
List<String> results = expressions.stream().map(expression -> {
if (Strings.isNullOrEmpty(expression)) {
return expression;
} else {
ExpressionConstraint query = queries.get(expression);
expand(query, labels);
return eclSerializer.serialize(query);
}
}).collect(Collectors.toList());
return new LabeledEclExpressions(results);
}
use of com.b2international.snomed.ecl.ecl.ExpressionConstraint 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());
}
Aggregations