Skip to content
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

Inference Rules and Data Integrity Checks #195

Merged
merged 14 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ tdb/
out
*.code-workspace
*.iml
*.ttl
.DS_Store
.factorypath
.devcontainer
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
requires transitive uk.gov.gchq.magmacore.hqdm;

exports uk.gov.gchq.magmacore.database.query;
exports uk.gov.gchq.magmacore.database.validation;
exports uk.gov.gchq.magmacore.exception;
exports uk.gov.gchq.magmacore.service.dto;
exports uk.gov.gchq.magmacore.service.transformation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import org.apache.jena.riot.Lang;

import uk.gov.gchq.magmacore.database.query.QueryResultList;
import uk.gov.gchq.magmacore.database.validation.ValidationReportEntry;
import uk.gov.gchq.magmacore.hqdm.model.Thing;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;
import uk.gov.gchq.magmacore.service.transformation.DbCreateOperation;
import uk.gov.gchq.magmacore.service.transformation.DbDeleteOperation;

/**
* Interface defining CRUD operations and generic queries for Magma Core data collections.
* Interface defining CRUD operations and generic queries for Magma Core data
* collections.
*/
public interface MagmaCoreDatabase {

Expand All @@ -42,13 +44,15 @@ public interface MagmaCoreDatabase {
void beginWrite();

/**
* Commit a transaction - Finish the current transaction and make any changes permanent (if a
* Commit a transaction - Finish the current transaction and make any changes
* permanent (if a
* "write" transaction).
*/
void commit();

/**
* Abort a transaction - Finish the transaction and undo any changes (if a "write" transaction).
* Abort a transaction - Finish the transaction and undo any changes (if a
* "write" transaction).
*/
void abort();

Expand Down Expand Up @@ -127,7 +131,8 @@ public interface MagmaCoreDatabase {
List<Thing> findByPredicateIriAndValue(IRI predicateIri, Object value);

/**
* Find object(s) that have a specific string-value attribute associated with them.
* Find object(s) that have a specific string-value attribute associated with
* them.
*
* @param predicateIri IRI of the predicate being queried.
* @param value Case-insensitive string to match.
Expand All @@ -143,7 +148,8 @@ public interface MagmaCoreDatabase {
void dump(PrintStream out);

/**
* Write the database as TTL using the {@link PrintStream} and {@link org.apache.jena.riot.Lang}.
* Write the database as TTL using the {@link PrintStream} and
* {@link org.apache.jena.riot.Lang}.
*
* @param out a {@link PrintStream}
* @param language a {@link Lang}
Expand Down Expand Up @@ -181,4 +187,43 @@ public interface MagmaCoreDatabase {
* @return a {@link List} of {@link Thing}
*/
List<Thing> executeConstruct(final String query);

/**
* Apply a set of inference rules to a subset of the model and return a
* MagmaCoreService attached to
* the resulting inference model for further use by the caller.
*
* @param constructQuery a SPARQL query String to extract a subset of the
* model for inferencing.
* @param rules a set of inference rules to be applied to the model
* subset.
* @param includeRdfsRules boolean true if inferencing should include the
* standard RDFS entailments.
* @return an in-memory MagmaCoreDatabase attached to the inferencing results
* which is
* independent of the source dataset.
*/
MagmaCoreDatabase applyInferenceRules(
final String constructQuery,
final String rules,
final boolean includeRdfsRules);

/**
* Run a validation report. This is only valid for databases obtained from
* the {@link MagmaCoreDatabase.applyInferenceRules} method.
*
* @param constructQuery a SPARQL query String to extract a subset of the
* model for inferencing.
* @param rules a set of inference rules to be applied to the model
* subset.
* @param includeRdfsRules boolean true if inferencing should include the
* standard RDFS entailments.
* @return A {@link List} of {@link ValidationReportEntry} objects.
* It will be Optional.empty if the underlying database is not an
* inference model.
*/
List<ValidationReportEntry> validate(
final String constructQuery,
final String rules,
final boolean includeRdfsRules);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.query.TxnType;
import org.apache.jena.rdf.model.InfModel;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
Expand All @@ -40,6 +41,10 @@
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.reasoner.ValidityReport;
import org.apache.jena.reasoner.ValidityReport.Report;
import org.apache.jena.reasoner.rulesys.GenericRuleReasoner;
import org.apache.jena.reasoner.rulesys.Rule;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.tdb2.TDB2Factory;
Expand All @@ -51,6 +56,7 @@

import uk.gov.gchq.magmacore.database.query.QueryResult;
import uk.gov.gchq.magmacore.database.query.QueryResultList;
import uk.gov.gchq.magmacore.database.validation.ValidationReportEntry;
import uk.gov.gchq.magmacore.hqdm.model.Thing;
import uk.gov.gchq.magmacore.hqdm.rdf.HqdmObjectFactory;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;
Expand Down Expand Up @@ -474,4 +480,75 @@ public final void load(final InputStream in, final Lang language) {
RDFDataMgr.read(model, in, language);
commit();
}

/**
* {@inheritDoc}
*/
@Override
public MagmaCoreDatabase applyInferenceRules(
final String constructQuery,
final String rules,
final boolean includeRdfsRules) {
// Create an Inference Model which will run the rules.
final InfModel model = getInferenceModel(constructQuery, rules, includeRdfsRules);

// Convert the inference model to a dataset and return it wrapped as
// an in-memory MagmaCoreDatabase.
final Dataset inferenceDataset = DatasetFactory.wrap(model);
return new MagmaCoreJenaDatabase(inferenceDataset);
}

/**
* {@inheritDoc}
*/
@Override
public List<ValidationReportEntry> validate(final String constructQuery,
final String rules,
final boolean includeRdfsRules) {
// Create an Inference Model which will run the rules.
final InfModel model = getInferenceModel(constructQuery, rules, includeRdfsRules);

// Run the validation.
final ValidityReport validityReport = model.validate();

// Convert the result to be non-Jena-specific.
final List<ValidationReportEntry> entries = new ArrayList<>();
final Iterator<Report> reports = validityReport.getReports();

while (reports.hasNext()) {
final Report report = reports.next();

entries.add(new ValidationReportEntry(
report.getType(),
report.getExtension(),
report.getDescription()));
}

return entries;
}

/**
* Create an in-memory model for inferencing.
*
* @param constructQuery {@link String}
* @param rules {@link String}
* @param includeRdfsRules boolean
* @return {@link InfModel}
*/
private InfModel getInferenceModel(
final String constructQuery,
final String rules,
final boolean includeRdfsRules) {
// Get the default Model
// Execute the query to get a subset of the data model.
final QueryExecution queryExec = QueryExecutionFactory.create(constructQuery, dataset);
final Model subset = queryExec.execConstruct();

// Parse the rules and create a reasoner using the rules and the sunset Model.
final List<Rule> ruleSet = Rule.parseRules(rules);
final GenericRuleReasoner reasoner = new GenericRuleReasoner(ruleSet);

// Create an Inference Model which will run the rules.
return ModelFactory.createInfModel(reasoner, subset);
}
}
Loading
Loading