blob: 974f4dffa42755f0e03aca10e7c41bf326ba140a [file] [log] [blame]
package org.apache.rya.rdftriplestore.inference;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
import org.apache.rya.api.utils.NullableStatementImpl;
import org.apache.rya.rdftriplestore.utils.FixedStatementPattern;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.OWL;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.query.algebra.StatementPattern;
import org.eclipse.rdf4j.query.algebra.TupleExpr;
import org.eclipse.rdf4j.query.algebra.Var;
/**
* Expands the query tree to account for any relevant has-value class
* expressions in the ontology known to the {@link InferenceEngine}.
*
* Only operates on {@link StatementPattern} nodes, and only those including a
* defined type or defined predicate which is relevant to a has-value
* expression in the ontology. When applicable, replaces the node with one or
* more nested {@link InferUnion}s, one of whose leaves is the original
* StatementPattern.
*
* A has-value class expression references a specific predicate and a specific
* value (object or literal), and represents the set of all individuals having
* the specified value for the specified predicate. This has two implications
* for inference: 1) If an individual has the specified value for the specified
* predicate, then it implicitly belongs to the has-value class expression; and
* 2) If an individual belongs to the has-value class expression, then it
* implicitly has the specified value for the specified predicate.
*
* To handle the first case, the visitor expands statement patterns of the form
* "?individual rdf:type :class" if the class or any of its subclasses are known
* to be has-value class expressions. (Does not apply if the class term
* is a variable.) The resulting query tree will match individuals who
* implicitly belong to the class expression because they have the specified
* value for the specified property, as well as any individuals explicitly
* stated to belong to the class.
*
* To handle the second case, the visitor expands statement patterns of the form
* "?individual :predicate ?value" if the predicate is known to be referenced by
* any has-value expression. (Does not apply if the predicate term is a
* variable.) The resulting query tree will match individuals and values that
* can be derived from the individual's membership in a has-value class
* expression (which itself may be explicit or derived from membership in a
* subclass of the has-value class expression), as well as any individuals and
* values explicitly stated.
*/
public class HasValueVisitor extends AbstractInferVisitor {
/**
* Creates a new {@link HasValueVisitor}.
* @param conf The {@link RdfCloudTripleStoreConfiguration}.
* @param inferenceEngine The InferenceEngine containing the relevant ontology.
*/
public HasValueVisitor(RdfCloudTripleStoreConfiguration conf, InferenceEngine inferenceEngine) {
super(conf, inferenceEngine);
include = conf.isInferHasValue();
}
/**
* Checks whether facts matching the StatementPattern could be derived using
* has-value inference, and if so, replaces the StatementPattern node with a
* union of itself and any such possible derivations.
*/
@Override
protected void meetSP(StatementPattern node) throws Exception {
final Var subjVar = node.getSubjectVar();
final Var predVar = node.getPredicateVar();
final Var objVar = node.getObjectVar();
// We can reason over two types of statement patterns:
// { ?var rdf:type :Restriction } and { ?var :property ?value }
// Both require defined predicate
if (predVar != null && predVar.getValue() != null) {
final IRI predIRI = (IRI) predVar.getValue();
if (RDF.TYPE.equals(predIRI) && objVar != null && objVar.getValue() != null
&& objVar.getValue() instanceof Resource) {
// If the predicate is rdf:type and the type is specified, check whether it can be
// inferred using any hasValue restriction(s)
final Resource objType = (Resource) objVar.getValue();
final Map<IRI, Set<Value>> sufficientValues = inferenceEngine.getHasValueByType(objType);
if (sufficientValues.size() > 0) {
final Var valueVar = new Var("v-" + UUID.randomUUID());
TupleExpr currentNode = node.clone();
for (IRI property : sufficientValues.keySet()) {
final Var propVar = new Var(property.toString(), property);
final TupleExpr valueSP = new DoNotExpandSP(subjVar, propVar, valueVar);
final FixedStatementPattern relevantValues = new FixedStatementPattern(objVar, propVar, valueVar);
for (Value value : sufficientValues.get(property)) {
relevantValues.statements.add(new NullableStatementImpl(objType, property, value));
}
currentNode = new InferUnion(currentNode, new InferJoin(relevantValues, valueSP));
}
node.replaceWith(currentNode);
}
}
else {
// If the predicate has some hasValue restriction associated with it, then finding
// that the object belongs to the appropriate type implies a value.
final Map<Resource, Set<Value>> impliedValues = inferenceEngine.getHasValueByProperty(predIRI);
if (impliedValues.size() > 0) {
final Var rdfTypeVar = new Var(RDF.TYPE.stringValue(), RDF.TYPE);
final Var typeVar = new Var("t-" + UUID.randomUUID());
final Var hasValueVar = new Var(OWL.HASVALUE.stringValue(), OWL.HASVALUE);
final TupleExpr typeSP = new DoNotExpandSP(subjVar, rdfTypeVar, typeVar);
final FixedStatementPattern typeToValue = new FixedStatementPattern(typeVar, hasValueVar, objVar);
final TupleExpr directValueSP = node.clone();
for (Resource type : impliedValues.keySet()) {
// { ?var rdf:type :type } implies { ?var :property :val } for certain (:type, :val) pairs
for (Value impliedValue : impliedValues.get(type)) {
typeToValue.statements.add(new NullableStatementImpl(type, OWL.HASVALUE, impliedValue));
}
}
node.replaceWith(new InferUnion(new InferJoin(typeToValue, typeSP), directValueSP));
}
}
}
}
}