All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.ikm.tinkar.provider.search.Searcher Maven / Gradle / Ivy

There is a newer version: 1.72.0
Show newest version
/*
 * Copyright © 2015 Integrated Knowledge Management ([email protected])
 *
 * 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.
 */
package dev.ikm.tinkar.provider.search;

import dev.ikm.tinkar.common.id.IntIdSet;
import dev.ikm.tinkar.common.id.PublicId;
import dev.ikm.tinkar.common.service.PrimitiveData;
import dev.ikm.tinkar.common.service.PrimitiveDataSearchResult;
import dev.ikm.tinkar.common.util.time.Stopwatch;
import dev.ikm.tinkar.coordinate.Coordinates;
import dev.ikm.tinkar.coordinate.language.LanguageCoordinateRecord;
import dev.ikm.tinkar.coordinate.navigation.NavigationCoordinateRecord;
import dev.ikm.tinkar.coordinate.navigation.calculator.NavigationCalculator;
import dev.ikm.tinkar.coordinate.navigation.calculator.NavigationCalculatorWithCache;
import dev.ikm.tinkar.coordinate.stamp.StampCoordinateRecord;
import dev.ikm.tinkar.entity.EntityService;
import dev.ikm.tinkar.entity.PatternEntity;
import dev.ikm.tinkar.entity.SemanticEntityVersion;
import dev.ikm.tinkar.terms.EntityProxy;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.search.highlight.*;
import org.eclipse.collections.impl.factory.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

public class Searcher {
    private static final Logger LOG = LoggerFactory.getLogger(Searcher.class);
    public static final EntityProxy.Pattern LIDR_RECORD_PATTERN = EntityProxy.Pattern.make(null, UUID.fromString("c3d52f47-0565-5cfb-9b0b-d7501a33b35d"));
    public static final EntityProxy.Pattern DIAGNOSTIC_DEVICE_PATTERN = EntityProxy.Pattern.make(null, UUID.fromString("a507b3c7-eadb-5d54-84c0-c44f3155d0bc"));
    public static final EntityProxy.Pattern QUANTITATIVE_ALLOWED_RESULT_SET_PATTERN = EntityProxy.Pattern.make(null, UUID.fromString("9d40d06b-7776-5a56-97e4-0c27f5d574c7"));
    public static final EntityProxy.Pattern QUALITATIVE_ALLOWED_RESULT_SET_PATTERN = EntityProxy.Pattern.make(null, UUID.fromString("160a63a6-3cba-510e-83d1-235822045885"));
    QueryParser parser;
    private static final SearcherManager searcherManager;

    //TODO - refactor this class to not have static fields. Currently needed when using this SearcherManager class.
    static {
        try {
            searcherManager = new SearcherManager(Indexer.indexReader(), null);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public Searcher() throws IOException {
        Stopwatch stopwatch = new Stopwatch();
        LOG.info("Opening lucene searcher");
        this.parser = new QueryParser("text", Indexer.analyzer());
        stopwatch.stop();
        LOG.info("Opened lucene searcher in: " + stopwatch.durationString());
    }

    public PrimitiveDataSearchResult[] search(String queryString, int maxResultSize) throws
            ParseException, IOException, InvalidTokenOffsetsException {
        boolean refreshOutcome = searcherManager.maybeRefresh();
        LOG.info("Index Reader refresh outcome = " + refreshOutcome);
        IndexSearcher indexSearcher = searcherManager.acquire();
        try {
            if (queryString != null & !queryString.isEmpty()) {
                PrimitiveDataSearchResult[] results;
                    Query query = parser.parse(queryString);
                    Formatter formatter = new SimpleHTMLFormatter();
                    QueryScorer scorer = new QueryScorer(query);
                    Highlighter highlighter = new Highlighter(formatter, scorer);
                    highlighter.setTextFragmenter(new NullFragmenter());

                    ScoreDoc[] hits = indexSearcher.search(query, maxResultSize).scoreDocs;
                    results = new PrimitiveDataSearchResult[hits.length];
                    for (int i = 0; i < hits.length; i++) {
                        Document hitDoc = indexSearcher.doc(hits[i].doc);
                        StoredField nidField = (StoredField) hitDoc.getField(Indexer.NID);
                        StoredField patternNidField = (StoredField) hitDoc.getField(Indexer.PATTERN_NID);
                        StoredField rcNidField = (StoredField) hitDoc.getField(Indexer.RC_NID);
                        StoredField fieldIndexField = (StoredField) hitDoc.getField(Indexer.FIELD_INDEX);
                        StoredField textField = (StoredField) hitDoc.getField(Indexer.TEXT_FIELD_NAME);
                        String highlightedString = highlighter.getBestFragment(Indexer.analyzer(), Indexer.TEXT_FIELD_NAME, textField.stringValue());

                        results[i] = new PrimitiveDataSearchResult(nidField.numericValue().intValue(), rcNidField.numericValue().intValue(),
                                patternNidField.numericValue().intValue(), fieldIndexField.numericValue().intValue(), hits[i].score, highlightedString);
                    }
                return results;
            }
        } finally {
            searcherManager.release(indexSearcher);
        }
        return new PrimitiveDataSearchResult[0];
    }

    /**
     * Returns a default navigation calculator with coordinates for
     * inferred navigation, active stamps on development path, & english synonyms
     *
     * @return  NavigationCalculator
     */
    private static NavigationCalculator defaultNavigationCalculator() {
        StampCoordinateRecord stampCoordinateRecord = Coordinates.Stamp.DevelopmentLatestActiveOnly();
        LanguageCoordinateRecord languageCoordinateRecord = Coordinates.Language.UsEnglishRegularName();
        NavigationCoordinateRecord navigationCoordinateRecord = Coordinates.Navigation.inferred().toNavigationCoordinateRecord();
        return NavigationCalculatorWithCache.getCalculator(stampCoordinateRecord, Lists.immutable.of(languageCoordinateRecord), navigationCoordinateRecord);
    }

    /**
     * Returns List of Children PublicIds for the Entity PublicId provided
     * using the default NavigationCalculator from {@link #defaultNavigationCalculator()}
     *
     * @param   parentConceptId PublicId of the parent concept
     * @return  List of PublicIds for the children concepts
     */
    public static List childrenOf(PublicId parentConceptId) {
        return childrenOf(defaultNavigationCalculator(), parentConceptId);
    }

    /**
     * Returns List of Children PublicIds for the Entity PublicId provided
     * using a supplied NavigationCalculator
     * 

* Provided as an ease-of-use method to convert nids provided by the * {@link NavigationCalculator#childrenOf(int)} method to PublicIds * * @param navCalc NavigationCalculator to calculate children * @param parentConceptId PublicId of the parent concept * @return List of PublicIds for the children concepts */ public static List childrenOf(NavigationCalculator navCalc, PublicId parentConceptId) { List childIds = new ArrayList<>(); int[] childNidList = navCalc.childrenOf(EntityService.get().nidForPublicId(parentConceptId)).toArray(); for (int childNid : childNidList) { EntityService.get().getEntity(childNid).ifPresent((entity) -> childIds.add(entity.publicId())); } return childIds; } /** * Returns List of descendant PublicIds for the Entity PublicId provided * using the default NavigationCalculator from {@link #defaultNavigationCalculator()} * * @param ancestorConceptId PublicId of the ancestor concept * @return List of PublicIds for the descendant concepts */ public static List descendantsOf(PublicId ancestorConceptId) { return descendantsOf(defaultNavigationCalculator(), ancestorConceptId); } /** * Returns List of descendant PublicIds for the Entity PublicId provided * using a supplied NavigationCalculator *

* Provided as an ease-of-use method to convert nids provided by the * {@link NavigationCalculator#descendentsOf(int)} method to PublicIds * * @param navCalc NavigationCalculator to calculate descendants * @param ancestorConceptId PublicId of the ancestor concept * @return List of PublicIds for the descendant concepts */ public static List descendantsOf(NavigationCalculator navCalc, PublicId ancestorConceptId) { List descendantIds = new ArrayList<>(); int[] descendantNidList = navCalc.descendentsOf(EntityService.get().nidForPublicId(ancestorConceptId)).toArray(); for (int descendantNid : descendantNidList) { EntityService.get().getEntity(descendantNid).ifPresent((entity) -> descendantIds.add(entity.publicId())); } return descendantIds; } /** * Returns List of Fully Qualified Name (FQN) Strings for the Entity PublicIds provided * using the default NavigationCalculator from {@link #defaultNavigationCalculator()} * * @param conceptIds List of PublicIds for concepts with FQNs to return * @return List of FQN Strings with indexes matching the supplied List of PublicIds */ public static List descriptionsOf(List conceptIds) { return descriptionsOf(defaultNavigationCalculator(), conceptIds); } /** * Returns List of Fully Qualified Name (FQN) Strings for the Entity PublicIds provided * using a supplied NavigationCalculator *

* Provided as an ease-of-use method to retrieve FQNs for multiple concepts at once * using the {@link NavigationCalculator#getFullyQualifiedNameText(int)} method * * @param navCalc NavigationCalculator to calculate FQNs * @param conceptIds List of PublicIds for concepts with FQNs to return * @return List of FQN Strings with indexes matching the supplied List of PublicIds */ public static List descriptionsOf(NavigationCalculator navCalc, List conceptIds) { List names = new ArrayList<>(); for (PublicId pid : conceptIds) { navCalc.getFullyQualifiedNameText(EntityService.get().nidForPublicId(pid)) .ifPresentOrElse(names::add, () -> { LOG.warn("FQN not defined for " + pid.idString()); names.add(""); } ); } return names; } /** * Returns List of LIDR Record PublicIds for the provided Test Kit Device. * * @param testKitId associated Test Kit Device PublicId * @return List of Public Ids given test kit concept and lidr record pattern. */ public static List getLidrRecordSemanticsFromTestKit(PublicId testKitId){ List lidrRecordSemanticIds = new ArrayList<>(); EntityService.get().getEntity(testKitId.asUuidArray()).ifPresent((testKitEntity) -> { EntityService.get().forEachSemanticForComponentOfPattern(testKitEntity.nid(), DIAGNOSTIC_DEVICE_PATTERN.nid(), diagnosticDeviceSemantic -> { EntityService.get().forEachSemanticForComponentOfPattern(diagnosticDeviceSemantic.nid(), LIDR_RECORD_PATTERN.nid(), lidrRecordSemantic -> { lidrRecordSemanticIds.add(lidrRecordSemantic.publicId()); }); }); }); return lidrRecordSemanticIds; } /** * Returns List of Result Conformance PublicIds for all LIDR Records of the Test Kit Device * using the default NavigationCalculator from {@link #defaultNavigationCalculator()} * * @param testKitId PublicId of the Test Kit Device * @return List of PublicIds for all Result Conformances / Constraints for the Test Kit Device */ public static List getResultConformanceFromTestKit(PublicId testKitId) { return getResultConformanceFromTestKit(defaultNavigationCalculator(), testKitId); } /** * Returns List of Result Conformance PublicIds for all LIDR Records of the Test Kit Device * * @param navCalc NavigationCalculator for determining latest version * @param testKitId PublicId of the Test Kit Device * @return List of PublicIds for all Result Conformances / Constraints for the Test Kit Device */ public static List getResultConformanceFromTestKit(NavigationCalculator navCalc, PublicId testKitId) { List resultConformanceList = new ArrayList<>(); for (PublicId lidrRecordId : getLidrRecordSemanticsFromTestKit(testKitId)) { resultConformanceList.addAll(getResultConformancesFromLidrRecord(navCalc, lidrRecordId)); } return resultConformanceList; } /** * Returns List of Result Conformance PublicIds for a LIDR Record * using the default NavigationCalculator from {@link #defaultNavigationCalculator()} * * @param lidrRecordId PublicId of the LIDR Record * @return List of PublicIds for Result Conformances of the LIDR Record */ public static List getResultConformancesFromLidrRecord(PublicId lidrRecordId) { return getResultConformancesFromLidrRecord(defaultNavigationCalculator(), lidrRecordId); } /** * Returns List of Result Conformance PublicIds for a LIDR Record * * @param navCalc NavigationCalculator for determining latest version * @param lidrRecordId PublicId of the LIDR Record * @return List of PublicIds for Result Conformances of the LIDR Record */ public static List getResultConformancesFromLidrRecord(NavigationCalculator navCalc, PublicId lidrRecordId) { List resultConformanceList = new ArrayList<>(); EntityService.get().getEntity(lidrRecordId.asUuidArray()).ifPresent((lidrRecordEntity) -> { navCalc.stampCalculator().latest(lidrRecordEntity) .ifPresent((lidrRecordVersion) -> { SemanticEntityVersion lidrRecordSemanticVersion = (SemanticEntityVersion) lidrRecordVersion; int idxResultConformances = 5; ((IntIdSet) lidrRecordSemanticVersion.fieldValues().get(idxResultConformances)) .map(PrimitiveData::publicId) .forEach(resultConformanceList::add); }); }); return resultConformanceList; } /** * Returns List of Allowed Results PublicIds for a Result Conformance * using the default NavigationCalculator from {@link #defaultNavigationCalculator()} * * @param resultConformanceId PublicId of the Result Conformance * @return List of PublicIds for Allowed Results of the Result Conformance */ public static List getAllowedResultsFromResultConformance(PublicId resultConformanceId) { return getAllowedResultsFromResultConformance(defaultNavigationCalculator(), resultConformanceId); } /** * Returns List of Allowed Results PublicIds for a Result Conformance * * @param navCalc NavigationCalculator for determining latest version * @param resultConformanceId PublicId of the Result Conformance * @return List of PublicIds for Allowed Results of the Result Conformance */ public static List getAllowedResultsFromResultConformance(NavigationCalculator navCalc, PublicId resultConformanceId) { List allowedResultsList = new ArrayList<>(); int resultConformanceNid = EntityService.get().nidForPublicId(resultConformanceId); EntityService.get().forEachSemanticForComponentOfPattern(resultConformanceNid, QUANTITATIVE_ALLOWED_RESULT_SET_PATTERN.nid(), (quantitativeResultSet) -> { navCalc.stampCalculator().latest(quantitativeResultSet) .ifPresent((latestQuantitativeResultSet) -> { ((IntIdSet) latestQuantitativeResultSet.fieldValues().get(0)) .map(PrimitiveData::publicId) .forEach(allowedResultsList::add); }); }); EntityService.get().forEachSemanticForComponentOfPattern(resultConformanceNid, QUALITATIVE_ALLOWED_RESULT_SET_PATTERN.nid(), (qualitativeResultSet) -> { navCalc.stampCalculator().latest(qualitativeResultSet) .ifPresent((latestQualitativeResultSet) -> { ((IntIdSet) latestQualitativeResultSet.fieldValues().get(0)) .map(PrimitiveData::publicId) .forEach(allowedResultsList::add); }); }); return allowedResultsList; } /** * Returns List of PublicIds for the Concepts tagged with the Membership Pattern * * @param memberPatternId PublicId of the Membership Pattern * @return List of PublicIds for the Concepts tagged with the Membership Pattern */ public static List membersOf(PublicId memberPatternId) { if (PrimitiveData.get().hasPublicId(memberPatternId)) { List conceptIds = new ArrayList<>(); EntityService.get().getEntity(memberPatternId.asUuidArray()).ifPresent((e) -> { if (e instanceof PatternEntity patternEntity) { EntityService.get().forEachSemanticOfPattern(patternEntity.nid(), (semanticEntityOfPattern) -> conceptIds.add(semanticEntityOfPattern.referencedComponent().publicId())); } }); return conceptIds; } return Collections.emptyList(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy