Skip to content
This repository has been archived by the owner on Dec 1, 2022. It is now read-only.

Match by tag index. #485

Merged
merged 23 commits into from Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
22a0532
Match by tag index.
Shylock-Hg Dec 16, 2020
a589c68
Add the extend test cases.
Shylock-Hg Dec 16, 2020
d39f665
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 16, 2020
54dafe8
Merge branch 'feature/match-by-tag' of github.com:Shylock-Hg/nebula-g…
Shylock-Hg Dec 16, 2020
51cdb50
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 18, 2020
ff110be
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 18, 2020
8947e65
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 22, 2020
b477ae8
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 22, 2020
cbbb16d
Address @CPWstatic's comment that need check the index when match pat…
Shylock-Hg Dec 23, 2020
bb668fe
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 24, 2020
ea5caf0
Merge branch 'master' of github.com:vesoft-inc/nebula-graph into feat…
Shylock-Hg Dec 24, 2020
18b399d
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 28, 2020
f1e8920
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 28, 2020
44bd0c5
Rebase the tag filter.
Shylock-Hg Dec 28, 2020
2507a2f
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 29, 2020
8ca8030
Fix the memory leak.
Shylock-Hg Dec 29, 2020
1e0afb0
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 29, 2020
f5344de
Fix the format.
Shylock-Hg Dec 29, 2020
f68dbbe
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 29, 2020
d4d04c2
Merge branch 'master' into feature/match-by-tag
Shylock-Hg Dec 29, 2020
949676b
Merge branch 'master' into feature/match-by-tag
bright-starry-sky Dec 30, 2020
7aa893d
Rebase.
Shylock-Hg Dec 30, 2020
504563c
Merge branch 'master' into feature/match-by-tag
CPWstatic Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/context/ast/QueryAstContext.h
Expand Up @@ -60,6 +60,8 @@ struct ScanInfo {
Expression *filter{nullptr};
int32_t schemaId{0};
const std::string *schemaName{nullptr};
// use for seek by index itself
IndexID indexId{-1};
};

struct CypherClauseContextBase : AstContext {
Expand Down
1 change: 1 addition & 0 deletions src/planner/CMakeLists.txt
Expand Up @@ -34,4 +34,5 @@ nebula_add_library(
match/PropIndexSeek.cpp
match/VertexIdSeek.cpp
match/Expand.cpp
match/LabelIndexSeek.cpp
)
7 changes: 5 additions & 2 deletions src/planner/PlannersRegister.cpp
Expand Up @@ -12,6 +12,7 @@
#include "planner/match/StartVidFinder.h"
#include "planner/match/PropIndexSeek.h"
#include "planner/match/VertexIdSeek.h"
#include "planner/match/LabelIndexSeek.h"

namespace nebula {
namespace graph {
Expand Down Expand Up @@ -39,8 +40,10 @@ void PlannersRegister::registMatch() {
// MATCH(n:Tag) WHERE n.prop = value RETURN n
startVidFinders.emplace_back(&PropIndexSeek::make);

// MATCH(n:Tag) RETURN n;
// planners.emplace_back(&MatchTagScanPlanner::match, &MatchTagScanPlanner::make);
// seek by tag or edge(index)
// MATCH(n: tag) RETURN n
// MATCH(s)-[:edge]->(e) RETURN e
startVidFinders.emplace_back(&LabelIndexSeek::make);
}
} // namespace graph
} // namespace nebula
95 changes: 95 additions & 0 deletions src/planner/match/LabelIndexSeek.cpp
@@ -0,0 +1,95 @@
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#include "planner/match/LabelIndexSeek.h"
#include "planner/Query.h"
#include "planner/match/MatchSolver.h"
#include "util/ExpressionUtils.h"

namespace nebula {
namespace graph {

bool LabelIndexSeek::matchNode(NodeContext* nodeCtx) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check if there exists index for such a tag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed.

auto& node = *nodeCtx->info;
// only require the tag
if (node.tid <= 0) {
return false;
}

nodeCtx->scanInfo.schemaId = node.tid;
nodeCtx->scanInfo.schemaName = node.label;

auto indexResult = pickTagIndex(nodeCtx);
if (!indexResult.ok()) {
return false;
}

nodeCtx->scanInfo.indexId = indexResult.value();

return true;
}

bool LabelIndexSeek::matchEdge(EdgeContext*) {
// TODO
return false;
}

StatusOr<SubPlan> LabelIndexSeek::transformNode(NodeContext* nodeCtx) {
SubPlan plan;
auto* matchClauseCtx = nodeCtx->matchClauseCtx;
using IQC = nebula::storage::cpp2::IndexQueryContext;
IQC iqctx;
iqctx.set_index_id(nodeCtx->scanInfo.indexId);
auto contexts = std::make_unique<std::vector<IQC>>();
contexts->emplace_back(std::move(iqctx));
auto columns = std::make_unique<std::vector<std::string>>();
columns->emplace_back(kVid);
auto scan = IndexScan::make(matchClauseCtx->qctx,
nullptr,
matchClauseCtx->space.id,
std::move(contexts),
std::move(columns),
false,
nodeCtx->scanInfo.schemaId);
scan->setColNames({kVid});
plan.tail = scan;
plan.root = scan;

// initialize start expression in project node
nodeCtx->initialExpr.reset(ExpressionUtils::newVarPropExpr(kVid));
return plan;
}

StatusOr<SubPlan> LabelIndexSeek::transformEdge(EdgeContext*) {
// TODO
return Status::Error("TODO");
}

/*static*/ StatusOr<IndexID> LabelIndexSeek::pickTagIndex(const NodeContext* nodeCtx) {
auto tagId = nodeCtx->scanInfo.schemaId;
const auto* qctx = nodeCtx->matchClauseCtx->qctx;
auto tagIndexesResult = qctx->indexMng()->getTagIndexes(nodeCtx->matchClauseCtx->space.id);
NG_RETURN_IF_ERROR(tagIndexesResult);
auto tagIndexes = std::move(tagIndexesResult).value();
std::shared_ptr<meta::cpp2::IndexItem> candidateIndex{nullptr};
for (const auto& index : tagIndexes) {
if (index->get_schema_id().get_tag_id() == tagId) {
if (candidateIndex == nullptr) {
candidateIndex = index;
} else {
candidateIndex = selectIndex(candidateIndex, index);
}
}
}
if (candidateIndex == nullptr) {
return Status::SemanticError("No valid index for label `%s'.",
nodeCtx->scanInfo.schemaName->c_str());
}
return candidateIndex->get_index_id();
}

} // namespace graph
} // namespace nebula
51 changes: 51 additions & 0 deletions src/planner/match/LabelIndexSeek.h
@@ -0,0 +1,51 @@
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#ifndef PLANNER_MATCH_LABELINDEXSEEK_H_
#define PLANNER_MATCH_LABELINDEXSEEK_H_

#include "planner/match/StartVidFinder.h"

namespace nebula {
namespace graph {

/*
* The LabelIndexSeek was designed to find if could get the starting vids by tag index.
*/
class LabelIndexSeek final : public StartVidFinder {
public:
static std::unique_ptr<LabelIndexSeek> make() {
return std::unique_ptr<LabelIndexSeek>(new LabelIndexSeek());
}

private:
LabelIndexSeek() = default;

bool matchNode(NodeContext* nodeCtx) override;

bool matchEdge(EdgeContext* edgeCtx) override;

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

StatusOr<SubPlan> transformEdge(EdgeContext* edgeCtx) override;

static StatusOr<IndexID> pickTagIndex(const NodeContext* nodeCtx);

static std::shared_ptr<meta::cpp2::IndexItem> selectIndex(
const std::shared_ptr<meta::cpp2::IndexItem> candidate,
const std::shared_ptr<meta::cpp2::IndexItem> income) {
// less fields is better
// TODO(shylock) rank for field itself(e.g. INT is better than STRING)
if (candidate->get_fields().size() > income->get_fields().size()) {
return income;
}
return candidate;
}
};

} // namespace graph
} // namespace nebula
#endif // PLANNER_MATCH_LABELINDEXSEEK_H_
3 changes: 1 addition & 2 deletions src/planner/match/MatchClausePlanner.cpp
Expand Up @@ -269,7 +269,7 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter,

PlanNode* root = gv;
if (nodeFilter != nullptr) {
auto filter = nodeFilter->clone().release();
auto filter = qctx->objPool()->add(nodeFilter->clone().release());
RewriteMatchLabelVisitor visitor(
[](const Expression* expr) -> Expression *{
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute ||
Expand All @@ -284,7 +284,6 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter,
return new VertexExpression();
});
filter->accept(&visitor);
qctx->objPool()->add(filter);
root = Filter::make(qctx, root, filter);
}

Expand Down
19 changes: 0 additions & 19 deletions src/planner/match/TagIndexSeek.h

This file was deleted.

2 changes: 2 additions & 0 deletions src/validator/test/CMakeLists.txt
Expand Up @@ -6,6 +6,7 @@
nebula_add_library(
mock_schema_obj OBJECT
MockSchemaManager.cpp
MockIndexManager.cpp
)

set(VALIDATOR_TEST_LIBS
Expand Down Expand Up @@ -68,6 +69,7 @@ nebula_add_test(
ValidatorTestBase.cpp
ExplainValidatorTest.cpp
GroupByValidatorTest.cpp
MatchValidatorTest.cpp
LookupValidatorTest.cpp
SymbolsTest.cpp
OBJECTS
Expand Down
80 changes: 80 additions & 0 deletions src/validator/test/MatchValidatorTest.cpp
@@ -0,0 +1,80 @@
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#include "validator/MatchValidator.h"

#include "validator/test/ValidatorTestBase.h"

namespace nebula {
namespace graph {

class MatchValidatorTest : public ValidatorTestBase {};

TEST_F(MatchValidatorTest, SeekByTagIndex) {
// empty properties index
{
std::string query = "MATCH (v:person) RETURN id(v) AS id;";
std::vector<PlanNode::Kind> expected = {PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kProject,
PlanNode::Kind::kProject,
// TODO this tag filter could remove in this case
PlanNode::Kind::kFilter,
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kDedup,
PlanNode::Kind::kProject,
PlanNode::Kind::kIndexScan,
PlanNode::Kind::kStart};
EXPECT_TRUE(checkResult(query, expected));
}
// non empty properties index
{
std::string query = "MATCH (v:book) RETURN id(v) AS id;";
std::vector<PlanNode::Kind> expected = {PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kProject,
PlanNode::Kind::kProject,
// TODO this tag filter could remove in this case
PlanNode::Kind::kFilter,
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kDedup,
PlanNode::Kind::kProject,
PlanNode::Kind::kIndexScan,
PlanNode::Kind::kStart};
EXPECT_TRUE(checkResult(query, expected));
}
// non empty properties index with extend
{
std::string query = "MATCH (p:person)-[:like]->(b:book) RETURN b.name AS book;";
std::vector<PlanNode::Kind> expected = {PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kProject,
PlanNode::Kind::kDataJoin,
PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kGetVertices,
PlanNode::Kind::kDedup,
PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kPassThrough,
PlanNode::Kind::kProject,
PlanNode::Kind::kFilter,
PlanNode::Kind::kGetNeighbors,
PlanNode::Kind::kDedup,
PlanNode::Kind::kProject,
PlanNode::Kind::kIndexScan,
PlanNode::Kind::kStart};
EXPECT_TRUE(checkResult(query, expected));
}
// non index
{
std::string query = "MATCH (v:room) RETURN id(v) AS id;";
EXPECT_FALSE(validate(query));
}
}

} // namespace graph
} // namespace nebula
52 changes: 52 additions & 0 deletions src/validator/test/MockIndexManager.cpp
@@ -0,0 +1,52 @@
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#include "validator/test/MockIndexManager.h"
#include <memory>
#include <vector>

// tag index:
// person()
// book(name(32))

namespace nebula {
namespace graph {

void MockIndexManager::init() {
// index related
meta::cpp2::IndexItem person_no_props_index;
person_no_props_index.set_index_id(233);
person_no_props_index.set_index_name("person_no_props_index");
meta::cpp2::SchemaID personSchemaId;
personSchemaId.set_tag_id(2);
person_no_props_index.set_schema_id(std::move(personSchemaId));
person_no_props_index.set_schema_name("person");

meta::cpp2::IndexItem book_name_index;
book_name_index.set_index_id(234);
book_name_index.set_index_name("book_name_index");
meta::cpp2::SchemaID bookSchemaId;
bookSchemaId.set_tag_id(5);
book_name_index.set_schema_id(std::move(bookSchemaId));
book_name_index.set_schema_name("book");
meta::cpp2::ColumnDef field;
field.set_name("name");
meta::cpp2::ColumnTypeDef type;
type.set_type(meta::cpp2::PropertyType::FIXED_STRING);
type.set_type_length(32);
field.set_type(std::move(type));
book_name_index.set_fields({});
book_name_index.fields.emplace_back(std::move(field));

tagIndexes_.emplace(1, std::vector<std::shared_ptr<meta::cpp2::IndexItem>>{});
tagIndexes_[1].emplace_back(
std::make_shared<meta::cpp2::IndexItem>(std::move(person_no_props_index)));
tagIndexes_[1].emplace_back(
std::make_shared<meta::cpp2::IndexItem>(std::move(book_name_index)));
}

} // namespace graph
} // namespace nebula