
org.apache.jena.sparql.path.PathLib Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jena-arq Show documentation
Show all versions of jena-arq Show documentation
ARQ is a SPARQL 1.1 query engine for Apache Jena
/*
* 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.
*/
package org.apache.jena.sparql.path;
import java.util.ArrayList ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Objects;
import java.util.function.Predicate;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.sparql.ARQInternalErrorException ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.op.OpBGP ;
import org.apache.jena.sparql.algebra.op.OpPath ;
import org.apache.jena.sparql.algebra.op.OpSequence ;
import org.apache.jena.sparql.core.BasicPattern ;
import org.apache.jena.sparql.core.PathBlock ;
import org.apache.jena.sparql.core.TriplePath ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.engine.ExecutionContext ;
import org.apache.jena.sparql.engine.QueryIterator ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.engine.binding.BindingFactory ;
import org.apache.jena.sparql.engine.iterator.QueryIterConcat ;
import org.apache.jena.sparql.engine.iterator.QueryIterPlainWrapper ;
import org.apache.jena.sparql.engine.iterator.QueryIterYieldN ;
import org.apache.jena.sparql.mgt.Explain ;
import org.apache.jena.sparql.path.eval.PathEval ;
import org.apache.jena.sparql.pfunction.PropertyFunctionFactory ;
import org.apache.jena.sparql.pfunction.PropertyFunctionRegistry ;
import org.apache.jena.sparql.util.graph.GraphUtils ;
public class PathLib
{
/** Convert any paths of exactly one predicate to a triple pattern */
public static Op pathToTriples(PathBlock pattern) {
BasicPattern bp = null;
Op op = null;
for ( TriplePath tp : pattern ) {
if ( tp.isTriple() ) {
if ( bp == null )
bp = new BasicPattern();
bp.add(tp.asTriple());
continue;
}
// Path form.
op = flush(bp, op);
bp = null;
OpPath opPath2 = new OpPath(tp);
op = OpSequence.create(op, opPath2);
continue;
}
// End. Finish off any outstanding BGP.
op = flush(bp, op);
return op;
}
static private Op flush(BasicPattern bp, Op op) {
if ( bp == null || bp.isEmpty() )
return op;
OpBGP opBGP = new OpBGP(bp);
op = OpSequence.create(op, opBGP);
return op;
}
/** Install a path as a property function in the global property function registry */
public static void install(String uri, Path path)
{ install(uri, path, PropertyFunctionRegistry.get()) ; }
/** Install a path as a property function in a given registry */
public static void install(String uri, final Path path, PropertyFunctionRegistry registry) {
PropertyFunctionFactory pathPropFuncFactory = (u) -> new PathPropertyFunction(path) ;
registry.put(uri, pathPropFuncFactory) ;
}
public static QueryIterator execTriplePath(Binding binding, TriplePath triplePath, ExecutionContext execCxt) {
if ( triplePath.isTriple() ) {
// Fake it. This happens only for API constructed situations.
Path path = new P_Link(triplePath.getPredicate());
triplePath = new TriplePath(triplePath.getSubject(), path, triplePath.getObject());
}
return execTriplePath(binding,
triplePath.getSubject(),
triplePath.getPath(),
triplePath.getObject(),
execCxt) ;
}
public static QueryIterator execTriplePath(Binding binding, Node s, Path path, Node o, ExecutionContext execCxt) {
Explain.explain(s, path, o, execCxt.getContext()) ;
s = Var.lookup(binding, s) ;
o = Var.lookup(binding, o) ;
Iterator iter = null ;
Node endNode = null ;
Graph graph = execCxt.getActiveGraph() ;
// Both variables.
if ( Var.isVar(s) && Var.isVar(o) ) {
if ( s.equals(o) )
return execUngroundedPathSameVar(binding, graph, Var.alloc(s), path, execCxt);
else
return execUngroundedPath(binding, graph, Var.alloc(s), path, Var.alloc(o), execCxt);
}
// Both constants.
if ( !Var.isVar(s) && !Var.isVar(o) )
return evalGroundedPath(binding, graph, s, path, o, execCxt);
// One variable, one constant
if ( Var.isVar(s) ) {
// Var subject, concrete object - do backwards.
iter = PathEval.evalReverse(graph, o, path, execCxt.getContext());
endNode = s;
} else {
iter = PathEval.eval(graph, s, path, execCxt.getContext());
endNode = o;
}
return evalGroundedOneEnd(binding, iter, endNode, execCxt);
}
private static QueryIterator evalGroundedOneEnd(Binding binding, Iterator iter, Node endNode, ExecutionContext execCxt) {
List results = new ArrayList<>() ;
if (! Var.isVar(endNode))
throw new ARQInternalErrorException("Non-variable endnode in _execTriplePath") ;
Var var = Var.alloc(endNode) ;
// Assign.
for (; iter.hasNext();) {
Node n = iter.next() ;
results.add(BindingFactory.binding(binding, var, n)) ;
}
return new QueryIterPlainWrapper(results.iterator(), execCxt) ;
}
// Subject and object are nodes.
private static QueryIterator evalGroundedPath(Binding binding,
Graph graph, Node subject, Path path, Node object,
ExecutionContext execCxt) {
Iterator iter = PathEval.eval(graph, subject, path, execCxt.getContext()) ;
// Now count the number of matches.
int count = 0 ;
for ( ; iter.hasNext() ; ) {
Node n = iter.next() ;
if ( n.sameValueAs(object) )
count++ ;
}
return new QueryIterYieldN(count, binding, execCxt) ;
}
// Brute force evaluation of a TriplePath where neither subject nor object are bound
private static QueryIterator execUngroundedPath(Binding binding, Graph graph, Var sVar, Path path, Var oVar, ExecutionContext execCxt) {
// Starting points.
Iterator iter = determineUngroundedStartingSet(graph, path, execCxt) ;
QueryIterConcat qIterCat = new QueryIterConcat(execCxt) ;
for ( ; iter.hasNext() ; )
{
Node n = iter.next() ;
Binding b2 = BindingFactory.binding(binding, sVar, n) ;
Iterator pathIter = PathEval.eval(graph, n, path, execCxt.getContext()) ;
QueryIterator qIter = evalGroundedOneEnd(b2, pathIter, oVar, execCxt) ;
qIterCat.add(qIter) ;
}
return qIterCat ;
}
private static QueryIterator execUngroundedPathSameVar(Binding binding, Graph graph, Var var, Path path, ExecutionContext execCxt) {
// Try each end, ungrounded.
// Slightly more efficient would be to add a per-engine to do this.
Iterator iter = determineUngroundedStartingSet(graph, path, execCxt) ;
QueryIterConcat qIterCat = new QueryIterConcat(execCxt) ;
for ( ; iter.hasNext() ; )
{
Node n = iter.next() ;
Binding b2 = BindingFactory.binding(binding, var, n) ;
int x = existsPath(graph, n, path, n, execCxt) ;
if ( x > 0 )
{
QueryIterator qIter = new QueryIterYieldN(x, b2, execCxt) ;
qIterCat.add(qIter) ;
}
}
return qIterCat ;
}
private static Iterator determineUngroundedStartingSet(Graph graph, Path path, ExecutionContext execCxt) {
// Find a better set of seed values than "everything"
// (:p+) and (^:p)+
// :p* need everything because it is always the case that " :p* "
if ( path instanceof P_OneOrMore1 || path instanceof P_OneOrMoreN ) {
Path subPath = ((P_Path1)path).getSubPath() ;
if ( subPath instanceof P_Link ) {
// :predicate+
P_Link link = (P_Link)subPath ;
Iterator sIter = graph.find(null, link.getNode(), null) ;
return Iter.iter(sIter).distinctAdjacent().map(Triple::getSubject).distinct() ;
} else {
if ( subPath instanceof P_Inverse ) {
P_Inverse pInv = (P_Inverse)subPath ;
if ( pInv.getSubPath() instanceof P_Link ) {
// (^:predicate)+
P_Link link = (P_Link)(pInv.getSubPath()) ;
Iterator sIter = graph.find(null, link.getNode(), null) ;
return Iter.iter(sIter).distinctAdjacent().map(Triple::getObject).distinct() ;
}
}
}
}
// No idea - everything.
return GraphUtils.allNodes(graph) ;
}
private static int existsPath(Graph graph, Node subject, Path path, final Node object, ExecutionContext execCxt) {
if ( ! subject.isConcrete() || !object.isConcrete() )
throw new ARQInternalErrorException("Non concrete node for existsPath evaluation") ;
Iterator iter = PathEval.eval(graph, subject, path, execCxt.getContext()) ;
Predicate filter = node -> Objects.equals(node, object);
// See if we got to the node we're interested in finishing at.
iter = Iter.filter(iter, filter) ;
long x = Iter.count(iter) ;
return (int)x ;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy