package org.linkeddatafragments.solver;
import org.apache.jena.graph.GraphStatisticsHandler;
import org.apache.jena.graph.Node;
import org.apache.jena.sparql.engine.optimizer.Pattern;
import org.apache.jena.sparql.engine.optimizer.StatsMatcher;
import org.apache.jena.sparql.engine.optimizer.reorder.PatternTriple;
import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformationSubstitution;
import org.apache.jena.sparql.graph.NodeConst;
import org.apache.jena.sparql.sse.Item;
import org.linkeddatafragments.model.LinkedDataFragmentGraph;
import static org.apache.jena.sparql.engine.optimizer.reorder.PatternElements.TERM;
import static org.apache.jena.sparql.engine.optimizer.reorder.PatternElements.VAR;
/**
* Reorders the Triple Patterns of a BGP by using statistics directly fetched from
* the dataset. At query optimization phase, when planning joins, some variables
* are known to be bound at some stage, but the actual values are unknown.
* In this case it uses predefined typical behaviour for RDF, using independent
* histograms for S/P/O, inspired by Jena's FixedReorder and HDT's reorder.
*
* @author ldevocht
*
*/
public class ReorderTransformationLDF extends ReorderTransformationSubstitution {
/** Maximum value for a match involving two terms. */
public final long multiTermMax ;
private final long numTriples ; // Actual number of triples of the dataset.
private GraphStatisticsHandler stats;
public final StatsMatcher matcher = new StatsMatcher() ;
public ReorderTransformationLDF(LinkedDataFragmentGraph graph)
{
this.stats = graph.getStatisticsHandler();
numTriples = graph.size();
multiTermMax = numTriples/100;
initializeMatcher();
}
private void initializeMatcher() {
Item type = Item.createNode(NodeConst.nodeRDFType);
//matcher.addPattern(new Pattern(1, TERM, TERM, TERM)) ; // SPO - built-in - not needed as a rule
// Numbers choosen as an approximation for a ldf graph of size numTriples
matcher.addPattern(new Pattern( 5, TERM, TERM, VAR)); // SP?
matcher.addPattern(new Pattern(Math.max(numTriples / 1000, 1000), VAR, type, TERM)); // ? type O -- worse than ?PO
matcher.addPattern(new Pattern(Math.max(numTriples / 10000, 90), VAR, TERM, TERM)); // ?PO
matcher.addPattern(new Pattern( 3, TERM, VAR, TERM)); // S?O
matcher.addPattern(new Pattern( 40, TERM, VAR, VAR)); // S??
matcher.addPattern(new Pattern( 200, VAR, VAR, TERM)); // ??O
matcher.addPattern(new Pattern(Math.max(numTriples / 200, 2000), VAR, TERM, VAR)); // ?P?
matcher.addPattern(new Pattern(numTriples, VAR, VAR, VAR)); // ???
}
@Override
protected double weight(PatternTriple pt)
{
// If all are nodes, there are no substitutions. We can get the exact number.
if(pt.subject.isNode() && pt.predicate.isNode() && pt.object.isNode()) {
return stats.getStatistic(pt.subject.getNode(), pt.predicate.getNode(), pt.object.getNode());
}
// Try on fixed
double x = matcher.match(pt);
// If there are two fixed terms, use the fixed weighting, all of which are quite small.
// This chooses a less optimal triple but the worse choice is still a very selective choice.
// One case is IFPs: the multi term choice for PO is not 1.
if ( x < multiTermMax )
{
return x;
}
// One or zero fixed terms.
// Otherwise, assuming S / P / O independent, do an estimation.
long S = stats.getStatistic(pt.subject.getNode(), Node.ANY, Node.ANY);
long P = stats.getStatistic(Node.ANY, pt.predicate.getNode(), Node.ANY);
long O = stats.getStatistic(Node.ANY, Node.ANY, pt.object.getNode());
if ( S == 0 || P == 0 || O == 0 ) {
// Can't match.
return 0 ;
}
// Find min positive
x = -1 ;
if ( S > 0 ) x = S ;
if ( P > 0 && P < x ) x = P ;
if ( O > 0 && O < x ) x = O ;
//System.out.printf("** [%d, %d, %d]\n", S, P ,O) ;
return x;
}
}