Files
memgraph/src/query/plan/cost_estimator.cpp
florijan 1d112e1141 GraphDbAccessor - style change
Summary: Not strictly neccessary, but it's been itching me. It took an hour.

Reviewers: buda, mislav.bradac

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D648
2017-08-09 16:09:08 +02:00

147 lines
5.3 KiB
C++

#include <experimental/optional>
#include "cost_estimator.hpp"
namespace query::plan {
bool CostEstimator::PostVisit(ScanAll &) {
cardinality_ *= db_accessor_.VerticesCount();
// ScanAll performs some work for every element that is produced
IncrementCost(CostParam::kScanAll);
return true;
}
bool CostEstimator::PostVisit(ScanAllByLabel &scan_all_by_label) {
cardinality_ *= db_accessor_.VerticesCount(scan_all_by_label.label());
// ScanAll performs some work for every element that is produced
IncrementCost(CostParam::kScanAllByLabel);
return true;
}
bool CostEstimator::PostVisit(ScanAllByLabelPropertyValue &logical_op) {
// this cardinality estimation depends on the property value (expression).
// if it's a literal (const) we can evaluate cardinality exactly, otherwise
// we estimate
std::experimental::optional<PropertyValue> property_value =
std::experimental::nullopt;
if (auto *literal = dynamic_cast<PrimitiveLiteral *>(logical_op.expression()))
if (literal->value_.IsPropertyValue())
property_value =
std::experimental::optional<PropertyValue>(literal->value_);
double factor = 1.0;
if (property_value)
// get the exact influence based on ScanAll(label, property, value)
factor = db_accessor_.VerticesCount(
logical_op.label(), logical_op.property(), property_value.value());
else
// estimate the influence as ScanAll(label, property) * filtering
factor =
db_accessor_.VerticesCount(logical_op.label(), logical_op.property()) *
CardParam::kFilter;
cardinality_ *= factor;
// ScanAll performs some work for every element that is produced
IncrementCost(CostParam::MakeScanAllByLabelPropertyValue);
return true;
}
namespace {
// converts an optional ScanAll range bound into a property value
// if the bound is present and is a literal expression convertible to
// a property value. otherwise returns nullopt
std::experimental::optional<utils::Bound<PropertyValue>> BoundToPropertyValue(
std::experimental::optional<ScanAllByLabelPropertyRange::Bound> bound) {
if (bound)
if (auto *literal = dynamic_cast<PrimitiveLiteral *>(bound->value()))
return std::experimental::make_optional(
utils::Bound<PropertyValue>(literal->value_, bound->type()));
return std::experimental::nullopt;
}
}
bool CostEstimator::PostVisit(ScanAllByLabelPropertyRange &logical_op) {
// this cardinality estimation depends on Bound expressions.
// if they are literals we can evaluate cardinality properly
auto lower = BoundToPropertyValue(logical_op.lower_bound());
auto upper = BoundToPropertyValue(logical_op.upper_bound());
int64_t factor = 1;
if (upper || lower)
// if we have either Bound<PropertyValue>, use the value index
factor = db_accessor_.VerticesCount(logical_op.label(),
logical_op.property(), lower, upper);
else
// no values, but we still have the label
factor =
db_accessor_.VerticesCount(logical_op.label(), logical_op.property());
// if we failed to take either bound from the op into account, then apply
// the filtering constant to the factor
if ((logical_op.upper_bound() && !upper) ||
(logical_op.lower_bound() && !lower))
factor *= CardParam::kFilter;
cardinality_ *= factor;
// ScanAll performs some work for every element that is produced
IncrementCost(CostParam::MakeScanAllByLabelPropertyRange);
return true;
}
// For the given op first increments the cardinality and then cost.
#define POST_VISIT_CARD_FIRST(NAME) \
bool CostEstimator::PostVisit(NAME &) { \
cardinality_ *= CardParam::k##NAME; \
IncrementCost(CostParam::k##NAME); \
return true; \
}
POST_VISIT_CARD_FIRST(Expand);
POST_VISIT_CARD_FIRST(ExpandVariable);
POST_VISIT_CARD_FIRST(ExpandBreadthFirst);
#undef POST_VISIT_CARD_FIRST
// For the given op first increments the cost and then cardinality.
#define POST_VISIT_COST_FIRST(LOGICAL_OP, PARAM_NAME) \
bool CostEstimator::PostVisit(LOGICAL_OP &) { \
IncrementCost(CostParam::PARAM_NAME); \
cardinality_ *= CardParam::PARAM_NAME; \
return true; \
}
POST_VISIT_COST_FIRST(Filter, kFilter)
POST_VISIT_COST_FIRST(ExpandUniquenessFilter<VertexAccessor>,
kExpandUniquenessFilter);
POST_VISIT_COST_FIRST(ExpandUniquenessFilter<EdgeAccessor>,
kExpandUniquenessFilter);
#undef POST_VISIT_COST_FIRST
bool CostEstimator::PostVisit(Unwind &unwind) {
// Unwind cost depends more on the number of lists that get unwound
// much less on the number of outputs
// for that reason first increment cost, then modify cardinality
IncrementCost(CostParam::kUnwind);
// try to determine how many values will be yielded by Unwind
// if the Unwind expression is a list literal, we can deduce cardinality
// exactly, otherwise we approximate
int unwind_value;
if (auto literal =
dynamic_cast<query::ListLiteral *>(unwind.input_expression()))
unwind_value = literal->elements_.size();
else
unwind_value = MiscParam::kUnwindNoLiteral;
cardinality_ *= unwind_value;
return true;
}
bool CostEstimator::Visit(Once &) { return true; }
bool CostEstimator::Visit(CreateIndex &) { return true; }
} // namespace query::plan