/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2006-2007.
* Copyright James Leigh (c) 2006.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.algebra.evaluation.impl;
import java.util.HashSet;
import java.util.Set;
import org.openrdf.query.algebra.BinaryTupleOperator;
import org.openrdf.query.algebra.EmptySet;
import org.openrdf.query.algebra.Join;
import org.openrdf.query.algebra.LeftJoin;
import org.openrdf.query.algebra.QueryModelNode;
import org.openrdf.query.algebra.SingletonSet;
import org.openrdf.query.algebra.StatementPattern;
import org.openrdf.query.algebra.TupleExpr;
import org.openrdf.query.algebra.UnaryTupleOperator;
import org.openrdf.query.algebra.Var;
import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
/**
* Supplies various query model statistics to the query engine/optimizer.
*
* @author Arjohn Kampman
* @author James Leigh
*/
public class EvaluationStatistics {
public double getCardinality(TupleExpr expr) {
return getCardinality(expr, new HashSet<String>());
}
public double getCardinality(TupleExpr expr, Set<String> boundVars) {
CardinalityCalculator cc = getCardinalityCalculator(boundVars);
expr.visit(cc);
return cc.getCardinality();
}
protected CardinalityCalculator getCardinalityCalculator(Set<String> boundVars) {
return new CardinalityCalculator(boundVars);
}
/*-----------------------------------*
* Inner class CardinalityCalculator *
*-----------------------------------*/
protected static class CardinalityCalculator extends QueryModelVisitorBase<RuntimeException> {
protected Set<String> boundVars;
protected double cardinality;
public CardinalityCalculator(Set<String> boundVars) {
this.boundVars = boundVars;
}
public double getCardinality() {
return cardinality;
}
@Override
public void meet(EmptySet node)
{
cardinality = 0;
}
@Override
public void meet(SingletonSet node)
{
cardinality = 1;
}
@Override
public void meet(StatementPattern sp)
{
cardinality = 1000.0;
int constantVarCount = countConstantVars(sp);
int boundVarCount = countBoundVars(sp);
int sqrtFactor = 2 * boundVarCount + constantVarCount;
if (sqrtFactor >= 2) {
cardinality = Math.pow(cardinality, 1.0 / sqrtFactor);
}
// int unboundVars = 4 - countBoundVars(sp) - countConstantVars(sp);
// cardinality = 1 << unboundVars; // 2 ^ unboundVars
}
protected int countConstantVars(StatementPattern sp) {
int constantVarCount = 0;
Var[] spVars = new Var[] {
sp.getSubjectVar(),
sp.getPredicateVar(),
sp.getObjectVar(),
sp.getContextVar() };
for (Var var : spVars) {
if (var != null && var.hasValue()) {
constantVarCount++;
}
}
return constantVarCount;
}
protected int countBoundVars(StatementPattern sp) {
int boundVarCount = 0;
Var[] spVars = new Var[] {
sp.getSubjectVar(),
sp.getPredicateVar(),
sp.getObjectVar(),
sp.getContextVar() };
for (Var var : spVars) {
if (var != null && this.boundVars.contains(var.getName())) {
boundVarCount++;
}
}
return boundVarCount;
}
@Override
public void meet(Join node)
{
node.getLeftArg().visit(this);
double leftArgCost = this.cardinality;
node.getRightArg().visit(this);
cardinality *= leftArgCost;
}
@Override
public void meet(LeftJoin node)
{
node.getLeftArg().visit(this);
double leftArgCost = this.cardinality;
node.getRightArg().visit(this);
cardinality *= leftArgCost;
}
@Override
protected void meetBinaryTupleOperator(BinaryTupleOperator node)
{
node.getLeftArg().visit(this);
double leftArgCost = this.cardinality;
node.getRightArg().visit(this);
cardinality += leftArgCost;
}
@Override
protected void meetUnaryTupleOperator(UnaryTupleOperator node)
{
node.getArg().visit(this);
}
@Override
protected void meetNode(QueryModelNode node)
{
throw new IllegalArgumentException("Unhandled node type: " + node.getClass());
}
}
}