diff --git a/src/parser/Clauses.h b/src/parser/Clauses.h index a76c5170a151fa5a42c301ba1ec5b8c76fc083e0..c4a47f2d7e96af2ece61a75a4ceaf69ed9ff8bc3 100644 --- a/src/parser/Clauses.h +++ b/src/parser/Clauses.h @@ -313,6 +313,14 @@ public: std::string toString() const; + const YieldColumn* back() const { + return columns_.back().get(); + } + + YieldColumn* back() { + return columns_.back().get(); + } + private: std::vector<std::unique_ptr<YieldColumn>> columns_; }; diff --git a/src/validator/CMakeLists.txt b/src/validator/CMakeLists.txt index 3baa16bd11814eb6507698ed7435653c63d0b427..ef915f1da5fe02c2e22d8df3baa349750f4e5d1a 100644 --- a/src/validator/CMakeLists.txt +++ b/src/validator/CMakeLists.txt @@ -28,7 +28,7 @@ nebula_add_library( ExplainValidator.cpp GroupByValidator.cpp FindPathValidator.cpp - IndexScanValidator.cpp + LookupValidator.cpp MatchValidator.cpp ) diff --git a/src/validator/IndexScanValidator.cpp b/src/validator/LookupValidator.cpp similarity index 62% rename from src/validator/IndexScanValidator.cpp rename to src/validator/LookupValidator.cpp index 42ea9877dae7e5822adb2a57ec00f88ae9c34c1e..19e1f865b40569f519f3eea17e0e7500fe580851 100644 --- a/src/validator/IndexScanValidator.cpp +++ b/src/validator/LookupValidator.cpp @@ -4,7 +4,7 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#include "validator/IndexScanValidator.h" +#include "validator/LookupValidator.h" #include "planner/Query.h" #include "util/ExpressionUtils.h" #include "util/SchemaUtil.h" @@ -14,21 +14,21 @@ DECLARE_uint32(ft_request_retry_times); namespace nebula { namespace graph { -/*static*/ constexpr char IndexScanValidator::kSrcVID[]; -/*static*/ constexpr char IndexScanValidator::kDstVID[]; -/*static*/ constexpr char IndexScanValidator::kRanking[]; +/*static*/ constexpr char LookupValidator::kSrcVID[]; +/*static*/ constexpr char LookupValidator::kDstVID[]; +/*static*/ constexpr char LookupValidator::kRanking[]; -/*static*/ constexpr char IndexScanValidator::kVertexID[]; +/*static*/ constexpr char LookupValidator::kVertexID[]; -Status IndexScanValidator::validateImpl() { +Status LookupValidator::validateImpl() { NG_RETURN_IF_ERROR(prepareFrom()); NG_RETURN_IF_ERROR(prepareYield()); NG_RETURN_IF_ERROR(prepareFilter()); return Status::OK(); } -Status IndexScanValidator::toPlan() { - auto *is = IndexScan::make(qctx_, +Status LookupValidator::toPlan() { + auto* is = IndexScan::make(qctx_, nullptr, spaceId_, std::move(contexts_), @@ -36,14 +36,33 @@ Status IndexScanValidator::toPlan() { isEdge_, schemaId_, isEmptyResultSet_); - is->setColNames(std::move(colNames_)); - root_ = is; + is->setColNames(std::move(idxScanColNames_)); + PlanNode* current = is; + + if (withProject_) { + auto* projectNode = Project::make(qctx_, current, newYieldColumns_); + projectNode->setInputVar(current->outputVar()); + projectNode->setColNames(colNames_); + current = projectNode; + } + + if (dedup_) { + auto* dedupNode = Dedup::make(qctx_, current); + dedupNode->setInputVar(current->outputVar()); + dedupNode->setColNames(colNames_); + current = dedupNode; + + // the framework will add data collect to collect the result + // if the result is required + } + + root_ = current; tail_ = is; return Status::OK(); } -Status IndexScanValidator::prepareFrom() { - auto *sentence = static_cast<const LookupSentence *>(sentence_); +Status LookupValidator::prepareFrom() { + auto* sentence = static_cast<const LookupSentence*>(sentence_); spaceId_ = vctx_->whichSpace().id; from_ = *sentence->from(); auto ret = qctx_->schemaMng()->getSchemaIDByName(spaceId_, from_); @@ -55,72 +74,100 @@ Status IndexScanValidator::prepareFrom() { return Status::OK(); } -Status IndexScanValidator::prepareYield() { - auto *sentence = static_cast<const LookupSentence *>(sentence_); +Status LookupValidator::prepareYield() { + auto* sentence = static_cast<const LookupSentence*>(sentence_); returnCols_ = std::make_unique<std::vector<std::string>>(); // always return if (isEdge_) { returnCols_->emplace_back(kSrc); - colNames_.emplace_back(kSrcVID); + idxScanColNames_.emplace_back(kSrcVID); + colNames_.emplace_back(idxScanColNames_.back()); + outputs_.emplace_back(colNames_.back(), Value::Type::STRING); returnCols_->emplace_back(kDst); - colNames_.emplace_back(kDstVID); + idxScanColNames_.emplace_back(kDstVID); + colNames_.emplace_back(idxScanColNames_.back()); + outputs_.emplace_back(colNames_.back(), Value::Type::STRING); returnCols_->emplace_back(kRank); - colNames_.emplace_back(kRanking); + idxScanColNames_.emplace_back(kRanking); + colNames_.emplace_back(idxScanColNames_.back()); + outputs_.emplace_back(colNames_.back(), Value::Type::INT); } else { returnCols_->emplace_back(kVid); - colNames_.emplace_back(kVertexID); + idxScanColNames_.emplace_back(kVertexID); + colNames_.emplace_back(idxScanColNames_.back()); + outputs_.emplace_back(colNames_.back(), Value::Type::STRING); } if (sentence->yieldClause() == nullptr) { return Status::OK(); } - // When whereClause is nullptr, yieldClause is not nullptr, - // only return vid for tag. return src, ranking, dst for edge - if (sentence->whereClause() == nullptr) { - return Status::SemanticError("Yield clauses are not supported " - "when WHERE clause does not exist"); + withProject_ = true; + if (sentence->yieldClause()->isDistinct()) { + dedup_ = true; + } + newYieldColumns_ = qctx_->objPool()->makeAndAdd<YieldColumns>(); + if (isEdge_) { + // default columns + newYieldColumns_->addColumn(new YieldColumn( + new InputPropertyExpression(new std::string(kSrcVID)), new std::string(kSrcVID))); + newYieldColumns_->addColumn(new YieldColumn( + new InputPropertyExpression(new std::string(kDstVID)), new std::string(kDstVID))); + newYieldColumns_->addColumn(new YieldColumn( + new InputPropertyExpression(new std::string(kRanking)), new std::string(kRanking))); + } else { + newYieldColumns_->addColumn(new YieldColumn( + new InputPropertyExpression(new std::string(kVertexID)), new std::string(kVertexID))); } auto columns = sentence->yieldClause()->columns(); - auto schema = isEdge_ - ? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_) - : qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_); + auto schema = isEdge_ ? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_) + : qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_); if (schema == nullptr) { - return isEdge_ - ? Status::EdgeNotFound("Edge schema not found : %s", from_.c_str()) - : Status::TagNotFound("Tag schema not found : %s", from_.c_str()); + return isEdge_ ? Status::EdgeNotFound("Edge schema not found : %s", from_.c_str()) + : Status::TagNotFound("Tag schema not found : %s", from_.c_str()); } for (auto col : columns) { - std::string schemaName, colName; + // TODO(shylock) support more expr if (col->expr()->kind() == Expression::Kind::kLabelAttribute) { - auto la = static_cast<LabelAttributeExpression *>(col->expr()); - schemaName = *la->left()->name(); - const auto &value = la->right()->value(); - colName = value.getStr(); + auto la = static_cast<LabelAttributeExpression*>(col->expr()); + const std::string &schemaName = *la->left()->name(); + const auto& value = la->right()->value(); + const std::string &colName = value.getStr(); + if (isEdge_) { + newYieldColumns_->addColumn(new YieldColumn(new EdgePropertyExpression( + new std::string(schemaName), new std::string(colName)))); + } else { + newYieldColumns_->addColumn(new YieldColumn(new TagPropertyExpression( + new std::string(schemaName), new std::string(colName)))); + } + if (col->alias() != nullptr) { + newYieldColumns_->back()->setAlias(new std::string(*col->alias())); + } + if (schemaName != from_) { + return Status::SemanticError("Schema name error : %s", schemaName.c_str()); + } + auto ret = schema->getFieldType(colName); + if (ret == meta::cpp2::PropertyType::UNKNOWN) { + return Status::SemanticError( + "Column %s not found in schema %s", colName.c_str(), from_.c_str()); + } + returnCols_->emplace_back(colName); + idxScanColNames_.emplace_back(from_ + "." + colName); + colNames_.emplace_back(deduceColName(newYieldColumns_->back())); + outputs_.emplace_back(colNames_.back(), SchemaUtil::propTypeToValueType(ret)); } else { return Status::SemanticError("Yield clauses are not supported : %s", col->expr()->toString().c_str()); } - - if (schemaName != from_) { - return Status::SemanticError("Schema name error : %s", schemaName.c_str()); - } - auto ret = schema->getFieldType(colName); - if (ret == meta::cpp2::PropertyType::UNKNOWN) { - return Status::SemanticError("Column %s not found in schema %s", - colName.c_str(), from_.c_str()); - } - returnCols_->emplace_back(colName); - colNames_.emplace_back(from_ + "." + colName); } return Status::OK(); } -Status IndexScanValidator::prepareFilter() { - auto *sentence = static_cast<const LookupSentence *>(sentence_); +Status LookupValidator::prepareFilter() { + auto* sentence = static_cast<const LookupSentence*>(sentence_); if (sentence->whereClause() == nullptr) { return Status::OK(); } - auto *filter = sentence->whereClause()->filter(); + auto* filter = sentence->whereClause()->filter(); storage::cpp2::IndexQueryContext ctx; if (needTextSearch(filter)) { NG_RETURN_IF_ERROR(checkTSService()); @@ -146,8 +193,7 @@ Status IndexScanValidator::prepareFilter() { return Status::OK(); } -StatusOr<std::string> -IndexScanValidator::rewriteTSFilter(Expression* expr) { +StatusOr<std::string> LookupValidator::rewriteTSFilter(Expression* expr) { std::vector<std::string> values; auto tsExpr = static_cast<TextSearchExpression*>(expr); auto vRet = textSearch(tsExpr); @@ -183,7 +229,7 @@ IndexScanValidator::rewriteTSFilter(Expression* expr) { return newExpr->encode(); } -StatusOr<std::vector<std::string>> IndexScanValidator::textSearch(TextSearchExpression* expr) { +StatusOr<std::vector<std::string>> LookupValidator::textSearch(TextSearchExpression* expr) { if (*expr->arg()->from() != from_) { return Status::SemanticError("Schema name error : %s", expr->arg()->from()->c_str()); } @@ -204,33 +250,23 @@ StatusOr<std::vector<std::string>> IndexScanValidator::textSearch(TextSearchExpr fuzz = expr->arg()->fuzziness(); } std::string op = (expr->arg()->op() == nullptr) ? "or" : *expr->arg()->op(); - ret = nebula::plugin::ESGraphAdapter::kAdapter->fuzzy(randomFTClient(), - doc, - limit, - fuzz, - op, - result); + ret = nebula::plugin::ESGraphAdapter::kAdapter->fuzzy( + randomFTClient(), doc, limit, fuzz, op, result); break; } case Expression::Kind::kTSPrefix: { - ret = nebula::plugin::ESGraphAdapter::kAdapter->prefix(randomFTClient(), - doc, - limit, - result); + ret = nebula::plugin::ESGraphAdapter::kAdapter->prefix( + randomFTClient(), doc, limit, result); break; } case Expression::Kind::kTSRegexp: { - ret = nebula::plugin::ESGraphAdapter::kAdapter->regexp(randomFTClient(), - doc, - limit, - result); + ret = nebula::plugin::ESGraphAdapter::kAdapter->regexp( + randomFTClient(), doc, limit, result); break; } case Expression::Kind::kTSWildcard: { - ret = nebula::plugin::ESGraphAdapter::kAdapter->wildcard(randomFTClient(), - doc, - limit, - result); + ret = nebula::plugin::ESGraphAdapter::kAdapter->wildcard( + randomFTClient(), doc, limit, result); break; } default: @@ -248,7 +284,7 @@ StatusOr<std::vector<std::string>> IndexScanValidator::textSearch(TextSearchExpr return Status::Error("scan external index failed"); } -bool IndexScanValidator::needTextSearch(Expression* expr) { +bool LookupValidator::needTextSearch(Expression* expr) { switch (expr->kind()) { case Expression::Kind::kTSFuzzy: case Expression::Kind::kTSPrefix: @@ -261,12 +297,12 @@ bool IndexScanValidator::needTextSearch(Expression* expr) { } } -Status IndexScanValidator::checkFilter(Expression* expr) { +Status LookupValidator::checkFilter(Expression* expr) { // TODO (sky) : Rewrite simple expressions, // for example rewrite expr from col1 > 1 + 2 to col > 3 switch (expr->kind()) { - case Expression::Kind::kLogicalOr : - case Expression::Kind::kLogicalAnd : { + case Expression::Kind::kLogicalOr: + case Expression::Kind::kLogicalAnd: { // TODO(dutor) Deal with n-ary operands auto lExpr = static_cast<LogicalExpression*>(expr); auto ret = checkFilter(lExpr->operand(0)); @@ -292,43 +328,37 @@ Status IndexScanValidator::checkFilter(Expression* expr) { return Status::OK(); } -Status IndexScanValidator::checkRelExpr(RelationalExpression* expr) { +Status LookupValidator::checkRelExpr(RelationalExpression* expr) { auto* left = expr->left(); auto* right = expr->right(); // Does not support filter : schema.col1 > schema.col2 if (left->kind() == Expression::Kind::kLabelAttribute && right->kind() == Expression::Kind::kLabelAttribute) { - return Status::NotSupported("Expression %s not supported yet", - expr->toString().c_str()); + return Status::NotSupported("Expression %s not supported yet", expr->toString().c_str()); } else if (left->kind() == Expression::Kind::kLabelAttribute || right->kind() == Expression::Kind::kLabelAttribute) { auto ret = rewriteRelExpr(expr); NG_RETURN_IF_ERROR(ret); } else { - return Status::NotSupported("Expression %s not supported yet", - expr->toString().c_str()); + return Status::NotSupported("Expression %s not supported yet", expr->toString().c_str()); } return Status::OK(); } -Status IndexScanValidator::rewriteRelExpr(RelationalExpression* expr) { +Status LookupValidator::rewriteRelExpr(RelationalExpression* expr) { auto* left = expr->left(); auto* right = expr->right(); auto leftIsAE = left->kind() == Expression::Kind::kLabelAttribute; - auto* la = leftIsAE - ? static_cast<LabelAttributeExpression *>(left) - : static_cast<LabelAttributeExpression *>(right); + auto* la = leftIsAE ? static_cast<LabelAttributeExpression*>(left) + : static_cast<LabelAttributeExpression*>(right); if (*la->left()->name() != from_) { - return Status::SemanticError("Schema name error : %s", - la->left()->name()->c_str()); + return Status::SemanticError("Schema name error : %s", la->left()->name()->c_str()); } std::string prop = la->right()->value().getStr(); // rewrite ConstantExpression - auto c = leftIsAE - ? checkConstExpr(right, prop) - : checkConstExpr(left, prop); + auto c = leftIsAE ? checkConstExpr(right, prop) : checkConstExpr(left, prop); if (!c.ok()) { return Status::SemanticError("expression error : %s", left->toString().c_str()); @@ -357,11 +387,9 @@ Status IndexScanValidator::rewriteRelExpr(RelationalExpression* expr) { return Status::OK(); } -StatusOr<Value> IndexScanValidator::checkConstExpr(Expression* expr, - const std::string& prop) { - auto schema = isEdge_ - ? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_) - : qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_); +StatusOr<Value> LookupValidator::checkConstExpr(Expression* expr, const std::string& prop) { + auto schema = isEdge_ ? qctx_->schemaMng()->getEdgeSchema(spaceId_, schemaId_) + : qctx_->schemaMng()->getTagSchema(spaceId_, schemaId_); auto type = schema->getFieldType(prop); QueryExpressionContext dummy(nullptr); auto v = Expression::eval(expr, dummy); @@ -371,7 +399,7 @@ StatusOr<Value> IndexScanValidator::checkConstExpr(Expression* expr, return v; } -Status IndexScanValidator::checkTSService() { +Status LookupValidator::checkTSService() { auto tcs = qctx_->getMetaClient()->getFTClientsFromCache(); if (!tcs.ok()) { return tcs.status(); @@ -392,7 +420,7 @@ Status IndexScanValidator::checkTSService() { return checkTSIndex(); } -Status IndexScanValidator::checkTSIndex() { +Status LookupValidator::checkTSIndex() { auto ftIndex = nebula::plugin::IndexTraits::indexName(space_.name, isEdge_); auto retryCnt = FLAGS_ft_request_retry_times; StatusOr<bool> ret = Status::Error("fulltext index not found : %s", ftIndex.c_str()); @@ -409,9 +437,9 @@ Status IndexScanValidator::checkTSIndex() { return ret.status(); } -const nebula::plugin::HttpClient& IndexScanValidator::randomFTClient() const { +const nebula::plugin::HttpClient& LookupValidator::randomFTClient() const { auto i = folly::Random::rand32(esClients_.size() - 1); return esClients_[i]; } -} // namespace graph -} // namespace nebula +} // namespace graph +} // namespace nebula diff --git a/src/validator/IndexScanValidator.h b/src/validator/LookupValidator.h similarity index 86% rename from src/validator/IndexScanValidator.h rename to src/validator/LookupValidator.h index c26e0531b13a9ec62cfe916f065a0cf574696bbb..0699455327e21a6bc87b8c9e3530817ff2c11751 100644 --- a/src/validator/IndexScanValidator.h +++ b/src/validator/LookupValidator.h @@ -16,9 +16,9 @@ namespace nebula { namespace graph { -class IndexScanValidator final : public Validator { +class LookupValidator final : public Validator { public: - IndexScanValidator(Sentence* sentence, QueryContext* context) + LookupValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} private: @@ -68,7 +68,11 @@ private: bool textSearchReady_{false}; std::string from_; std::vector<nebula::plugin::HttpClient> esClients_; + std::vector<std::string> idxScanColNames_; std::vector<std::string> colNames_; + bool withProject_{false}; + bool dedup_{false}; + YieldColumns *newYieldColumns_{nullptr}; }; } // namespace graph diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index 63aae3fae8a02aa8f74886830d625436b648a9b7..6976f1be1cf28fdf6ddba42a3daf12e2a7c6c927 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -38,7 +38,7 @@ #include "validator/GroupByValidator.h" #include "validator/MatchValidator.h" #include "visitor/EvaluableExprVisitor.h" -#include "validator/IndexScanValidator.h" +#include "validator/LookupValidator.h" namespace nebula { namespace graph { @@ -194,7 +194,7 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon case Sentence::Kind::kDropEdgeIndex: return std::make_unique<DropEdgeIndexValidator>(sentence, context); case Sentence::Kind::kLookup: - return std::make_unique<IndexScanValidator>(sentence, context); + return std::make_unique<LookupValidator>(sentence, context); case Sentence::Kind::kAddGroup: return std::make_unique<AddGroupValidator>(sentence, context); case Sentence::Kind::kDropGroup: diff --git a/src/validator/test/CMakeLists.txt b/src/validator/test/CMakeLists.txt index 49b3fe95869ffe6357a116ca478c8e89e8ea966f..cb410652516bb3fa6a1c210622429064e7864cce 100644 --- a/src/validator/test/CMakeLists.txt +++ b/src/validator/test/CMakeLists.txt @@ -69,6 +69,7 @@ nebula_add_test( ValidatorTestBase.cpp ExplainValidatorTest.cpp GroupByValidatorTest.cpp + LookupValidatorTest.cpp SymbolsTest.cpp OBJECTS ${VALIDATOR_TEST_LIBS} diff --git a/src/validator/test/LookupValidatorTest.cpp b/src/validator/test/LookupValidatorTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83fcc80cc08ad992441d3399a6b65b30a3d92658 --- /dev/null +++ b/src/validator/test/LookupValidatorTest.cpp @@ -0,0 +1,84 @@ +/* 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 "common/base/ObjectPool.h" +#include "planner/Logic.h" +#include "planner/Query.h" +#include "validator/LookupValidator.h" +#include "validator/test/ValidatorTestBase.h" + +namespace nebula { +namespace graph { + +class LookupValidatorTest : public ValidatorTestBase {}; + +TEST_F(LookupValidatorTest, InputOutput) { + // pipe + { + const std::string query = "LOOKUP ON person where person.age == 35 | " + "FETCH PROP ON person $-.VertexID"; + EXPECT_TRUE(checkResult(query, + { + PlanNode::Kind::kGetVertices, + PlanNode::Kind::kIndexScan, + PlanNode::Kind::kStart, + })); + } + // pipe with yield + { + const std::string query = + "LOOKUP ON person where person.age == 35 YIELD person.name AS name | " + "FETCH PROP ON person $-.name"; + EXPECT_TRUE(checkResult(query, + { + PlanNode::Kind::kGetVertices, + PlanNode::Kind::kProject, + PlanNode::Kind::kIndexScan, + PlanNode::Kind::kStart, + })); + } + // variable + { + const std::string query = "$a = LOOKUP ON person where person.age == 35; " + "FETCH PROP ON person $a.VertexID"; + EXPECT_TRUE(checkResult(query, + { + PlanNode::Kind::kGetVertices, + PlanNode::Kind::kIndexScan, + PlanNode::Kind::kStart, + })); + } + // var with yield + { + const std::string query = + "$a = LOOKUP ON person where person.age == 35 YIELD person.name AS name;" + "FETCH PROP ON person $a.name"; + EXPECT_TRUE(checkResult(query, + { + PlanNode::Kind::kGetVertices, + PlanNode::Kind::kProject, + PlanNode::Kind::kIndexScan, + PlanNode::Kind::kStart, + })); + } +} + +TEST_F(LookupValidatorTest, InvalidYieldExpression) { + // TODO(shylock) + { + const std::string query = + "LOOKUP ON person where person.age == 35 YIELD person.age + 1 AS age;"; + EXPECT_FALSE(checkResult(query, + { + PlanNode::Kind::kProject, + PlanNode::Kind::kIndexScan, + PlanNode::Kind::kStart, + })); + } +} + +} // namespace graph +} // namespace nebula diff --git a/tests/data/nba/config.yaml b/tests/data/nba/config.yaml index a509970220035466165f24f69b2b79fde8470d50..fbea950c3cca2163f27449d150369aaada6640b6 100644 --- a/tests/data/nba/config.yaml +++ b/tests/data/nba/config.yaml @@ -15,6 +15,7 @@ schema: | CREATE TAG INDEX IF NOT EXISTS player_name_index ON player(name(64)); CREATE TAG INDEX IF NOT EXISTS player_age_index ON player(age); CREATE TAG INDEX IF NOT EXISTS team_name_index ON team(name(64)); + CREATE EDGE INDEX IF NOT EXISTS serve_start_end_index ON serve(start_year, end_year); files: - path: ./player.csv withHeader: true diff --git a/tests/data/nba_int_vid/config.yaml b/tests/data/nba_int_vid/config.yaml index 939869b6066df0c67f995e90d397733e53441113..45611b4e4d0270a9e3a8c2d7fc12c1e714386a87 100644 --- a/tests/data/nba_int_vid/config.yaml +++ b/tests/data/nba_int_vid/config.yaml @@ -15,6 +15,7 @@ schema: | CREATE TAG INDEX IF NOT EXISTS player_name_index ON player(name(64)); CREATE TAG INDEX IF NOT EXISTS player_age_index ON player(age); CREATE TAG INDEX IF NOT EXISTS team_name_index ON team(name(64)); + CREATE EDGE INDEX IF NOT EXISTS serve_start_end_index ON serve(start_year, end_year); files: - path: ../nba/player.csv withHeader: true diff --git a/tests/query/stateless/test_lookup.py b/tests/query/stateless/test_lookup.py index 334b17d7616cc53b98cb1710272ee3c44603ee72..16a1e8437d6b598c71c59dcb03cb8403fad31d04 100644 --- a/tests/query/stateless/test_lookup.py +++ b/tests/query/stateless/test_lookup.py @@ -17,7 +17,6 @@ class TestIndex(NebulaTestSuite): replica_factor=self.replica_factor)) self.check_resp_succeeded(resp) - def test_edge_index(self): time.sleep(self.delay) resp = self.execute('USE nbaLookup') self.check_resp_succeeded(resp) @@ -60,30 +59,6 @@ class TestIndex(NebulaTestSuite): resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "121" -> "201":(1999, 2018)') self.check_resp_succeeded(resp) - resp = self.execute('LOOKUP ON serve where serve.start_year > 0') - self.check_resp_succeeded(resp) - assert resp.row_size() == 6 - - resp = self.execute('LOOKUP ON serve where serve.start_year > 1997 and serve.end_year < 2020') - self.check_resp_succeeded(resp) - assert resp.row_size() == 3 - - resp = self.execute('LOOKUP ON serve where serve.start_year > 2000 and serve.end_year < 2020') - self.check_resp_succeeded(resp) - self.check_empty_result(resp) - - resp = self.execute('LOOKUP ON like where like.likeness > 89') - self.check_resp_succeeded(resp) - assert resp.row_size() == 3 - - resp = self.execute('LOOKUP ON like where like.likeness < 39') - self.check_resp_succeeded(resp) - self.check_empty_result(resp) - - def test_tag_index(self): - time.sleep(self.delay) - resp = self.execute('USE nbaLookup') - self.check_resp_succeeded(resp) resp = self.execute("CREATE TAG player (name FIXED_STRING(30), age INT)") self.check_resp_succeeded(resp) resp = self.execute("CREATE TAG team (name FIXED_STRING(30))") @@ -127,33 +102,113 @@ class TestIndex(NebulaTestSuite): resp = self.execute('INSERT VERTEX team(name) VALUES "204":("opl")') self.check_resp_succeeded(resp) + + def test_edge_index(self): + resp = self.execute('LOOKUP ON serve where serve.start_year > 0') + self.check_resp_succeeded(resp) + col_names = ['SrcVID', 'DstVID', 'Ranking'] + self.check_column_names(resp, col_names) + expected_result = [['100', '200', 0], + ['101', '201', 0], + ['102', '202', 0], + ['103', '203', 0], + ['105', '204', 0], + ['121', '201', 0]] + self.check_out_of_order_result(resp, expected_result) + + resp = self.execute('LOOKUP ON serve where serve.start_year > 1997 and serve.end_year < 2020') + self.check_resp_succeeded(resp) + col_names = ['SrcVID', 'DstVID', 'Ranking'] + self.check_column_names(resp, col_names) + expected_result = [['101', '201', 0], + ['103', '203', 0], + ['121', '201', 0]] + self.check_out_of_order_result(resp, expected_result) + + resp = self.execute('LOOKUP ON serve where serve.start_year > 2000 and serve.end_year < 2020') + self.check_resp_succeeded(resp) + col_names = ['SrcVID', 'DstVID', 'Ranking'] + self.check_column_names(resp, col_names) + self.check_empty_result(resp) + + resp = self.execute('LOOKUP ON like where like.likeness > 89') + self.check_resp_succeeded(resp) + col_names = ['SrcVID', 'DstVID', 'Ranking'] + self.check_column_names(resp, col_names) + expected_result = [['100', '101', 0], + ['101', '102', 0], + ['105', '106', 0]] + self.check_out_of_order_result(resp, expected_result) + + resp = self.execute('LOOKUP ON like where like.likeness < 39') + self.check_resp_succeeded(resp) + col_names = ['SrcVID', 'DstVID', 'Ranking'] + self.check_column_names(resp, col_names) + self.check_empty_result(resp) + + def test_tag_index(self): resp = self.execute('LOOKUP ON player where player.age == 35') self.check_resp_succeeded(resp) - assert resp.row_size() == 1 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['103']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON player where player.age > 0') self.check_resp_succeeded(resp) - assert resp.row_size() == 8 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['100'], + ['101'], + ['102'], + ['103'], + ['104'], + ['105'], + ['106'], + ['121']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON player where player.age < 100') self.check_resp_succeeded(resp) - assert resp.row_size() == 8 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['100'], + ['101'], + ['102'], + ['103'], + ['104'], + ['105'], + ['106'], + ['121']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON player where player.name == "Useless"') self.check_resp_succeeded(resp) - assert resp.row_size() == 1 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['121']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON player where player.name == "Useless" and player.age < 30') self.check_resp_succeeded(resp) - assert resp.row_size() == 1 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['121']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON team where team.name == "Warriors"') self.check_resp_succeeded(resp) - assert resp.row_size() == 1 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['200']] + self.check_out_of_order_result(resp, expected_result) resp = self.execute('LOOKUP ON team where team.name == "oopp"') self.check_resp_succeeded(resp) - assert resp.row_size() == 1 + col_names = ['VertexID'] + self.check_column_names(resp, col_names) + expected_result = [['202']] + self.check_out_of_order_result(resp, expected_result) @classmethod def cleanup(self): diff --git a/tests/tck/features/lookup/ByIndex.feature b/tests/tck/features/lookup/ByIndex.feature new file mode 100644 index 0000000000000000000000000000000000000000..f033ea93a25bfec56dedd3d3656f47081b976f19 --- /dev/null +++ b/tests/tck/features/lookup/ByIndex.feature @@ -0,0 +1,396 @@ +Feature: Lookup by index itself + + Background: Prepare space + Given a graph with space named "nba" + + Scenario: [1] tag index + When executing query: + """ + LOOKUP ON team + """ + Then the result should be, in any order: + | VertexID | + | 'Nets' | + | 'Pistons' | + | 'Bucks' | + | 'Mavericks' | + | 'Clippers' | + | 'Thunders' | + | 'Lakers' | + | 'Jazz' | + | 'Nuggets' | + | 'Wizards' | + | 'Pacers' | + | 'Timberwolves' | + | 'Hawks' | + | 'Warriors' | + | 'Magic' | + | 'Rockets' | + | 'Pelicans' | + | 'Raptors' | + | 'Spurs' | + | 'Heat' | + | 'Grizzlies' | + | 'Knicks' | + | 'Suns' | + | 'Hornets' | + | 'Cavaliers' | + | 'Kings' | + | 'Celtics' | + | '76ers' | + | 'Trail Blazers' | + | 'Bulls' | + When executing query: + """ + LOOKUP ON team YIELD team.name AS Name + """ + Then the result should be, in any order: + | VertexID | Name | + | 'Nets' | 'Nets' | + | 'Pistons' | 'Pistons' | + | 'Bucks' | 'Bucks' | + | 'Mavericks' | 'Mavericks' | + | 'Clippers' | 'Clippers' | + | 'Thunders' | 'Thunders' | + | 'Lakers' | 'Lakers' | + | 'Jazz' | 'Jazz' | + | 'Nuggets' | 'Nuggets' | + | 'Wizards' | 'Wizards' | + | 'Pacers' | 'Pacers' | + | 'Timberwolves' | 'Timberwolves' | + | 'Hawks' | 'Hawks' | + | 'Warriors' | 'Warriors' | + | 'Magic' | 'Magic' | + | 'Rockets' | 'Rockets' | + | 'Pelicans' | 'Pelicans' | + | 'Raptors' | 'Raptors' | + | 'Spurs' | 'Spurs' | + | 'Heat' | 'Heat' | + | 'Grizzlies' | 'Grizzlies' | + | 'Knicks' | 'Knicks' | + | 'Suns' | 'Suns' | + | 'Hornets' | 'Hornets' | + | 'Cavaliers' | 'Cavaliers' | + | 'Kings' | 'Kings' | + | 'Celtics' | 'Celtics' | + | '76ers' | '76ers' | + | 'Trail Blazers' | 'Trail Blazers' | + | 'Bulls' | 'Bulls' | + + Scenario: [2] edge index + When executing query: + """ + LOOKUP ON serve + """ + Then the result should be, in any order: + | SrcVID | DstVID | Ranking | + | "Amar'e Stoudemire" | 'Suns' | 0 | + | "Amar'e Stoudemire" | 'Knicks' | 0 | + | "Amar'e Stoudemire" | 'Heat' | 0 | + | 'Russell Westbrook' | 'Thunders' | 0 | + | 'James Harden' | 'Thunders' | 0 | + | 'James Harden' | 'Rockets' | 0 | + | 'Kobe Bryant' | 'Lakers' | 0 | + | 'Tracy McGrady' | 'Raptors' | 0 | + | 'Tracy McGrady' | 'Magic' | 0 | + | 'Tracy McGrady' | 'Rockets' | 0 | + | 'Tracy McGrady' | 'Spurs' | 0 | + | 'Chris Paul' | 'Hornets' | 0 | + | 'Chris Paul' | 'Clippers' | 0 | + | 'Chris Paul' | 'Rockets' | 0 | + | 'Boris Diaw' | 'Hawks' | 0 | + | 'Boris Diaw' | 'Suns' | 0 | + | 'Boris Diaw' | 'Hornets' | 0 | + | 'Boris Diaw' | 'Spurs' | 0 | + | 'Boris Diaw' | 'Jazz' | 0 | + | 'LeBron James' | 'Cavaliers' | 0 | + | 'LeBron James' | 'Heat' | 0 | + | 'LeBron James' | 'Cavaliers' | 1 | + | 'LeBron James' | 'Lakers' | 0 | + | 'Klay Thompson' | 'Warriors' | 0 | + | 'Kristaps Porzingis' | 'Knicks' | 0 | + | 'Kristaps Porzingis' | 'Mavericks' | 0 | + | 'Jonathon Simmons' | 'Spurs' | 0 | + | 'Jonathon Simmons' | 'Magic' | 0 | + | 'Jonathon Simmons' | '76ers' | 0 | + | 'Marco Belinelli' | 'Warriors' | 0 | + | 'Marco Belinelli' | 'Raptors' | 0 | + | 'Marco Belinelli' | 'Hornets' | 0 | + | 'Marco Belinelli' | 'Bulls' | 0 | + | 'Marco Belinelli' | 'Spurs' | 0 | + | 'Marco Belinelli' | 'Kings' | 0 | + | 'Marco Belinelli' | 'Hornets' | 1 | + | 'Marco Belinelli' | 'Hawks' | 0 | + | 'Marco Belinelli' | '76ers' | 0 | + | 'Marco Belinelli' | 'Spurs' | 1 | + | 'Luka Doncic' | 'Mavericks' | 0 | + | 'David West' | 'Hornets' | 0 | + | 'David West' | 'Pacers' | 0 | + | 'David West' | 'Spurs' | 0 | + | 'David West' | 'Warriors' | 0 | + | 'Tony Parker' | 'Spurs' | 0 | + | 'Tony Parker' | 'Hornets' | 0 | + | 'Danny Green' | 'Cavaliers' | 0 | + | 'Danny Green' | 'Spurs' | 0 | + | 'Danny Green' | 'Raptors' | 0 | + | 'Rudy Gay' | 'Grizzlies' | 0 | + | 'Rudy Gay' | 'Raptors' | 0 | + | 'Rudy Gay' | 'Kings' | 0 | + | 'Rudy Gay' | 'Spurs' | 0 | + | 'LaMarcus Aldridge' | 'Trail Blazers' | 0 | + | 'LaMarcus Aldridge' | 'Spurs' | 0 | + | 'Tim Duncan' | 'Spurs' | 0 | + | 'Kevin Durant' | 'Thunders' | 0 | + | 'Kevin Durant' | 'Warriors' | 0 | + | 'Stephen Curry' | 'Warriors' | 0 | + | 'Ray Allen' | 'Bucks' | 0 | + | 'Ray Allen' | 'Thunders' | 0 | + | 'Ray Allen' | 'Celtics' | 0 | + | 'Ray Allen' | 'Heat' | 0 | + | 'Tiago Splitter' | 'Spurs' | 0 | + | 'Tiago Splitter' | 'Hawks' | 0 | + | 'Tiago Splitter' | '76ers' | 0 | + | 'DeAndre Jordan' | 'Clippers' | 0 | + | 'DeAndre Jordan' | 'Mavericks' | 0 | + | 'DeAndre Jordan' | 'Knicks' | 0 | + | 'Paul Gasol' | 'Grizzlies' | 0 | + | 'Paul Gasol' | 'Lakers' | 0 | + | 'Paul Gasol' | 'Bulls' | 0 | + | 'Paul Gasol' | 'Spurs' | 0 | + | 'Paul Gasol' | 'Bucks' | 0 | + | 'Aron Baynes' | 'Spurs' | 0 | + | 'Aron Baynes' | 'Pistons' | 0 | + | 'Aron Baynes' | 'Celtics' | 0 | + | 'Cory Joseph' | 'Spurs' | 0 | + | 'Cory Joseph' | 'Raptors' | 0 | + | 'Cory Joseph' | 'Pacers' | 0 | + | 'Vince Carter' | 'Raptors' | 0 | + | 'Vince Carter' | 'Nets' | 0 | + | 'Vince Carter' | 'Magic' | 0 | + | 'Vince Carter' | 'Suns' | 0 | + | 'Vince Carter' | 'Mavericks' | 0 | + | 'Vince Carter' | 'Grizzlies' | 0 | + | 'Vince Carter' | 'Kings' | 0 | + | 'Vince Carter' | 'Hawks' | 0 | + | 'Marc Gasol' | 'Grizzlies' | 0 | + | 'Marc Gasol' | 'Raptors' | 0 | + | 'Ricky Rubio' | 'Timberwolves' | 0 | + | 'Ricky Rubio' | 'Jazz' | 0 | + | 'Ben Simmons' | '76ers' | 0 | + | 'Giannis Antetokounmpo' | 'Bucks' | 0 | + | 'Rajon Rondo' | 'Celtics' | 0 | + | 'Rajon Rondo' | 'Mavericks' | 0 | + | 'Rajon Rondo' | 'Kings' | 0 | + | 'Rajon Rondo' | 'Bulls' | 0 | + | 'Rajon Rondo' | 'Pelicans' | 0 | + | 'Rajon Rondo' | 'Lakers' | 0 | + | 'Manu Ginobili' | 'Spurs' | 0 | + | 'Kyrie Irving' | 'Cavaliers' | 0 | + | 'Kyrie Irving' | 'Celtics' | 0 | + | 'Carmelo Anthony' | 'Nuggets' | 0 | + | 'Carmelo Anthony' | 'Knicks' | 0 | + | 'Carmelo Anthony' | 'Thunders' | 0 | + | 'Carmelo Anthony' | 'Rockets' | 0 | + | 'Dwyane Wade' | 'Heat' | 0 | + | 'Dwyane Wade' | 'Bulls' | 0 | + | 'Dwyane Wade' | 'Cavaliers' | 0 | + | 'Dwyane Wade' | 'Heat' | 1 | + | 'Joel Embiid' | '76ers' | 0 | + | 'Damian Lillard' | 'Trail Blazers' | 0 | + | 'Yao Ming' | 'Rockets' | 0 | + | 'Kyle Anderson' | 'Spurs' | 0 | + | 'Kyle Anderson' | 'Grizzlies' | 0 | + | 'Dejounte Murray' | 'Spurs' | 0 | + | 'Blake Griffin' | 'Clippers' | 0 | + | 'Blake Griffin' | 'Pistons' | 0 | + | 'Steve Nash' | 'Suns' | 0 | + | 'Steve Nash' | 'Mavericks' | 0 | + | 'Steve Nash' | 'Suns' | 1 | + | 'Steve Nash' | 'Lakers' | 0 | + | 'Jason Kidd' | 'Mavericks' | 0 | + | 'Jason Kidd' | 'Suns' | 0 | + | 'Jason Kidd' | 'Nets' | 0 | + | 'Jason Kidd' | 'Mavericks' | 1 | + | 'Jason Kidd' | 'Knicks' | 0 | + | 'Dirk Nowitzki' | 'Mavericks' | 0 | + | 'Paul George' | 'Pacers' | 0 | + | 'Paul George' | 'Thunders' | 0 | + | 'Grant Hill' | 'Pistons' | 0 | + | 'Grant Hill' | 'Magic' | 0 | + | 'Grant Hill' | 'Suns' | 0 | + | 'Grant Hill' | 'Clippers' | 0 | + | "Shaquile O'Neal" | 'Magic' | 0 | + | "Shaquile O'Neal" | 'Lakers' | 0 | + | "Shaquile O'Neal" | 'Heat' | 0 | + | "Shaquile O'Neal" | 'Suns' | 0 | + | "Shaquile O'Neal" | 'Cavaliers' | 0 | + | "Shaquile O'Neal" | 'Celtics' | 0 | + | 'JaVale McGee' | 'Wizards' | 0 | + | 'JaVale McGee' | 'Nuggets' | 0 | + | 'JaVale McGee' | 'Mavericks' | 0 | + | 'JaVale McGee' | 'Warriors' | 0 | + | 'JaVale McGee' | 'Lakers' | 0 | + | 'Dwight Howard' | 'Magic' | 0 | + | 'Dwight Howard' | 'Lakers' | 0 | + | 'Dwight Howard' | 'Rockets' | 0 | + | 'Dwight Howard' | 'Hawks' | 0 | + | 'Dwight Howard' | 'Hornets' | 0 | + | 'Dwight Howard' | 'Wizards' | 0 | + When executing query: + """ + LOOKUP ON serve YIELD serve.start_year AS startYear + """ + Then the result should be, in any order: + | SrcVID | DstVID | Ranking | startYear | + | "Amar'e Stoudemire" | 'Suns' | 0 | 2002 | + | "Amar'e Stoudemire" | 'Knicks' | 0 | 2010 | + | "Amar'e Stoudemire" | 'Heat' | 0 | 2015 | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'James Harden' | 'Thunders' | 0 | 2009 | + | 'James Harden' | 'Rockets' | 0 | 2012 | + | 'Kobe Bryant' | 'Lakers' | 0 | 1996 | + | 'Tracy McGrady' | 'Raptors' | 0 | 1997 | + | 'Tracy McGrady' | 'Magic' | 0 | 2000 | + | 'Tracy McGrady' | 'Rockets' | 0 | 2004 | + | 'Tracy McGrady' | 'Spurs' | 0 | 2013 | + | 'Chris Paul' | 'Hornets' | 0 | 2005 | + | 'Chris Paul' | 'Clippers' | 0 | 2011 | + | 'Chris Paul' | 'Rockets' | 0 | 2017 | + | 'Boris Diaw' | 'Hawks' | 0 | 2003 | + | 'Boris Diaw' | 'Suns' | 0 | 2005 | + | 'Boris Diaw' | 'Hornets' | 0 | 2008 | + | 'Boris Diaw' | 'Spurs' | 0 | 2012 | + | 'Boris Diaw' | 'Jazz' | 0 | 2016 | + | 'LeBron James' | 'Cavaliers' | 0 | 2003 | + | 'LeBron James' | 'Heat' | 0 | 2010 | + | 'LeBron James' | 'Cavaliers' | 1 | 2014 | + | 'LeBron James' | 'Lakers' | 0 | 2018 | + | 'Klay Thompson' | 'Warriors' | 0 | 2011 | + | 'Kristaps Porzingis' | 'Knicks' | 0 | 2015 | + | 'Kristaps Porzingis' | 'Mavericks' | 0 | 2019 | + | 'Jonathon Simmons' | 'Spurs' | 0 | 2015 | + | 'Jonathon Simmons' | 'Magic' | 0 | 2017 | + | 'Jonathon Simmons' | '76ers' | 0 | 2019 | + | 'Marco Belinelli' | 'Warriors' | 0 | 2007 | + | 'Marco Belinelli' | 'Raptors' | 0 | 2009 | + | 'Marco Belinelli' | 'Hornets' | 0 | 2010 | + | 'Marco Belinelli' | 'Bulls' | 0 | 2012 | + | 'Marco Belinelli' | 'Spurs' | 0 | 2013 | + | 'Marco Belinelli' | 'Kings' | 0 | 2015 | + | 'Marco Belinelli' | 'Hornets' | 1 | 2016 | + | 'Marco Belinelli' | 'Hawks' | 0 | 2017 | + | 'Marco Belinelli' | '76ers' | 0 | 2018 | + | 'Marco Belinelli' | 'Spurs' | 1 | 2018 | + | 'Luka Doncic' | 'Mavericks' | 0 | 2018 | + | 'David West' | 'Hornets' | 0 | 2003 | + | 'David West' | 'Pacers' | 0 | 2011 | + | 'David West' | 'Spurs' | 0 | 2015 | + | 'David West' | 'Warriors' | 0 | 2016 | + | 'Tony Parker' | 'Spurs' | 0 | 1999 | + | 'Tony Parker' | 'Hornets' | 0 | 2018 | + | 'Danny Green' | 'Cavaliers' | 0 | 2009 | + | 'Danny Green' | 'Spurs' | 0 | 2010 | + | 'Danny Green' | 'Raptors' | 0 | 2018 | + | 'Rudy Gay' | 'Grizzlies' | 0 | 2006 | + | 'Rudy Gay' | 'Raptors' | 0 | 2013 | + | 'Rudy Gay' | 'Kings' | 0 | 2013 | + | 'Rudy Gay' | 'Spurs' | 0 | 2017 | + | 'LaMarcus Aldridge' | 'Trail Blazers' | 0 | 2006 | + | 'LaMarcus Aldridge' | 'Spurs' | 0 | 2015 | + | 'Tim Duncan' | 'Spurs' | 0 | 1997 | + | 'Kevin Durant' | 'Thunders' | 0 | 2007 | + | 'Kevin Durant' | 'Warriors' | 0 | 2016 | + | 'Stephen Curry' | 'Warriors' | 0 | 2009 | + | 'Ray Allen' | 'Bucks' | 0 | 1996 | + | 'Ray Allen' | 'Thunders' | 0 | 2003 | + | 'Ray Allen' | 'Celtics' | 0 | 2007 | + | 'Ray Allen' | 'Heat' | 0 | 2012 | + | 'Tiago Splitter' | 'Spurs' | 0 | 2010 | + | 'Tiago Splitter' | 'Hawks' | 0 | 2015 | + | 'Tiago Splitter' | '76ers' | 0 | 2017 | + | 'DeAndre Jordan' | 'Clippers' | 0 | 2008 | + | 'DeAndre Jordan' | 'Mavericks' | 0 | 2018 | + | 'DeAndre Jordan' | 'Knicks' | 0 | 2019 | + | 'Paul Gasol' | 'Grizzlies' | 0 | 2001 | + | 'Paul Gasol' | 'Lakers' | 0 | 2008 | + | 'Paul Gasol' | 'Bulls' | 0 | 2014 | + | 'Paul Gasol' | 'Spurs' | 0 | 2016 | + | 'Paul Gasol' | 'Bucks' | 0 | 2019 | + | 'Aron Baynes' | 'Spurs' | 0 | 2013 | + | 'Aron Baynes' | 'Pistons' | 0 | 2015 | + | 'Aron Baynes' | 'Celtics' | 0 | 2017 | + | 'Cory Joseph' | 'Spurs' | 0 | 2011 | + | 'Cory Joseph' | 'Raptors' | 0 | 2015 | + | 'Cory Joseph' | 'Pacers' | 0 | 2017 | + | 'Vince Carter' | 'Raptors' | 0 | 1998 | + | 'Vince Carter' | 'Nets' | 0 | 2004 | + | 'Vince Carter' | 'Magic' | 0 | 2009 | + | 'Vince Carter' | 'Suns' | 0 | 2010 | + | 'Vince Carter' | 'Mavericks' | 0 | 2011 | + | 'Vince Carter' | 'Grizzlies' | 0 | 2014 | + | 'Vince Carter' | 'Kings' | 0 | 2017 | + | 'Vince Carter' | 'Hawks' | 0 | 2018 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | + | 'Marc Gasol' | 'Raptors' | 0 | 2019 | + | 'Ricky Rubio' | 'Timberwolves' | 0 | 2011 | + | 'Ricky Rubio' | 'Jazz' | 0 | 2017 | + | 'Ben Simmons' | '76ers' | 0 | 2016 | + | 'Giannis Antetokounmpo' | 'Bucks' | 0 | 2013 | + | 'Rajon Rondo' | 'Celtics' | 0 | 2006 | + | 'Rajon Rondo' | 'Mavericks' | 0 | 2014 | + | 'Rajon Rondo' | 'Kings' | 0 | 2015 | + | 'Rajon Rondo' | 'Bulls' | 0 | 2016 | + | 'Rajon Rondo' | 'Pelicans' | 0 | 2017 | + | 'Rajon Rondo' | 'Lakers' | 0 | 2018 | + | 'Manu Ginobili' | 'Spurs' | 0 | 2002 | + | 'Kyrie Irving' | 'Cavaliers' | 0 | 2011 | + | 'Kyrie Irving' | 'Celtics' | 0 | 2017 | + | 'Carmelo Anthony' | 'Nuggets' | 0 | 2003 | + | 'Carmelo Anthony' | 'Knicks' | 0 | 2011 | + | 'Carmelo Anthony' | 'Thunders' | 0 | 2017 | + | 'Carmelo Anthony' | 'Rockets' | 0 | 2018 | + | 'Dwyane Wade' | 'Heat' | 0 | 2003 | + | 'Dwyane Wade' | 'Bulls' | 0 | 2016 | + | 'Dwyane Wade' | 'Cavaliers' | 0 | 2017 | + | 'Dwyane Wade' | 'Heat' | 1 | 2018 | + | 'Joel Embiid' | '76ers' | 0 | 2014 | + | 'Damian Lillard' | 'Trail Blazers' | 0 | 2012 | + | 'Yao Ming' | 'Rockets' | 0 | 2002 | + | 'Kyle Anderson' | 'Spurs' | 0 | 2014 | + | 'Kyle Anderson' | 'Grizzlies' | 0 | 2018 | + | 'Dejounte Murray' | 'Spurs' | 0 | 2016 | + | 'Blake Griffin' | 'Clippers' | 0 | 2009 | + | 'Blake Griffin' | 'Pistons' | 0 | 2018 | + | 'Steve Nash' | 'Suns' | 0 | 1996 | + | 'Steve Nash' | 'Mavericks' | 0 | 1998 | + | 'Steve Nash' | 'Suns' | 1 | 2004 | + | 'Steve Nash' | 'Lakers' | 0 | 2012 | + | 'Jason Kidd' | 'Mavericks' | 0 | 1994 | + | 'Jason Kidd' | 'Suns' | 0 | 1996 | + | 'Jason Kidd' | 'Nets' | 0 | 2001 | + | 'Jason Kidd' | 'Mavericks' | 1 | 2008 | + | 'Jason Kidd' | 'Knicks' | 0 | 2012 | + | 'Dirk Nowitzki' | 'Mavericks' | 0 | 1998 | + | 'Paul George' | 'Pacers' | 0 | 2010 | + | 'Paul George' | 'Thunders' | 0 | 2017 | + | 'Grant Hill' | 'Pistons' | 0 | 1994 | + | 'Grant Hill' | 'Magic' | 0 | 2000 | + | 'Grant Hill' | 'Suns' | 0 | 2007 | + | 'Grant Hill' | 'Clippers' | 0 | 2012 | + | "Shaquile O'Neal" | 'Magic' | 0 | 1992 | + | "Shaquile O'Neal" | 'Lakers' | 0 | 1996 | + | "Shaquile O'Neal" | 'Heat' | 0 | 2004 | + | "Shaquile O'Neal" | 'Suns' | 0 | 2008 | + | "Shaquile O'Neal" | 'Cavaliers' | 0 | 2009 | + | "Shaquile O'Neal" | 'Celtics' | 0 | 2010 | + | 'JaVale McGee' | 'Wizards' | 0 | 2008 | + | 'JaVale McGee' | 'Nuggets' | 0 | 2012 | + | 'JaVale McGee' | 'Mavericks' | 0 | 2015 | + | 'JaVale McGee' | 'Warriors' | 0 | 2016 | + | 'JaVale McGee' | 'Lakers' | 0 | 2018 | + | 'Dwight Howard' | 'Magic' | 0 | 2004 | + | 'Dwight Howard' | 'Lakers' | 0 | 2012 | + | 'Dwight Howard' | 'Rockets' | 0 | 2013 | + | 'Dwight Howard' | 'Hawks' | 0 | 2016 | + | 'Dwight Howard' | 'Hornets' | 0 | 2017 | + | 'Dwight Howard' | 'Wizards' | 0 | 2018 | diff --git a/tests/tck/features/lookup/Output.feature b/tests/tck/features/lookup/Output.feature new file mode 100644 index 0000000000000000000000000000000000000000..838209ea80eaebe0404f342d25abb42207d5bf50 --- /dev/null +++ b/tests/tck/features/lookup/Output.feature @@ -0,0 +1,96 @@ +Feature: Lookup with output + + Background: Prepare space + Given a graph with space named "nba" + + Scenario: [1] tag output + When executing query: + """ + LOOKUP ON player WHERE player.age == 40 | + FETCH PROP ON player $-.VertexID YIELD player.name + """ + Then the result should be, in any order: + | VertexID | player.name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [1] tag ouput with yield rename + When executing query: + """ + LOOKUP ON player WHERE player.age == 40 YIELD player.name AS name | + FETCH PROP ON player $-.name YIELD player.name AS name + """ + Then the result should be, in any order: + | VertexID | name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [1] tag output by var + When executing query: + """ + $a = LOOKUP ON player WHERE player.age == 40; + FETCH PROP ON player $a.VertexID YIELD player.name + """ + Then the result should be, in any order: + | VertexID | player.name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [1] tag ouput with yield rename by var + When executing query: + """ + $a = LOOKUP ON player WHERE player.age == 40 YIELD player.name AS name; + FETCH PROP ON player $a.name YIELD player.name AS name + """ + Then the result should be, in any order: + | VertexID | name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [2] edge output + When executing query: + """ + LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year | + FETCH PROP ON serve $-.SrcVID->$-.DstVID YIELD serve.start_year + """ + Then the result should be, in any order: + | serve._src | serve._dst | serve._rank | serve.start_year | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | + + Scenario: [2] edge output with yield rename + When executing query: + """ + LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year AS startYear | + FETCH PROP ON serve $-.SrcVID->$-.DstVID YIELD serve.start_year AS startYear + """ + Then the result should be, in any order: + | serve._src | serve._dst | serve._rank | startYear | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | + + Scenario: [2] edge output by var + When executing query: + """ + $a = LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year; + FETCH PROP ON serve $a.SrcVID->$a.DstVID YIELD serve.start_year + """ + Then the result should be, in any order: + | serve._src | serve._dst | serve._rank | serve.start_year | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | + + Scenario: [2] edge output with yield rename by var + When executing query: + """ + $a = LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year AS startYear; + FETCH PROP ON serve $a.SrcVID->$a.DstVID YIELD serve.start_year AS startYear + """ + Then the result should be, in any order: + | serve._src | serve._dst | serve._rank | startYear | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | diff --git a/tests/tck/features/lookup/WithYield.feature b/tests/tck/features/lookup/WithYield.feature new file mode 100644 index 0000000000000000000000000000000000000000..9aedd4ebe2750a9dc7cecb47c8ac8cb6d13c2635 --- /dev/null +++ b/tests/tck/features/lookup/WithYield.feature @@ -0,0 +1,46 @@ +Feature: Lookup with yield + + Background: Prepare space + Given a graph with space named "nba" + + Scenario: [1] tag with yield + When executing query: + """ + LOOKUP ON player WHERE player.age == 40 YIELD player.name + """ + Then the result should be, in any order: + | VertexID | player.name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [1] tag with yield rename + When executing query: + """ + LOOKUP ON player WHERE player.age == 40 YIELD player.name AS name + """ + Then the result should be, in any order: + | VertexID | name | + | 'Kobe Bryant' | 'Kobe Bryant' | + | 'Dirk Nowitzki' | 'Dirk Nowitzki' | + + Scenario: [2] edge with yield + When executing query: + """ + LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year + """ + Then the result should be, in any order: + | SrcVID | DstVID | Ranking | serve.start_year | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 | + + Scenario: [2] edge with yield rename + When executing query: + """ + LOOKUP ON serve WHERE serve.start_year == 2008 and serve.end_year == 2019 + YIELD serve.start_year AS startYear + """ + Then the result should be, in any order: + | SrcVID | DstVID | Ranking | startYear | + | 'Russell Westbrook' | 'Thunders' | 0 | 2008 | + | 'Marc Gasol' | 'Grizzlies' | 0 | 2008 |