Skip to content

Commit

Permalink
feat(core): support other resource URIs when suggesting concepts
Browse files Browse the repository at this point in the history
  • Loading branch information
cmark committed Aug 16, 2022
1 parent 584a5f4 commit 64c5559
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.List;
import java.util.Map;

import com.b2international.commons.exceptions.BadRequestException;
import com.b2international.commons.http.ExtendedLocale;
import com.b2international.commons.options.Options;
import com.b2international.snowowl.core.domain.RepositoryContext;
Expand All @@ -37,40 +38,55 @@
public interface ResourceTypeConverter {

final class Registry {

private final Map<String, ResourceTypeConverter> resourceTypeConverters = new HashMap<>();

public Registry(ClassPathScanner scanner) {
scanner.getComponentsByInterface(ResourceTypeConverter.class).forEach(converter -> {
resourceTypeConverters.put(converter.getResourceType(), converter);
});
}

public Resource toResource(ResourceDocument doc) {
checkArgument(resourceTypeConverters.containsKey(doc.getResourceType()), "ResourceTypeConverter implementation is missing for type: %s", doc.getResourceType());
checkArgument(resourceTypeConverters.containsKey(doc.getResourceType()), "ResourceTypeConverter implementation is missing for type: %s",
doc.getResourceType());
return resourceTypeConverters.get(doc.getResourceType()).toResource(doc);
}

public Map<String, ResourceTypeConverter> getResourceTypeConverters() {
return ImmutableMap.copyOf(resourceTypeConverters);
}

public void expand(RepositoryContext context, Options expand, List<ExtendedLocale> locales, List<Resource> results) {
Multimap<String, Resource> resourcesByIndex = Multimaps.index(results, Resource::getResourceType);
for (String resourceTypeToExpand : resourcesByIndex.keySet()) {
checkArgument(resourceTypeConverters.containsKey(resourceTypeToExpand), "ResourceTypeConverter implementation is missing for type: %s", resourceTypeToExpand);
checkArgument(resourceTypeConverters.containsKey(resourceTypeToExpand),
"ResourceTypeConverter implementation is missing for type: %s", resourceTypeToExpand);
resourceTypeConverters.get(resourceTypeToExpand).expand(context, expand, locales, resourcesByIndex.get(resourceTypeToExpand));
}
}
}

String getResourceType();

Integer getRank();

Resource toResource(ResourceDocument doc);

default void expand(RepositoryContext context, Options expand, List<ExtendedLocale> locales, Collection<Resource> results) {
}


/**
* Resolves an URI to a CodeSystemURI with ECL part if supported by this resource type converter using the given context.
*
* @param context
* @param uriToResolve
*
* @return
* @throws BadRequestException - if the uri cannot be resolved to a valid CodeSystem URI with ECL part
*/
default ResourceURIWithQuery resolveToCodeSystemUriWithQuery(ServiceProvider context, String uriToResolve) {
throw new BadRequestException("'%s' represents a resource that cannot be resolved into a CodeSystem URI with ECL query part.", uriToResolve);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,8 @@ public static ResourceURI of(String resourceType, String resourceId) {
return new ResourceURI(String.join(Branch.SEPARATOR, resourceType, resourceId));
}

public ResourceURIWithQuery withQuery(String query) {
return new ResourceURIWithQuery(this, query);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public ResourceURIWithQuery(String uri) {
this.query = firstQueryCharAt == uri.length() ? "" : uri.substring(firstQueryCharAt + 1, uri.length());
}

ResourceURIWithQuery(ResourceURI resourceUri, String query) {
this.resourceUri = resourceUri;
this.query = query;
this.uri = String.join(QUERY_PART_SEPARATOR, resourceUri.toString(), query);
}

public String getUri() {
return uri;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.b2international.commons.options.Options;
import com.b2international.snowowl.core.Resource;
import com.b2international.snowowl.core.ResourceTypeConverter;
import com.b2international.snowowl.core.ResourceURIWithQuery;
import com.b2international.snowowl.core.ServiceProvider;
import com.b2international.snowowl.core.domain.Concepts;
import com.b2international.snowowl.core.domain.RepositoryContext;
import com.b2international.snowowl.core.internal.ResourceDocument;
Expand Down Expand Up @@ -64,5 +66,10 @@ public void expand(RepositoryContext context, Options expand, List<ExtendedLocal
});
}
}

@Override
public ResourceURIWithQuery resolveToCodeSystemUriWithQuery(ServiceProvider context, String uriToResolve) {
return CodeSystem.uriWithQuery(uriToResolve);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.b2international.commons.exceptions.BadRequestException;
import com.b2international.commons.http.ExtendedLocale;
import com.b2international.snomed.ecl.Ecl;
import com.b2international.snowowl.core.ResourceTypeConverter;
import com.b2international.snowowl.core.ResourceURI;
import com.b2international.snowowl.core.ResourceURIWithQuery;
import com.b2international.snowowl.core.ServiceProvider;
Expand All @@ -49,15 +50,14 @@ public final class ConceptSuggestionContext extends DelegatingContext {
// dynamically computed exclusion items during like item computation
private Multimap<ResourceURI, String> exclusionQueriesPerResourceUri = HashMultimap.create();

public ConceptSuggestionContext(ServiceProvider delegate, String from, List<String> likes, List<String> unlikes, List<ExtendedLocale> locales) {
super(delegate);
public ConceptSuggestionContext(ServiceProvider context, String from, List<String> likes, List<String> unlikes, List<ExtendedLocale> locales) {
super(context);
this.locales = locales;
// FIXME accepts only codesystem URIs for now, should we allow suggestions to come from VS/CM/Bundles/etc.?
this.from = CodeSystem.uriWithQuery(from);
this.from = resolveUri(context, from);
this.likes = Collections3.toImmutableSortedSet(likes);
this.unlikes = Collections3.toImmutableSortedSet(unlikes);
}

public SortedSet<String> likes() {
return likes;
}
Expand All @@ -71,7 +71,7 @@ public Stream<String> streamLikes() {

unlikes.forEach(unlike -> {
try {
final ResourceURIWithQuery uri = CodeSystem.uriWithQuery(unlike);
final ResourceURIWithQuery uri = resolveUri(this, unlike);
Collection<String> eclQueries = uri.getQueryValues().get("ecl");
if (eclQueries.isEmpty()) {
throw new BadRequestException("Selecting an entire Code System as unlike is not supported yet. Specify an ECL query part like this: %s?ecl=<your_query>", uri.getResourceUri().withoutResourceType());
Expand All @@ -92,7 +92,7 @@ public Stream<String> streamLikes() {
.stream()
.flatMap(like -> {
try {
final ResourceURIWithQuery uri = CodeSystem.uriWithQuery(like);
final ResourceURIWithQuery uri = resolveUri(this, like);
// raw URIs are not supported yet, because those can select too many concepts
Collection<String> eclQueries = uri.getQueryValues().get("ecl");
if (eclQueries.isEmpty()) {
Expand Down Expand Up @@ -147,5 +147,23 @@ private List<String> getAllTerms(Concept concept) {
public Collection<String> exclusionQuery(ResourceURI resourceUri) {
return exclusionQueriesPerResourceUri.get(resourceUri);
}

public String getInclusionQueries() {
final Collection<String> inclusionQueries = from().getQueryValues().get("ecl");
return inclusionQueries.isEmpty() ? null : Ecl.or(inclusionQueries);
}

private ResourceURIWithQuery resolveUri(ServiceProvider context, String uriToResolve) {
// find the appropriate resource for this URI by looking at the plugged in resources types
ResourceURIWithQuery uri = null;
for (ResourceTypeConverter resourceTypeConverter : context.service(ResourceTypeConverter.Registry.class).getResourceTypeConverters().values()) {
if (uriToResolve.startsWith(resourceTypeConverter.getResourceType())) {
uri = resourceTypeConverter.resolveToCodeSystemUriWithQuery(context, uriToResolve);
break;
}
}
// if the URI is still null, treat it as CodeSystem for now
return uri == null ? CodeSystem.uriWithQuery(uriToResolve) : uri;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.stream.Collectors;

import com.b2international.commons.http.ExtendedLocale;
import com.b2international.snomed.ecl.Ecl;
import com.b2international.snowowl.core.codesystem.CodeSystemRequests;
import com.b2international.snowowl.core.events.util.Promise;
import com.b2international.snowowl.core.identity.User;
Expand Down Expand Up @@ -79,7 +78,7 @@ public Promise<Suggestions> suggest(ConceptSuggestionContext context, int limit,
.build();

// get the ECL query of the from code system
final Collection<String> inclusionQueries = context.from().getQueryValues().get("ecl");
final String inclusionQuery = context.getInclusionQueries();
// get the dynamically computed exclusion query set
final Collection<String> exclusionQueries = context.exclusionQuery(context.from().getResourceUri());

Expand All @@ -89,7 +88,7 @@ public Promise<Suggestions> suggest(ConceptSuggestionContext context, int limit,
.filterByActive(true)
// configure from, resource and optional ECL query
.filterByCodeSystemUri(context.from().getResourceUri())
.filterByQuery(inclusionQueries.isEmpty() ? null : Ecl.or(inclusionQueries))
.filterByQuery(inclusionQuery)
// make sure we won't suggest the same concepts as defined in like and unlike arrays (for the same code system)
.filterByExclusions(exclusionQueries.isEmpty() ? null : exclusionQueries)
// configure lexical match as basis of suggestion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

import com.b2international.commons.http.ExtendedLocale;
import com.b2international.index.compat.TextConstants;
import com.b2international.snomed.ecl.Ecl;
import com.b2international.snowowl.core.codesystem.CodeSystemRequests;
import com.b2international.snowowl.core.events.util.Promise;
import com.b2international.snowowl.core.identity.User;
Expand Down Expand Up @@ -122,7 +121,7 @@ public Promise<Suggestions> suggest(ConceptSuggestionContext context, int limit,
.build();

// get the ECL query of the from code system
final Collection<String> inclusionQueries = context.from().getQueryValues().get("ecl");
final String inclusionQuery = context.getInclusionQueries();
// get the dynamically computed exclusion query set
final Collection<String> exclusionQueries = context.exclusionQuery(context.from().getResourceUri());

Expand All @@ -132,7 +131,7 @@ public Promise<Suggestions> suggest(ConceptSuggestionContext context, int limit,
.filterByActive(true)
// configure from, resource and optional ECL query
.filterByCodeSystemUri(context.from().getResourceUri())
.filterByQuery(inclusionQueries.isEmpty() ? null : Ecl.or(inclusionQueries))
.filterByQuery(inclusionQuery)
// make sure we won't suggest the same concepts as defined in like and unlike arrays (for the same code system)
.filterByExclusions(exclusionQueries.isEmpty() ? null : exclusionQueries)
// configure lexical match as basis of suggestion
Expand Down

0 comments on commit 64c5559

Please sign in to comment.