| 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)); |
| } |
| } |
| } |
| } |
| } |