-
Notifications
You must be signed in to change notification settings - Fork 79
WIP: Early draft owlet query expansion #1139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
904df79
6324064
fe00192
b543ba7
b8734f4
5a42a6f
5a02b4e
d269e86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # DL-Query | ||
|
|
||
| ## Contents | ||
|
|
||
| 1. [Overview](#overview) | ||
| 2. [Query types](#query-types) | ||
|
|
||
| ## Overview | ||
|
|
||
| ROBOT can execute DL queries against an ontology. The functionality closely mimics the functionality of the <a href="https://protegewiki.stanford.edu/wiki/DLQueryTab" target="_blank">DL Query Tab</a> in Protege. | ||
|
|
||
| The `dl-query` command can be used to query for ancestors, descendants, instances and other relatives of an OWL Class Expression that is provided in Manchester syntax. | ||
|
|
||
| The output is always a list of Entity IRIs. Multiple queries and output files can be supplied. For example: | ||
|
|
||
| robot query --input uberon_module.owl \ | ||
| --query "'part_of' some 'subdivision of trunk'" part_of_subdiv_trunk.txt \ | ||
| --query "'part_of' some 'nervous system'" part_of_nervous_system.txt | ||
|
|
||
| ## Query Types | ||
|
|
||
| The following query types are currently supported: | ||
|
|
||
| - equivalents: Classes that are exactly equivalent to the supplied class expression | ||
| - parents: Direct parents (superclasses) of the class expression provided | ||
| - children: Direct children (subclasses) of the class expression provided | ||
| - descendants (default): All subclasses of the class expression provided | ||
| - ancestors: All superclasses of the class expression provided | ||
| - instances: All named individuals that are instances of the class expression provided |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| package org.obolibrary.robot; | ||
|
|
||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.util.*; | ||
| import org.apache.commons.cli.CommandLine; | ||
| import org.apache.commons.cli.Option; | ||
| import org.apache.commons.cli.Options; | ||
| import org.apache.commons.io.FileUtils; | ||
| import org.obolibrary.robot.exceptions.InconsistentOntologyException; | ||
| import org.semanticweb.owlapi.apibinding.OWLManager; | ||
| import org.semanticweb.owlapi.model.*; | ||
| import org.semanticweb.owlapi.reasoner.OWLReasoner; | ||
| import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** | ||
| * Handles inputs and outputs for the {@link DLQueryOperation}. | ||
| * | ||
| * @author <a href="mailto:[email protected]">Nicolas Matentzoglu</a> | ||
| */ | ||
| public class DLQueryCommand implements Command { | ||
| /** Logger. */ | ||
| private static final Logger logger = LoggerFactory.getLogger(DLQueryCommand.class); | ||
|
|
||
| private static final String NS = "dl-query#"; | ||
|
|
||
| private static final List<String> LEGAL_RELATIONS = | ||
| Arrays.asList("equivalents", "ancestors", "descendants", "instances", "parents", "children"); | ||
| OWLDataFactory df = OWLManager.getOWLDataFactory(); | ||
|
|
||
| private static final String maxTypeError = NS + "MAX TYPE ERROR --max ('%s') must be an integer"; | ||
| private static final String illegalRelationError = | ||
| NS + "ILLEGAL RELATION ERROR: %s. Must be one of " + String.join(" ", LEGAL_RELATIONS) + "."; | ||
| private static final String missingQueryArgumentError = | ||
| NS + "MISSING QUERY ARGUMENT ERROR: must have a valid --query."; | ||
|
|
||
| /** Error message when --query does not have two arguments. */ | ||
| private static final String missingOutputError = | ||
| NS + "MISSING OUTPUT ERROR --%s requires two arguments: query and output"; | ||
|
|
||
| /** Error message when a query is not provided */ | ||
| private static final String missingQueryError = | ||
| NS + "MISSING QUERY ERROR at least one query must be provided"; | ||
|
|
||
| /** Store the command-line options for the command. */ | ||
| private Options options; | ||
|
|
||
| public DLQueryCommand() { | ||
| Options o = CommandLineHelper.getCommonOptions(); | ||
| o.addOption("i", "input", true, "load ontology from a file"); | ||
| o.addOption("I", "input-iri", true, "load ontology from an IRI"); | ||
| o.addOption("r", "reasoner", true, "reasoner to use: ELK, HermiT, JFact"); | ||
|
|
||
| Option opt = new Option("q", "query", true, "the DL query to run"); | ||
| opt.setArgs(2); | ||
| o.addOption(opt); | ||
|
|
||
| o.addOption( | ||
| "s", | ||
| "select", | ||
| true, | ||
| "select what relations to query: equivalents, parents, children, ancestors, descendants, instances"); | ||
| o.addOption("o", "output", true, "save ontology containing only explanation axioms to a file"); | ||
| options = o; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return "dl-query"; | ||
| } | ||
|
|
||
| @Override | ||
| public String getDescription() { | ||
| return "query the ontology with the given class expression"; | ||
| } | ||
|
|
||
| @Override | ||
| public String getUsage() { | ||
| return "robot dl-query --input <file> --query <expression> --output <output>"; | ||
| } | ||
|
|
||
| @Override | ||
| public Options getOptions() { | ||
| return options; | ||
| } | ||
|
|
||
| /** | ||
| * Handle the command-line and file operations for the DLQueryOperation. | ||
| * | ||
| * @param args strings to use as arguments | ||
| */ | ||
| @Override | ||
| public void main(String[] args) { | ||
| try { | ||
| execute(null, args); | ||
| } catch (Exception e) { | ||
| CommandLineHelper.handleException(e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public CommandState execute(CommandState state, String[] args) throws Exception { | ||
| CommandLine line = CommandLineHelper.getCommandLine(getUsage(), getOptions(), args); | ||
| if (line == null) { | ||
| return null; | ||
| } | ||
| if (state == null) { | ||
| state = new CommandState(); | ||
| } | ||
| IOHelper ioHelper = CommandLineHelper.getIOHelper(line); | ||
| state = CommandLineHelper.updateInputOntology(ioHelper, state, line); | ||
| OWLOntology ontology = state.getOntology(); | ||
|
|
||
| OWLReasonerFactory reasonerFactory = CommandLineHelper.getReasonerFactory(line, true); | ||
| List<String> selects = CommandLineHelper.getOptionalValues(line, "select"); | ||
|
|
||
| List<List<String>> queries = getQueries(line); | ||
| for (List<String> q : queries) { | ||
| queryOntology(q, ontology, reasonerFactory, selects); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As implemented, it is very inefficient to run several queries against the same ontology, because there is no shared state between each query (in particular, no shared reasoner, even though the ontology cannot possibly change between each query, so initialising a brand new reasoner instance for each query is pointless). From some tests I did with Uberon:
By contrast, with a modified version that uses a shared state:
|
||
| } | ||
|
|
||
| state.setOntology(ontology); | ||
| CommandLineHelper.maybeSaveOutput(line, ontology); | ||
| return state; | ||
| } | ||
|
|
||
| private void queryOntology( | ||
| List<String> q, | ||
| OWLOntology ontology, | ||
| OWLReasonerFactory reasonerFactory, | ||
| List<String> selects) | ||
| throws InconsistentOntologyException, IOException { | ||
| OWLReasoner r = reasonerFactory.createReasoner(ontology); | ||
| String query = q.get(0); | ||
| File output = new File(q.get(1)); | ||
| OWLClassExpression classExpression = DLQueryOperation.parseOWLClassExpression(query, ontology); | ||
| if (r.isConsistent()) { | ||
| List<OWLEntity> entities = DLQueryOperation.query(classExpression, r, selects); | ||
| writeQueryResultsToFile(output, entities); | ||
| } else { | ||
| throw new InconsistentOntologyException(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Given a command line, get a list of queries. | ||
| * | ||
| * @param line CommandLine with options | ||
| * @return List of queries | ||
| */ | ||
| private static List<List<String>> getQueries(CommandLine line) { | ||
| // Collect all queries as (queryPath, outputPath) pairs. | ||
| List<List<String>> queries = new ArrayList<>(); | ||
| List<String> qs = CommandLineHelper.getOptionalValues(line, "query"); | ||
| for (int i = 0; i < qs.size(); i += 2) { | ||
| try { | ||
| queries.add(qs.subList(i, i + 2)); | ||
| } catch (IndexOutOfBoundsException e) { | ||
| throw new IllegalArgumentException(String.format(missingOutputError, "query")); | ||
| } | ||
| } | ||
| if (queries.isEmpty()) { | ||
| throw new IllegalArgumentException(missingQueryError); | ||
| } | ||
| return queries; | ||
| } | ||
|
|
||
| private void writeQueryResultsToFile(File output, List<OWLEntity> results) throws IOException { | ||
| Collections.sort(results); | ||
| FileUtils.writeLines(output, results); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command is defined but never added to the
CommandManager.