/* * Licensed under the Apache License, Version 2.0 (the "License"); * * You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.provider.query.search; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; //~--- non-JDK imports -------------------------------------------------------- import sh.isaac.api.Get; import sh.isaac.api.chronicle.LatestVersion; import sh.isaac.api.component.sememe.SememeChronology; import sh.isaac.api.component.sememe.version.DescriptionSememe; import sh.isaac.api.coordinate.StampCoordinate; import sh.isaac.provider.query.lucene.LuceneDescriptionType; //~--- classes ---------------------------------------------------------------- /** * {@link Search}. * * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a> */ public class Search { /** * Launch a search in a background thread (returns immediately) handing back a handle to the search. * * @param searchString - the query string * @param operationToRunWhenSearchComplete - (optional) Pass the function that you want to have executed when the search is complete and the results * are ready for use. Note that this function will also be executed in the background thread. * @param descriptionType - (optional) if provided, only searches within the specified description type * @param advancedDescriptionType - (optional) if provided, only searches within the specified advanced description type. * When this parameter is provided, the descriptionType parameter is ignored. * @param targetCodeSystemPathNidOrSequence - (optional) Restrict the results to concepts from the specified path. * @param memberOfRefsetNid - (optional) Restrict the results to concepts that are members of the specified refset. * @param kindOfNid - (optional) restrict the results to concepts that are a kind of the specified concept * @return - A handle to the running search. * @throws IOException Signals that an I/O exception has occurred. */ public static SearchHandle search(String searchString, Consumer<SearchHandle> operationToRunWhenSearchComplete, LuceneDescriptionType descriptionType, UUID advancedDescriptionType, Integer targetCodeSystemPathNidOrSequence, Integer memberOfRefsetNid, Integer kindOfNid) throws IOException { final ArrayList<Function<List<CompositeSearchResult>, List<CompositeSearchResult>>> filters = new ArrayList<>(); if (targetCodeSystemPathNidOrSequence != null) { final int pathFilterSequence = (targetCodeSystemPathNidOrSequence < 0) ? Get.identifierService() .getConceptSequence( targetCodeSystemPathNidOrSequence) : targetCodeSystemPathNidOrSequence; filters.add(t -> { final ArrayList<CompositeSearchResult> keep = new ArrayList<>(); for (final CompositeSearchResult csr: t) { if (csr.getContainingConcept().isPresent() && (csr.getContainingConcept().get().getPathSequence() == pathFilterSequence)) { keep.add(csr); } } return keep; }); } if (memberOfRefsetNid != null) { filters.add(t -> { try { final ArrayList<CompositeSearchResult> keep = new ArrayList<>(); final HashSet<Integer> refsetMembers = new HashSet<>(); Get.sememeService().getSememesFromAssemblage(Get.identifierService() .getSememeSequence(memberOfRefsetNid)).forEach(sememeC -> { refsetMembers.add(sememeC.getReferencedComponentNid()); }); for (final CompositeSearchResult csr: t) { if (csr.getContainingConcept().isPresent() && refsetMembers.contains(csr.getContainingConcept().get().getNid())) { keep.add(csr); } } return keep; } catch (final Exception e) { throw new RuntimeException(e); } }); } if (kindOfNid != null) { filters.add(t -> { final ArrayList<CompositeSearchResult> keep = new ArrayList<>(); for (final CompositeSearchResult csr: t) { if (csr.getContainingConcept().isPresent() && Get.taxonomyService().wasEverKindOf(csr.getContainingConcept().get().getNid(), kindOfNid)) { keep.add(csr); } } return keep; }); } final SearchResultsIntersectionFilter filterSet = ((filters.size() > 0) ? new SearchResultsIntersectionFilter(filters) : null); // TODO At some point, Dan needs to update this to avoid the query processor when we are automating the query generation // we also need to more consistently handle characters like [ and ( when they are going into the query parser // but that is a problem bigger than just the usage in mapping. searchString = SearchStringProcessor.prepareSearchString(searchString); if ((descriptionType == null) && (advancedDescriptionType == null)) { return SearchHandler.descriptionSearch(searchString, 500, false, (UUID) null, operationToRunWhenSearchComplete, null, filterSet, null, true, false); } else if (advancedDescriptionType != null) { return SearchHandler.descriptionSearch(searchString, 500, false, advancedDescriptionType, operationToRunWhenSearchComplete, null, filterSet, null, true, false); } else if (descriptionType != null) { return SearchHandler.descriptionSearch(searchString, 500, false, descriptionType, operationToRunWhenSearchComplete, null, filterSet, null, true, false); } else { throw new RuntimeException("Logic failure!"); } } /** * Launch a search in a background thread (returns immediately) handing back a handle to the search. * * @param sourceConceptNid - the source concept of the map - the descriptions of this concept will be used to create a search * @param operationToRunWhenSearchComplete - (optional) Pass the function that you want to have executed when the search is complete and the results * are ready for use. Note that this function will also be executed in the background thread. * @param descriptionType - (optional) if provided, only searches within the specified description type * @param advancedDescriptionType - (optional) if provided, only searches within the specified advanced description type. * When this parameter is provided, the descriptionType parameter is ignored. * @param targetCodeSystemPathNid - (optional) Restrict the results to concepts from the specified path. * @param memberOfRefsetNid - (optional) Restrict the results to concepts that are members of the specified refset. * @param kindOfNid the kind of nid * @param stampCoord - (optional) - use this stamp coordinate for fetching descriptions to build the search - otherwise, uses the default stamp coord. * @return - A handle to the running search. * @throws IOException Signals that an I/O exception has occurred. */ public static SearchHandle search(int sourceConceptNid, Consumer<SearchHandle> operationToRunWhenSearchComplete, LuceneDescriptionType descriptionType, UUID advancedDescriptionType, Integer targetCodeSystemPathNid, Integer memberOfRefsetNid, Integer kindOfNid, StampCoordinate stampCoord) throws IOException { StringBuilder searchString; searchString = new StringBuilder(); Get.sememeService() .getDescriptionsForComponent(sourceConceptNid) .forEach(descriptionC -> { @SuppressWarnings({ "rawtypes", "unchecked" }) final Optional<LatestVersion<DescriptionSememe<?>>> latest = ((SememeChronology) descriptionC).getLatestVersion(DescriptionSememe.class, (stampCoord == null) ? Get.configurationService() .getDefaultStampCoordinate() : stampCoord); if (latest.isPresent()) { searchString.append(latest.get() .value() .getText()); searchString.append(" "); } }); return search(searchString.toString(), operationToRunWhenSearchComplete, descriptionType, advancedDescriptionType, targetCodeSystemPathNid, memberOfRefsetNid, kindOfNid); } }