/* * 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; //~--- JDK imports ------------------------------------------------------------ import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; //~--- non-JDK imports -------------------------------------------------------- import sh.isaac.api.Get; import sh.isaac.api.chronicle.LatestVersion; import sh.isaac.api.collections.ConceptSequenceSet; import sh.isaac.api.collections.NidSet; import sh.isaac.api.collections.SememeSequenceSet; import sh.isaac.api.component.concept.ConceptChronology; import sh.isaac.api.component.concept.ConceptVersion; import sh.isaac.api.coordinate.LanguageCoordinate; import sh.isaac.api.coordinate.LogicCoordinate; import sh.isaac.api.coordinate.PremiseType; import sh.isaac.api.coordinate.StampCoordinate; import sh.isaac.api.coordinate.TaxonomyCoordinate; import sh.isaac.provider.query.clauses.ChangedFromPreviousVersion; import sh.isaac.provider.query.clauses.ConceptForComponent; import sh.isaac.provider.query.clauses.ConceptIs; import sh.isaac.provider.query.clauses.ConceptIsChildOf; import sh.isaac.provider.query.clauses.ConceptIsDescendentOf; import sh.isaac.provider.query.clauses.ConceptIsKindOf; import sh.isaac.provider.query.clauses.DescriptionActiveLuceneMatch; import sh.isaac.provider.query.clauses.DescriptionActiveRegexMatch; import sh.isaac.provider.query.clauses.DescriptionLuceneMatch; import sh.isaac.provider.query.clauses.DescriptionRegexMatch; import sh.isaac.provider.query.clauses.FullySpecifiedNameForConcept; import sh.isaac.provider.query.clauses.PreferredNameForConcept; import sh.isaac.provider.query.clauses.RefsetContainsConcept; import sh.isaac.provider.query.clauses.RefsetContainsKindOfConcept; import sh.isaac.provider.query.clauses.RefsetContainsString; import sh.isaac.provider.query.clauses.RefsetLuceneMatch; import sh.isaac.provider.query.clauses.RelRestriction; //~--- classes ---------------------------------------------------------------- /** * Executes queries within the terminology hierarchy and returns the nids of the * components that match the criterion of query. * * @author kec */ @XmlRootElement(name = "query") @XmlAccessorType(value = XmlAccessType.NONE) @XmlType( factoryClass = QueryFactory.class, factoryMethod = "createQuery" ) public abstract class Query { /** The Constant currentTaxonomyCoordinateKey. */ public static final String currentTaxonomyCoordinateKey = "Current taxonomy coordinate"; //~--- fields -------------------------------------------------------------- /** The for collection types. */ @XmlElementWrapper(name = "for") @XmlElement(name = "component") protected List<ComponentCollectionTypes> forCollectionTypes = new ArrayList<>(); /** The custom collection. */ @XmlElementWrapper(name = "custom-for") @XmlElement(name = "uuid") protected Set<UUID> customCollection = new HashSet<>(); /** The root clause. */ @XmlElementWrapper(name = "where") @XmlElement(name = "clause") protected Clause[] rootClause = new Clause[1]; /** The return types. */ @XmlElementWrapper(name = "return") @XmlElement(name = "type") private final EnumSet<ReturnTypes> returnTypes = EnumSet.of(ReturnTypes.NIDS); /** * Number of Components output in the returnResultSet method. */ int resultSetLimit = 50; /** * The steps required to compute the query clause. */ private final EnumSet<ClauseComputeType> computeTypes = EnumSet.noneOf(ClauseComputeType.class); /** The premise type. */ private PremiseType premiseType = PremiseType.INFERRED; /** The let declarations. */ @XmlElementWrapper(name = "let") private HashMap<String, Object> letDeclarations; /** * The concepts, stored as nids in a <code>NidSet</code>, that are * considered in the query. */ private NidSet forSet; /** * The <code>TaxonomyCoordinate</code> used in the query. */ private TaxonomyCoordinate taxonomyCoordinate; //~--- constructors -------------------------------------------------------- /** * No argument constructor, which creates a <code>Query</code> with the * Snomed inferred latest as the input <code>ViewCoordinate</code>. */ public Query() { this(null); } /** * Constructor for <code>Query</code>. If a <code>ViewCoordinate</code> is * not specified, the default is the Snomed inferred latest. * * @param taxonomyCoordinate the taxonomy coordinate */ public Query(TaxonomyCoordinate taxonomyCoordinate) { this.taxonomyCoordinate = taxonomyCoordinate; } //~--- methods ------------------------------------------------------------- /** * Let. */ public abstract void Let(); /** * Not. * * @param clause the clause * @return the not */ public Not Not(Clause clause) { return new Not(this, clause); } /** * Retrieves the root clause of the query. * * @return root <code>Clause</code> in the query */ public abstract Clause Where(); /** * Constructs the query and computes the set of concepts that match the * criterion specified in the clauses. * * @return the <code>NativeIdSetBI</code> of nids that meet the criterion of * the query */ public NidSet compute() { setup(); this.forSet = For(); getLetDeclarations(); this.rootClause[0] = Where(); final NidSet possibleComponents = this.rootClause[0].computePossibleComponents(this.forSet); if (this.computeTypes.contains(ClauseComputeType.ITERATION)) { final NidSet conceptsToIterateOver = NidSet.of(Get.identifierService() .getConceptSequencesForConceptNids(possibleComponents)); final ConceptSequenceSet conceptSequences = Get.identifierService() .getConceptSequencesForConceptNids(conceptsToIterateOver); Get.conceptService() .getParallelConceptChronologyStream(conceptSequences) .forEach((concept) -> { concept.createMutableVersion(concept.getNid()); final ConceptChronology cch = concept; final Optional<LatestVersion<ConceptVersion<?>>> latest = cch.getLatestVersion(ConceptVersion.class, this.taxonomyCoordinate.getStampCoordinate()); // Optional<LatestVersion<ConceptVersion<?>>> latest // = ((ConceptChronology<ConceptVersion<?>>) concept).getLatestVersion(ConceptVersion.class, stampCoordinate); if (latest.isPresent()) { this.rootClause[0].getChildren().stream().forEach((c) -> { c.getQueryMatches(latest.get() .value()); }); } }); } return this.rootClause[0].computeComponents(possibleComponents); } /** * Let. * * @param key the key * @param object the object */ public void let(String key, Object object) { this.letDeclarations.put(key, object); } /** * Setup. */ public void setup() { getLetDeclarations(); this.rootClause[0] = Where(); final ForSetSpecification forSetSpec = ForSetSpecification(); this.forCollectionTypes = forSetSpec.getForCollectionTypes(); this.customCollection = forSetSpec.getCustomCollection(); } /** * And. * * @param clauses the clauses * @return the and */ protected And And(Clause... clauses) { return new And(this, clauses); } /** * And not. * * @param clauses the clauses * @return the and not */ protected AndNot AndNot(Clause... clauses) { return new AndNot(this, clauses); } /** * Changed from previous version. * * @param previousCoordinateKey the previous coordinate key * @return the changed from previous version */ protected ChangedFromPreviousVersion ChangedFromPreviousVersion(String previousCoordinateKey) { return new ChangedFromPreviousVersion(this, previousCoordinateKey); } /** * Creates <code>ConceptForComponent</code> clause with input child clause. * * @param child the child * @return the concept for component */ protected ConceptForComponent ConceptForComponent(Clause child) { return new ConceptForComponent(this, child); } /** * Concept is. * * @param conceptSpecKey the concept spec key * @return the concept is */ protected ConceptIs ConceptIs(String conceptSpecKey) { return new ConceptIs(this, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Creates <code>ConceptIs</code> clause with input * <code>ViewCoordinate</code>. * * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the concept is */ protected ConceptIs ConceptIs(String conceptSpecKey, String viewCoordinateKey) { return new ConceptIs(this, conceptSpecKey, viewCoordinateKey); } /** * Concept is child of. * * @param conceptSpecKey the concept spec key * @return the concept is child of */ protected ConceptIsChildOf ConceptIsChildOf(String conceptSpecKey) { return new ConceptIsChildOf(this, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Creates <code>ConceptIsChildOf</code> clause with input * <code>ViewCoordinate</code>. * * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the concept is child of */ protected ConceptIsChildOf ConceptIsChildOf(String conceptSpecKey, String viewCoordinateKey) { return new ConceptIsChildOf(this, conceptSpecKey, viewCoordinateKey); } /** * Concept is descendent of. * * @param conceptSpecKey the concept spec key * @return the concept is descendent of */ protected ConceptIsDescendentOf ConceptIsDescendentOf(String conceptSpecKey) { return new ConceptIsDescendentOf(this, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Creates <code>ConceptIsDescendentOf</code> clause with input * <code>ViewCoordinate</code>. * * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the concept is descendent of */ protected ConceptIsDescendentOf ConceptIsDescendentOf(String conceptSpecKey, String viewCoordinateKey) { return new ConceptIsDescendentOf(this, conceptSpecKey, viewCoordinateKey); } /** * Creates <code>ConceptIsKindOf</code> clause with default * <code>ViewCoordinate</code>. * * @param conceptSpecKey the concept spec key * @return the concept is kind of */ protected ConceptIsKindOf ConceptIsKindOf(String conceptSpecKey) { return new ConceptIsKindOf(this, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Creates <code>ConceptIsKindOf</code> clause with input * <code>ViewCoordinate</code>. * * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the concept is kind of */ protected ConceptIsKindOf ConceptIsKindOf(String conceptSpecKey, String viewCoordinateKey) { return new ConceptIsKindOf(this, conceptSpecKey, viewCoordinateKey); } /** * Description active lucene match. * * @param queryTextKey the query text key * @return the description active lucene match */ protected DescriptionActiveLuceneMatch DescriptionActiveLuceneMatch(String queryTextKey) { return new DescriptionActiveLuceneMatch(this, queryTextKey, currentTaxonomyCoordinateKey); } /** * Description active lucene match. * * @param queryTextKey the query text key * @param viewCoordinateKey the view coordinate key * @return the description active lucene match */ protected DescriptionActiveLuceneMatch DescriptionActiveLuceneMatch(String queryTextKey, String viewCoordinateKey) { return new DescriptionActiveLuceneMatch(this, queryTextKey, viewCoordinateKey); } /** * Description active regex match. * * @param regexKey the regex key * @return the description active regex match */ protected DescriptionActiveRegexMatch DescriptionActiveRegexMatch(String regexKey) { return new DescriptionActiveRegexMatch(this, regexKey, currentTaxonomyCoordinateKey); } /** * Description active regex match. * * @param regexKey the regex key * @param viewCoordinateKey the view coordinate key * @return the description active regex match */ protected DescriptionActiveRegexMatch DescriptionActiveRegexMatch(String regexKey, String viewCoordinateKey) { return new DescriptionActiveRegexMatch(this, regexKey, viewCoordinateKey); } /** * Description lucene match. * * @param queryTextKey the query text key * @return the description lucene match */ protected DescriptionLuceneMatch DescriptionLuceneMatch(String queryTextKey) { return new DescriptionLuceneMatch(this, queryTextKey, currentTaxonomyCoordinateKey); } /** * Description regex match. * * @param regexKey the regex key * @return the description regex match */ protected DescriptionRegexMatch DescriptionRegexMatch(String regexKey) { return new DescriptionRegexMatch(this, regexKey, currentTaxonomyCoordinateKey); } /** * Description regex match. * * @param regexKey the regex key * @param viewCoordinateKey the view coordinate key * @return the description regex match */ protected DescriptionRegexMatch DescriptionRegexMatch(String regexKey, String viewCoordinateKey) { return new DescriptionRegexMatch(this, regexKey, viewCoordinateKey); } /** * Determines the set that will be searched in the query. * * @return the <code>NativeIdSetBI</code> of the set that will be queried */ protected final NidSet For() { this.forSet = new NidSet(); for (final ComponentCollectionTypes collection: this.forCollectionTypes) { switch (collection) { case ALL_COMPONENTS: this.forSet.or(NidSet.ofAllComponentNids()); break; case ALL_CONCEPTS: this.forSet.or(NidSet.of(ConceptSequenceSet.ofAllConceptSequences())); break; case ALL_SEMEMES: this.forSet.or(NidSet.of(SememeSequenceSet.ofAllSememeSequences())); break; case CUSTOM_SET: this.customCollection.stream().forEach((uuid) -> { this.forSet.add(Get.identifierService() .getNidForUuids(uuid)); }); break; default: throw new UnsupportedOperationException(); } } return this.forSet; } /** * For set specification. * * @return the for set specification */ protected abstract ForSetSpecification ForSetSpecification(); /** * Fully specified name for concept. * * @param clause the clause * @return the fully specified name for concept */ protected FullySpecifiedNameForConcept FullySpecifiedNameForConcept(Clause clause) { return new FullySpecifiedNameForConcept(this, clause); } /** * Intersection. * * @param clauses the clauses * @return the and */ protected And Intersection(Clause... clauses) { return new And(this, clauses); } /** * Or. * * @param clauses the clauses * @return the or */ protected Or Or(Clause... clauses) { return new Or(this, clauses); } /** * Preferred name for concept. * * @param clause the clause * @return the preferred name for concept */ protected PreferredNameForConcept PreferredNameForConcept(Clause clause) { return new PreferredNameForConcept(this, clause); } /** * Refset contains concept. * * @param refsetSpecKey the refset spec key * @param conceptSpecKey the concept spec key * @return the refset contains concept */ protected RefsetContainsConcept RefsetContainsConcept(String refsetSpecKey, String conceptSpecKey) { return new RefsetContainsConcept(this, refsetSpecKey, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Refset contains concept. * * @param refsetSpecKey the refset spec key * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the refset contains concept */ protected RefsetContainsConcept RefsetContainsConcept(String refsetSpecKey, String conceptSpecKey, String viewCoordinateKey) { return new RefsetContainsConcept(this, refsetSpecKey, conceptSpecKey, viewCoordinateKey); } /** * Refset contains kind of concept. * * @param refsetSpecKey the refset spec key * @param conceptSpecKey the concept spec key * @return the refset contains kind of concept */ protected RefsetContainsKindOfConcept RefsetContainsKindOfConcept(String refsetSpecKey, String conceptSpecKey) { return new RefsetContainsKindOfConcept(this, refsetSpecKey, conceptSpecKey, currentTaxonomyCoordinateKey); } /** * Refset contains kind of concept. * * @param refsetSpecKey the refset spec key * @param conceptSpecKey the concept spec key * @param viewCoordinateKey the view coordinate key * @return the refset contains kind of concept */ protected RefsetContainsKindOfConcept RefsetContainsKindOfConcept(String refsetSpecKey, String conceptSpecKey, String viewCoordinateKey) { return new RefsetContainsKindOfConcept(this, refsetSpecKey, conceptSpecKey, viewCoordinateKey); } /** * Refset contains string. * * @param refsetSpecKey the refset spec key * @param stringMatchKey the string match key * @return the refset contains string */ protected RefsetContainsString RefsetContainsString(String refsetSpecKey, String stringMatchKey) { return new RefsetContainsString(this, refsetSpecKey, stringMatchKey, currentTaxonomyCoordinateKey); } /** * Refset contains string. * * @param refsetSpecKey the refset spec key * @param stringMatchKey the string match key * @param viewCoordinateKey the view coordinate key * @return the refset contains string */ protected RefsetContainsString RefsetContainsString(String refsetSpecKey, String stringMatchKey, String viewCoordinateKey) { return new RefsetContainsString(this, refsetSpecKey, stringMatchKey, viewCoordinateKey); } /** * Refset lucene match. * * @param queryString the query string * @return the refset lucene match */ protected RefsetLuceneMatch RefsetLuceneMatch(String queryString) { return new RefsetLuceneMatch(this, queryString, currentTaxonomyCoordinateKey); } /** * Rel restriction. * * @param relTypeKey the rel type key * @param destinationSpecKey the destination spec key * @return the rel restriction */ protected RelRestriction RelRestriction(String relTypeKey, String destinationSpecKey) { return new RelRestriction(this, relTypeKey, destinationSpecKey, currentTaxonomyCoordinateKey, null, null); } /** * Rel restriction. * * @param relTypeKey the rel type key * @param destinationSpecKey the destination spec key * @param key the key * @return the rel restriction */ protected RelRestriction RelRestriction(String relTypeKey, String destinationSpecKey, String key) { if (this.letDeclarations.get(key) instanceof Boolean) { return new RelRestriction(this, relTypeKey, destinationSpecKey, currentTaxonomyCoordinateKey, key, null); } else { return new RelRestriction(this, relTypeKey, destinationSpecKey, key, null, null); } } /** * Rel restriction. * * @param relTypeKey the rel type key * @param destinatonSpecKey the destinaton spec key * @param relTypeSubsumptionKey the rel type subsumption key * @param targetSubsumptionKey the target subsumption key * @return the rel restriction */ protected RelRestriction RelRestriction(String relTypeKey, String destinatonSpecKey, String relTypeSubsumptionKey, String targetSubsumptionKey) { return new RelRestriction(this, relTypeKey, destinatonSpecKey, currentTaxonomyCoordinateKey, relTypeSubsumptionKey, targetSubsumptionKey); } /** * Rel restriction. * * @param relTypeKey the rel type key * @param destinationSpecKey the destination spec key * @param viewCoordinateKey the view coordinate key * @param relTypeSubsumptionKey the rel type subsumption key * @param targetSubsumptionKey the target subsumption key * @return the rel restriction */ protected RelRestriction RelRestriction(String relTypeKey, String destinationSpecKey, String viewCoordinateKey, String relTypeSubsumptionKey, String targetSubsumptionKey) { return new RelRestriction(this, relTypeKey, destinationSpecKey, viewCoordinateKey, relTypeSubsumptionKey, targetSubsumptionKey); } /** * Union. * * @param clauses the clauses * @return the or */ protected Or Union(Clause... clauses) { return new Or(this, clauses); } /** * Xor. * * @param clauses the clauses * @return the xor */ protected Xor Xor(Clause... clauses) { return new Xor(this, clauses); } //~--- get methods --------------------------------------------------------- /** * Retrieves what type of iterations are required to compute the clause. * * @return an <code>EnumSet</code> of the compute types required */ public EnumSet<ClauseComputeType> getComputePhases() { return this.computeTypes; } /** * Getter for the For set. * * @return the <code>NativeIdSetBI</code> of the concepts that will be * searched in the query */ public NidSet getForSet() { return this.forSet; } /** * Gets the language coordinate. * * @return the language coordinate */ public LanguageCoordinate getLanguageCoordinate() { return this.taxonomyCoordinate.getLanguageCoordinate(); } /** * Gets the let declarations. * * @return the let declarations */ public HashMap<String, Object> getLetDeclarations() { if (this.letDeclarations == null) { this.letDeclarations = new HashMap<>(); if (!this.letDeclarations.containsKey(currentTaxonomyCoordinateKey)) { if (this.taxonomyCoordinate != null) { this.letDeclarations.put(currentTaxonomyCoordinateKey, this.taxonomyCoordinate); } else { this.letDeclarations.put(currentTaxonomyCoordinateKey, Get.configurationService() .getDefaultTaxonomyCoordinate()); } } Let(); } return this.letDeclarations; } /** * Gets the logic coordinate. * * @return the logic coordinate */ public LogicCoordinate getLogicCoordinate() { return this.taxonomyCoordinate.getLogicCoordinate(); } /** * Gets the premise type. * * @return the premise type */ public PremiseType getPremiseType() { return this.premiseType; } //~--- set methods --------------------------------------------------------- /** * Sets the premise type. * * @param premiseType the new premise type */ public void setPremiseType(PremiseType premiseType) { this.premiseType = premiseType; } /** * Set number of Components output in the returnResultSet method. * * @param limit the new number of Components output in the returnResultSet method */ public void setResultSetLimit(int limit) { this.resultSetLimit = limit; } //~--- get methods --------------------------------------------------------- /** * Gets the stamp coordinate. * * @return the <code>StampCoordinate</code> in the query */ public StampCoordinate getStampCoordinate() { return this.taxonomyCoordinate.getStampCoordinate(); } //~--- set methods --------------------------------------------------------- /** * Set <code>TaxonomyCoordinate</code> used in the query. * * @param taxonomyCoordinate the new <code>TaxonomyCoordinate</code> used in the query */ public void setTaxonomyCoordinate(TaxonomyCoordinate taxonomyCoordinate) { this.taxonomyCoordinate = taxonomyCoordinate; } }