Skip to content

Commit

Permalink
Merge pull request #195 from gchq/118-data-integrity-checks
Browse files Browse the repository at this point in the history
Inference Rules and Data Integrity Checks
  • Loading branch information
GCHQDeveloper42 authored Apr 5, 2024
2 parents 1685b2d + 7c48d3f commit 0768de2
Show file tree
Hide file tree
Showing 13 changed files with 1,978 additions and 74 deletions.
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

0 comments on commit 0768de2

Please sign in to comment.