Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support variable when seeking by property index in match clause #5553

Merged
merged 10 commits into from
May 18, 2023
Next Next commit
Support variable when seeking by prop index in match clause
  • Loading branch information
yixinglu committed May 16, 2023
commit 7da740bb21487fc224965c4b7aa9de18290eae84
1 change: 1 addition & 0 deletions src/graph/planner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ nebula_add_library(
match/MatchPathPlanner.cpp
match/ShortestPathPlanner.cpp
match/VariableVertexIdSeek.cpp
match/VariablePropIndexSeek.cpp
ngql/PathPlanner.cpp
ngql/GoPlanner.cpp
ngql/SubgraphPlanner.cpp
Expand Down
4 changes: 4 additions & 0 deletions src/graph/planner/PlannersRegister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "graph/planner/match/ScanSeek.h"
#include "graph/planner/match/StartVidFinder.h"
#include "graph/planner/match/VariableVertexIdSeek.h"
#include "graph/planner/match/VariablePropIndexSeek.h"
#include "graph/planner/match/VertexIdSeek.h"
#include "graph/planner/ngql/FetchEdgesPlanner.h"
#include "graph/planner/ngql/FetchVerticesPlanner.h"
Expand Down Expand Up @@ -103,6 +104,9 @@ void PlannersRegister::registerMatch() {
// WITH 'xxx' AS vid MATCH(n) WHERE id(n)==vid RETURN n
startVidFinders.emplace_back(&VariableVertexIdSeek::make);

// WITH "xxx" AS prop MATCH(n:Tag) WHERE n.Tag.prop==prop RETURN n
startVidFinders.emplace_back(&VariablePropIndexSeek::make);

// seek by tag or edge(index)
// MATCH(n: tag) RETURN n
// MATCH(s)-[:edge]->(e) RETURN e
Expand Down
85 changes: 85 additions & 0 deletions src/graph/planner/match/MatchSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,91 @@ Expression* MatchSolver::makeIndexFilter(const std::string& label,
return root;
}

// Expression* MatchSolver::rewriteTagIndexFilter(const std::string& label,
// const std::string& alias,
// Expression* filter,
// QueryContext* qctx,
// std::string* varName) {
// auto kind = filter->kind();
// if (kind != Expression::Kind::kRelEQ && kind != Expression::Kind::kLogicalAnd) {
// return nullptr;
// }

// std::vector<const Expression*> ands;
// if (kind == Expression::Kind::kLogicalAnd) {
// auto* logic = static_cast<LogicalExpression*>(filter);
// ExpressionUtils::pullAnds(logic);
// for (auto& operand : logic->operands()) {
// if (operand->kind() == Expression::Kind::kRelEQ ||
// operand->kind() == Expression::Kind::kRelIn) {
// ands.emplace_back(operand);
// }
// }
// } else {
// ands.emplace_back(filter);
// }

// std::string propName;
// auto checkExpr = [varName, &alias, &label, &propName](const Expression* expr,
// const Expression* varExpr) -> bool {
// if (expr->kind() != Expression::Kind::kLabelTagProperty ||
// varExpr->kind() != Expression::Kind::kLabel) {
// return false;
// }

// auto laExpr = static_cast<const LabelTagPropertyExpression*>(expr);
// if (laExpr->sym() != label) return false;

// auto propExpr = dynamic_cast<const PropertyExpression*>(laExpr->label());
// if (!propExpr || propExpr->prop() != alias) return false;

// auto labelExpr = static_cast<const LabelExpression*>(varExpr);
// *varName = labelExpr->name();
// propName = laExpr->prop();

// return true;
// };

// auto* pool = qctx->objPool();
// std::vector<Expression*> relationals;
// for (auto* item : ands) {
// if (item->kind() != Expression::Kind::kRelEQ) {
// continue;
// }

// auto* binary = static_cast<const RelationalExpression*>(item);
// auto* left = binary->left();
// auto* right = binary->right();
// if (left->kind() == Expression::Kind::kLabelTagProperty) {
// if (!checkExpr(left, right)) continue;
// } else if (right->kind() == Expression::Kind::kLabelTagProperty) {
// if (!checkExpr(right, left)) continue;
// } else {
// continue;
// }

// auto tpExpr = TagPropertyExpression::make(pool, label, propName);
// if (right->kind() == Expression::Kind::kConstant) {
// auto* rel = RelationalExpression::makeKind(pool, item->kind(), tpExpr, newConstant);
// relationals.emplace_back(rel);
// } else {
// auto* rel = RelationalExpression::makeKind(pool, item->kind(), newConstant, tpExpr);
// relationals.emplace_back(rel);
// }
// }

// if (relationals.empty()) {
// return nullptr;
// }

// auto* root = relationals[0];
// for (auto i = 1u; i < relationals.size(); i++) {
// root = LogicalExpression::makeAnd(qctx->objPool(), root, relationals[i]);
// }

// return root;
// }

PlanNode* MatchSolver::filtPathHasSameEdge(PlanNode* input,
const std::string& column,
QueryContext* qctx) {
Expand Down
7 changes: 7 additions & 0 deletions src/graph/planner/match/MatchSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class MatchSolver final {
Expression* filter,
QueryContext* qctx,
bool isEdgeProperties = false);
// Rewrite the tag index filter in the following way:
// v.player.name==name => v.player.name IN $name
// static Expression* rewriteTagIndexFilter(const std::string& label,
// const std::string& alias,
// Expression* filter,
// QueryContext* qctx,
// std::string* varName);

static PlanNode* filtPathHasSameEdge(PlanNode* input,
const std::string& column,
Expand Down
7 changes: 5 additions & 2 deletions src/graph/planner/match/StartVidFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ using StartVidFinderInstantiateFunc = std::function<std::unique_ptr<StartVidFind
// 4. VariableVertexIdSeek
// WITH "xx" AS vid MATCH (v:Tag) WHERE id(v)==vid RETURN v
//
// 5. LabelIndexSeek finds if a plan could traverse from some vids that could be
// 5. VariablePropIndexSeek
// WITH "xx" AS prop MATCH (v:Tag) WHERE v.Tag.prop==prop RETURN v
//
// 6. LabelIndexSeek finds if a plan could traverse from some vids that could be
// read from the label indices.
// MATCH(n: tag) RETURN n
// MATCH(s)-[:edge]->(e) RETURN e
//
// 6. ScanSeek finds if a plan could traverse from some vids by scanning.
// 7. ScanSeek finds if a plan could traverse from some vids by scanning.
//
class StartVidFinder {
public:
Expand Down
68 changes: 68 additions & 0 deletions src/graph/planner/match/VariablePropIndexSeek.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright (c) 2023 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

#include "graph/planner/match/VariablePropIndexSeek.h"

#include "graph/planner/match/MatchSolver.h"
#include "graph/planner/plan/Logic.h"
#include "graph/planner/plan/Query.h"
#include "graph/util/ExpressionUtils.h"

namespace nebula {
namespace graph {

bool VariablePropIndexSeek::matchNode(NodeContext* nodeCtx) {
auto& nodeInfo = *nodeCtx->info;
const auto& labels = nodeInfo.labels;
if (labels.size() != 1) {
// TODO multiple tag index seek need the IndexScan support
VLOG(2) << "Multiple tag index seek is not supported now.";
return false;
}

auto whereClause = nodeCtx->bindWhereClause;
if (!whereClause || !whereClause->filter) return false;

auto qctx = nodeCtx->qctx;
auto newFilter = ExpressionUtils::rewriteInnerInExpr(whereClause->filter);
auto filter = newFilter;
// auto filter = MatchSolver::rewriteTagIndexFilter(
// labels.back(), nodeInfo.alias, newFilter, qctx, &nodeCtx->refVarName);

if (!filter) return false;

nodeCtx->scanInfo.filter = filter;
nodeCtx->scanInfo.schemaIds = nodeInfo.tids;
nodeCtx->scanInfo.schemaNames = nodeInfo.labels;

return true;
}

StatusOr<SubPlan> VariablePropIndexSeek::transformNode(NodeContext* nodeCtx) {
SubPlan plan;

const auto& schemaIds = nodeCtx->scanInfo.schemaIds;
DCHECK_EQ(schemaIds.size(), 1u) << "Not supported multiple tag properties seek.";

auto qctx = nodeCtx->qctx;
auto argument = Argument::make(qctx, nodeCtx->refVarName);
argument->setColNames({nodeCtx->refVarName});
using IQC = nebula::storage::cpp2::IndexQueryContext;
IQC iqctx;
iqctx.filter_ref() = Expression::encode(*nodeCtx->scanInfo.filter);
auto scan =
IndexScan::make(qctx, argument, nodeCtx->spaceId, {iqctx}, {kVid}, false, schemaIds.back());
scan->setColNames({kVid});
plan.tail = argument;
plan.root = scan;

// initialize start expression in project node
auto* pool = nodeCtx->qctx->objPool();
nodeCtx->initialExpr = VariablePropertyExpression::make(pool, "", kVid);
return plan;
}

} // namespace graph
} // namespace nebula
36 changes: 36 additions & 0 deletions src/graph/planner/match/VariablePropIndexSeek.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Copyright (c) 2023 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

#ifndef GRAPH_PLANNER_MATCH_VARIABLEPROPINDEXSEEK_H_
#define GRAPH_PLANNER_MATCH_VARIABLEPROPINDEXSEEK_H_

#include "graph/planner/match/StartVidFinder.h"

namespace nebula {
namespace graph {

// The VariablePropIndexSeek finds if a plan could get starting vids by tag
// props or edge props index.
class VariablePropIndexSeek final : public StartVidFinder {
public:
static std::unique_ptr<VariablePropIndexSeek> make() {
return std::unique_ptr<VariablePropIndexSeek>(new VariablePropIndexSeek());
}

bool matchNode(NodeContext* nodeCtx) override;

StatusOr<SubPlan> transformNode(NodeContext* nodeCtx) override;

const char* name() const override {
return "VariablePropIndexSeekFinder";
}

private:
VariablePropIndexSeek() = default;
};

} // namespace graph
} // namespace nebula
#endif // GRAPH_PLANNER_MATCH_PropIndexSeek_H_
Loading