/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb;
import java.util.HashMap;
import org.hypergraphdb.query.And;
import org.hypergraphdb.query.AnyAtomCondition;
import org.hypergraphdb.query.AtomPartCondition;
import org.hypergraphdb.query.AtomTypeCondition;
import org.hypergraphdb.query.AtomValueCondition;
import org.hypergraphdb.query.BFSCondition;
import org.hypergraphdb.query.ComparisonOperator;
import org.hypergraphdb.query.DFSCondition;
import org.hypergraphdb.query.HGQueryCondition;
import org.hypergraphdb.query.IncidentCondition;
import org.hypergraphdb.query.IndexCondition;
import org.hypergraphdb.query.IndexedPartCondition;
import org.hypergraphdb.query.LinkCondition;
import org.hypergraphdb.query.MapCondition;
import org.hypergraphdb.query.Nothing;
import org.hypergraphdb.query.Or;
import org.hypergraphdb.query.OrderedLinkCondition;
import org.hypergraphdb.query.SubsumedCondition;
import org.hypergraphdb.query.SubsumesCondition;
import org.hypergraphdb.query.TargetCondition;
import org.hypergraphdb.query.TypePlusCondition;
import org.hypergraphdb.query.TypedValueCondition;
import org.hypergraphdb.query.cond2qry.ExpressionBasedQuery;
import org.hypergraphdb.type.HGAtomType;
// This is a temporary implementation, to research a bit what's involved in
// estimating result set sizes...
@SuppressWarnings("unchecked")
class ResultSizeEstimation
{
// count() should return the size of of the result set if the condition
// is evaluated in cost() should simply estimate how costly it would
// be the perform the count. cost will return Integer.MAX_VALUE when
// the only way to count is the evaluate the query and scan the result set
public interface Counter
{
long count(HyperGraph graph, HGQueryCondition cond);
long cost(HyperGraph graph, HGQueryCondition cond);
}
static HashMap<Class<?>, Counter> countersMap = new HashMap<Class<?>, Counter>();
static long countResultSet(HyperGraph graph, HGQueryCondition cond)
{
return countResultSet(HGQuery.make(graph, cond));
}
static long countResultSet(HGQuery<?> q)
{
// need to do full query.
HGSearchResult<HGPersistentHandle> rs = (HGSearchResult<HGPersistentHandle>)q.execute();
try
{
long result = 0;
for (; rs.hasNext(); rs.next())
result++;
return result;
}
finally
{
try { rs.close(); } catch (Throwable t) { }
}
}
// The default version when no counts can be obtained by simple means
static class FullScanCounter implements Counter
{
public long count(HyperGraph graph, HGQueryCondition x)
{
return countResultSet(graph, x);
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
return Integer.MAX_VALUE;
}
}
static Counter fullScanCounter = new FullScanCounter();
static {
countersMap.put(AnyAtomCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition cond)
{
HGSearchResult<HGPersistentHandle> rs = graph.indexByType.scanKeys();
try
{
long result = 0;
while (rs.hasNext())
// TODO: this is actually stupid because that we have to create another cursor
// and position it on the same key as our rs.current...but can't break
// information hiding boundaries!
result += graph.indexByType.count(rs.next());
return result;
}
finally
{
try { rs.close(); } catch (Throwable t) { }
}
}
public long cost(HyperGraph graph, HGQueryCondition cond)
{
return graph.indexByType.count();
}
});
countersMap.put(AtomTypeCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
AtomTypeCondition cond = (AtomTypeCondition)x;
HGHandle typeHandle = cond.getTypeHandle();
if (typeHandle == null)
typeHandle = graph.getTypeSystem().getTypeHandle(cond.getJavaClass());
return graph.indexByType.count(graph.getPersistentHandle(typeHandle));
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
return 1;
}
});
countersMap.put(TypePlusCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
TypePlusCondition cond = (TypePlusCondition)x;
long result = 0;
for (HGHandle h : cond.getSubTypes(graph))
result += graph.indexByType.count(graph.getPersistentHandle(h));
return result;
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
TypePlusCondition cond = (TypePlusCondition)x;
return cond.getSubTypes(graph).size();
}
});
countersMap.put(TypedValueCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
TypedValueCondition cond = (TypedValueCondition)x;
HGHandle typeHandle = cond.getTypeHandle();
if (typeHandle == null)
typeHandle = graph.getTypeSystem().getTypeHandle(cond.getJavaClass());
HGAtomType type = graph.getTypeSystem().getType(typeHandle);
if (type instanceof HGSearchable && cond.getOperator() == ComparisonOperator.EQ)
{
HGSearchResult<HGPersistentHandle> rs = ((HGSearchable)type).find(cond.getValue());
try
{
long result = 0;
while (rs.hasNext())
result += graph.indexByValue.count(rs.next());
return result;
}
finally
{
try { rs.close(); } catch (Throwable t) { }
}
}
else
{
return countResultSet(graph, x);
}
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
TypedValueCondition cond = (TypedValueCondition)x;
HGHandle typeHandle = cond.getTypeHandle();
if (typeHandle == null)
typeHandle = graph.getTypeSystem().getTypeHandle(cond.getJavaClass());
HGAtomType type = graph.getTypeSystem().getType(typeHandle);
if (type instanceof HGSearchable && cond.getOperator() == ComparisonOperator.EQ)
return 2;
else
return Integer.MAX_VALUE;
}
});
countersMap.put(AtomValueCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
AtomValueCondition vc = (AtomValueCondition)x;
Object value = vc.getValue();
if (value == null)
throw new HGException("Count by null values is not supported yet.");
HGHandle type = graph.getTypeSystem().getTypeHandle(value);
return countersMap.get(TypedValueCondition.class).
count(graph, new TypedValueCondition(type,
vc.getValue(),
vc.getOperator()));
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
AtomValueCondition vc = (AtomValueCondition)x;
Object value = vc.getValue();
if (value == null)
throw new HGException("Count by null values is not supported yet.");
HGHandle type = graph.getTypeSystem().getTypeHandle(value);
return countersMap.get(TypedValueCondition.class).
cost(graph, new TypedValueCondition(type,
vc.getValue(),
vc.getOperator()));
}
});
countersMap.put(TargetCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
HGHandle h = ((TargetCondition)x).getLink();
if (graph.isLoaded(h))
return ((HGLink)graph.get(h)).getArity();
else
{
HGPersistentHandle [] A = graph.getStore().getLink(graph.getPersistentHandle(h));
if (A == null)
throw new NullPointerException("No link data for handle " + h);
else
return A.length - 2;
}
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
return 1;
}
});
countersMap.put(IncidentCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
IncidentCondition cond = (IncidentCondition)x;
return graph.getStore().getIncidenceSetCardinality(
graph.getPersistentHandle(cond.getTarget()));
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
return 1;
}
});
countersMap.put(MapCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
x = ((MapCondition)x).getCondition();
Counter c = countersMap.get(x.getClass());
return c == null ? countResultSet(graph, x) : c.count(graph, x);
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
x = ((MapCondition)x).getCondition();
Counter c = countersMap.get(x.getClass());
return c == null ? Integer.MAX_VALUE : c.cost(graph, x);
}
});
countersMap.put(IndexCondition.class, new Counter()
{
@SuppressWarnings("rawtypes")
public long count(HyperGraph graph, HGQueryCondition x)
{
IndexCondition ic = (IndexCondition)x;
if (ic.getOperator() == ComparisonOperator.EQ)
return ic.getIndex().count(ic.getKey());
else
return countResultSet(graph, ic);
}
@SuppressWarnings("rawtypes")
public long cost(HyperGraph graph, HGQueryCondition x)
{
IndexCondition ic = (IndexCondition)x;
return ic.getOperator() == ComparisonOperator.EQ ? 1 : Integer.MAX_VALUE;
}
});
countersMap.put(IndexedPartCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
IndexedPartCondition ip = (IndexedPartCondition)x;
if (ip.getOperator() == ComparisonOperator.EQ)
return ((HGIndex<Object, Object>)ip.getIndex()).count(ip.getPartValue());
else
return countResultSet(graph, ip);
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
IndexedPartCondition ip = (IndexedPartCondition)x;
return ip.getOperator() == ComparisonOperator.EQ ? 1 : Integer.MAX_VALUE;
}
});
countersMap.put(AtomPartCondition.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
throw new HGException("Can't count AtomPartCondition results: this condition can't be used alone.");
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
throw new HGException("Can't estimate cost of counting AtomPartCondition results: this condition can't be used alone.");
}
});
countersMap.put(And.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
ExpressionBasedQuery q = (ExpressionBasedQuery)HGQuery.make(graph, x);
x = q.getCondition();
if (x == Nothing.Instance)
return 0;
And cond = (And)q.getCondition();
if (cond.size() == 1)
{
x = cond.get(0);
Counter c = countersMap.get(cond.get(0).getClass());
if (c != null)
return c.count(graph, x);
}
return countResultSet(q);
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
ExpressionBasedQuery q = (ExpressionBasedQuery)HGQuery.make(graph, x);
x = q.getCondition();
if (x == Nothing.Instance)
return 0;
And cond = (And)q.getCondition();
if (cond.size() == 1)
{
x = cond.get(0);
Counter c = countersMap.get(cond.get(0).getClass());
if (c != null)
return c.cost(graph, x);
}
return Integer.MAX_VALUE;
}
});
countersMap.put(Or.class, new Counter()
{
public long count(HyperGraph graph, HGQueryCondition x)
{
long result = 0;
for (HGQueryCondition cond : (Or)x)
{
Counter c = countersMap.get(cond.getClass());
result += c == null ? countResultSet(graph, cond) : c.count(graph, cond);
}
return result;
}
public long cost(HyperGraph graph, HGQueryCondition x)
{
long cost = 0;
for (HGQueryCondition cond : (Or)x)
{
Counter c = countersMap.get(cond.getClass());
if (c == null)
return Integer.MAX_VALUE;
long cc = c.cost(graph, cond);
if (cc == Integer.MAX_VALUE)
return cc;
else
cost += cc;
}
return cost;
}
});
countersMap.put(SubsumesCondition.class, fullScanCounter);
countersMap.put(SubsumedCondition.class, fullScanCounter);
countersMap.put(LinkCondition.class, fullScanCounter);
countersMap.put(OrderedLinkCondition.class, fullScanCounter);
countersMap.put(BFSCondition.class, fullScanCounter);
countersMap.put(DFSCondition.class, fullScanCounter);
}
}