diff --git a/src/graph/planner/plan/Admin.cpp b/src/graph/planner/plan/Admin.cpp index 119256e47ec..a80cf0f2f09 100644 --- a/src/graph/planner/plan/Admin.cpp +++ b/src/graph/planner/plan/Admin.cpp @@ -15,7 +15,7 @@ namespace graph { std::unique_ptr CreateSpace::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("ifNotExists", util::toJson(ifNotExists_), desc.get()); + addDescription("ifNotExists", folly::toJson(util::toJson(ifNotExists_)), desc.get()); addDescription("spaceDesc", folly::toJson(util::toJson(spaceDesc_)), desc.get()); return desc; } @@ -30,14 +30,14 @@ std::unique_ptr CreateSpaceAsNode::explain() const { std::unique_ptr DropSpace::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("spaceName", spaceName_, desc.get()); - addDescription("ifExists", util::toJson(ifExists_), desc.get()); + addDescription("ifExists", folly::toJson(util::toJson(ifExists_)), desc.get()); return desc; } std::unique_ptr ClearSpace::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("spaceName", spaceName_, desc.get()); - addDescription("ifExists", util::toJson(ifExists_), desc.get()); + addDescription("ifExists", folly::toJson(util::toJson(ifExists_)), desc.get()); return desc; } @@ -61,7 +61,7 @@ std::unique_ptr DropSnapshot::explain() const { std::unique_ptr ShowParts::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("spaceId", folly::to(spaceId_), desc.get()); + addDescription("spaceId", folly::toJson(util::toJson(spaceId_)), desc.get()); addDescription("partIds", folly::toJson(util::toJson(partIds_)), desc.get()); return desc; } @@ -89,13 +89,13 @@ std::unique_ptr GetConfig::explain() const { std::unique_ptr CreateNode::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("ifNotExist", util::toJson(ifNotExist_), desc.get()); + addDescription("ifNotExist", folly::toJson(util::toJson(ifNotExist_)), desc.get()); return desc; } std::unique_ptr DropNode::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("ifExist", util::toJson(ifExist_), desc.get()); + addDescription("ifExist", folly::toJson(util::toJson(ifExist_)), desc.get()); return desc; } @@ -169,7 +169,7 @@ std::unique_ptr ListUserRoles::explain() const { std::unique_ptr ListRoles::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("space", util::toJson(space_), desc.get()); + addDescription("space", folly::toJson(util::toJson(space_)), desc.get()); return desc; } @@ -183,7 +183,7 @@ std::unique_ptr SubmitJob::explain() const { std::unique_ptr ShowQueries::explain() const { auto desc = SingleDependencyNode::explain(); - addDescription("isAll", util::toJson(isAll()), desc.get()); + addDescription("isAll", folly::toJson(util::toJson(isAll())), desc.get()); return desc; } diff --git a/src/graph/planner/plan/Algo.cpp b/src/graph/planner/plan/Algo.cpp index 86a782a1350..f6625c68fe3 100644 --- a/src/graph/planner/plan/Algo.cpp +++ b/src/graph/planner/plan/Algo.cpp @@ -11,26 +11,26 @@ namespace graph { std::unique_ptr BFSShortestPath::explain() const { auto desc = BinaryInputNode::explain(); - addDescription("LeftNextVidVar", util::toJson(leftVidVar_), desc.get()); - addDescription("RightNextVidVar", util::toJson(rightVidVar_), desc.get()); - addDescription("steps", util::toJson(steps_), desc.get()); + addDescription("LeftNextVidVar", folly::toJson(util::toJson(leftVidVar_)), desc.get()); + addDescription("RightNextVidVar", folly::toJson(util::toJson(rightVidVar_)), desc.get()); + addDescription("steps", folly::toJson(util::toJson(steps_)), desc.get()); return desc; } std::unique_ptr MultiShortestPath::explain() const { auto desc = BinaryInputNode::explain(); - addDescription("LeftNextVidVar", util::toJson(leftVidVar_), desc.get()); - addDescription("RightNextVidVar", util::toJson(rightVidVar_), desc.get()); - addDescription("steps", util::toJson(steps_), desc.get()); + addDescription("LeftNextVidVar", folly::toJson(util::toJson(leftVidVar_)), desc.get()); + addDescription("RightNextVidVar", folly::toJson(util::toJson(rightVidVar_)), desc.get()); + addDescription("steps", folly::toJson(util::toJson(steps_)), desc.get()); return desc; } std::unique_ptr ProduceAllPaths::explain() const { auto desc = BinaryInputNode::explain(); - addDescription("LeftNextVidVar", util::toJson(leftVidVar_), desc.get()); - addDescription("RightNextVidVar", util::toJson(rightVidVar_), desc.get()); - addDescription("noloop ", util::toJson(noLoop_), desc.get()); - addDescription("steps", util::toJson(steps_), desc.get()); + addDescription("LeftNextVidVar", folly::toJson(util::toJson(leftVidVar_)), desc.get()); + addDescription("RightNextVidVar", folly::toJson(util::toJson(rightVidVar_)), desc.get()); + addDescription("noloop ", folly::toJson(util::toJson(noLoop_)), desc.get()); + addDescription("steps", folly::toJson(util::toJson(steps_)), desc.get()); return desc; } diff --git a/src/graph/planner/plan/Maintain.cpp b/src/graph/planner/plan/Maintain.cpp index 4a108ce1cc0..9a1d46791fe 100644 --- a/src/graph/planner/plan/Maintain.cpp +++ b/src/graph/planner/plan/Maintain.cpp @@ -15,7 +15,7 @@ namespace graph { std::unique_ptr CreateSchemaNode::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("name", name_, desc.get()); - addDescription("ifNotExists", util::toJson(ifNotExists_), desc.get()); + addDescription("ifNotExists", folly::toJson(util::toJson(ifNotExists_)), desc.get()); addDescription("schema", folly::toJson(util::toJson(schema_)), desc.get()); return desc; } @@ -38,7 +38,7 @@ std::unique_ptr DescSchemaNode::explain() const { std::unique_ptr DropSchemaNode::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("name", name_, desc.get()); - addDescription("ifExists", util::toJson(ifExists_), desc.get()); + addDescription("ifExists", folly::toJson(util::toJson(ifExists_)), desc.get()); return desc; } @@ -67,7 +67,7 @@ std::unique_ptr DescIndexNode::explain() const { std::unique_ptr DropIndexNode::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("indexName", indexName_, desc.get()); - addDescription("ifExists", util::toJson(ifExists_), desc.get()); + addDescription("ifExists", folly::toJson(util::toJson(ifExists_)), desc.get()); return desc; } diff --git a/src/graph/planner/plan/Mutate.cpp b/src/graph/planner/plan/Mutate.cpp index 27ad08440ce..8bddf3da506 100644 --- a/src/graph/planner/plan/Mutate.cpp +++ b/src/graph/planner/plan/Mutate.cpp @@ -14,7 +14,7 @@ namespace graph { std::unique_ptr InsertVertices::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("spaceId", folly::to(spaceId_), desc.get()); - addDescription("ifNotExists", util::toJson(ifNotExists_), desc.get()); + addDescription("ifNotExists", folly::toJson(util::toJson(ifNotExists_)), desc.get()); folly::dynamic tagPropsArr = folly::dynamic::array(); for (const auto &p : tagPropNames_) { @@ -31,7 +31,7 @@ std::unique_ptr InsertVertices::explain() const { std::unique_ptr InsertEdges::explain() const { auto desc = SingleDependencyNode::explain(); addDescription("spaceId", folly::to(spaceId_), desc.get()); - addDescription("ifNotExists", util::toJson(ifNotExists_), desc.get()); + addDescription("ifNotExists", folly::toJson(util::toJson(ifNotExists_)), desc.get()); addDescription("propNames", folly::toJson(util::toJson(propNames_)), desc.get()); addDescription("edges", folly::toJson(util::toJson(edges_)), desc.get()); return desc; diff --git a/src/graph/planner/plan/Query.cpp b/src/graph/planner/plan/Query.cpp index c0fe8c2b68d..0237e075d48 100644 --- a/src/graph/planner/plan/Query.cpp +++ b/src/graph/planner/plan/Query.cpp @@ -29,7 +29,7 @@ int64_t Explore::limit(QueryContext* qctx) const { std::unique_ptr Explore::explain() const { auto desc = SingleInputNode::explain(); addDescription("space", folly::to(space_), desc.get()); - addDescription("dedup", util::toJson(dedup_), desc.get()); + addDescription("dedup", folly::toJson(util::toJson(dedup_)), desc.get()); addDescription( "limit", folly::to(limit_ == nullptr ? "" : limit_->toString()), desc.get()); std::string filter = filter_ == nullptr ? "" : filter_->toString(); @@ -60,7 +60,7 @@ std::unique_ptr GetNeighbors::explain() const { addDescription( "statProps", statProps_ ? folly::toJson(util::toJson(*statProps_)) : "", desc.get()); addDescription("exprs", exprs_ ? folly::toJson(util::toJson(*exprs_)) : "", desc.get()); - addDescription("random", util::toJson(random_), desc.get()); + addDescription("random", folly::toJson(util::toJson(random_)), desc.get()); return desc; } @@ -137,7 +137,7 @@ void GetVertices::cloneMembers(const GetVertices& gv) { std::unique_ptr GetEdges::explain() const { auto desc = Explore::explain(); addDescription("src", src_ ? src_->toString() : "", desc.get()); - addDescription("type", util::toJson(type_), desc.get()); + addDescription("type", folly::toJson(util::toJson(type_)), desc.get()); addDescription("ranking", ranking_ ? ranking_->toString() : "", desc.get()); addDescription("dst", dst_ ? dst_->toString() : "", desc.get()); addDescription("props", props_ ? folly::toJson(util::toJson(*props_)) : "", desc.get()); @@ -174,8 +174,8 @@ void GetEdges::cloneMembers(const GetEdges& ge) { std::unique_ptr IndexScan::explain() const { auto desc = Explore::explain(); - addDescription("schemaId", util::toJson(schemaId_), desc.get()); - addDescription("isEdge", util::toJson(isEdge_), desc.get()); + addDescription("schemaId", folly::toJson(util::toJson(schemaId_)), desc.get()); + addDescription("isEdge", folly::toJson(util::toJson(isEdge_)), desc.get()); addDescription("returnCols", folly::toJson(util::toJson(returnCols_)), desc.get()); addDescription("indexCtx", folly::toJson(util::toJson(contexts_)), desc.get()); return desc; @@ -605,8 +605,8 @@ void DataCollect::cloneMembers(const DataCollect& l) { std::unique_ptr Join::explain() const { auto desc = SingleDependencyNode::explain(); folly::dynamic inputVar = folly::dynamic::object(); - inputVar.insert("leftVar", util::toJson(leftVar_)); - inputVar.insert("rightVar", util::toJson(rightVar_)); + inputVar.insert("leftVar", folly::toJson(util::toJson(leftVar_))); + inputVar.insert("rightVar", folly::toJson(util::toJson(rightVar_))); addDescription("inputVar", folly::toJson(inputVar), desc.get()); addDescription("hashKeys", folly::toJson(util::toJson(hashKeys_)), desc.get()); addDescription("probeKeys", folly::toJson(util::toJson(probeKeys_)), desc.get()); @@ -741,7 +741,7 @@ std::unique_ptr Traverse::explain() const { addDescription("steps", range_ != nullptr ? range_->toString() : "", desc.get()); addDescription("vertex filter", vFilter_ != nullptr ? vFilter_->toString() : "", desc.get()); addDescription("edge filter", eFilter_ != nullptr ? eFilter_->toString() : "", desc.get()); - addDescription("if_track_previous_path", util::toJson(trackPrevPath_), desc.get()); + addDescription("if_track_previous_path", folly::toJson(util::toJson(trackPrevPath_)), desc.get()); return desc; } @@ -769,7 +769,7 @@ void AppendVertices::cloneMembers(const AppendVertices& a) { std::unique_ptr AppendVertices::explain() const { auto desc = GetVertices::explain(); addDescription("vertex_filter", vFilter_ != nullptr ? vFilter_->toString() : "", desc.get()); - addDescription("if_track_previous_path", util::toJson(trackPrevPath_), desc.get()); + addDescription("if_track_previous_path", folly::toJson(util::toJson(trackPrevPath_)), desc.get()); return desc; } diff --git a/src/graph/util/ToJson.cpp b/src/graph/util/ToJson.cpp index e3652062b55..5c6b574690a 100644 --- a/src/graph/util/ToJson.cpp +++ b/src/graph/util/ToJson.cpp @@ -23,24 +23,24 @@ namespace nebula { namespace util { -std::string toJson(const std::string &str) { +folly::dynamic toJson(const std::string &str) { return str; } -std::string toJson(int32_t i) { - return folly::to(i); +folly::dynamic toJson(int32_t i) { + return i; } -std::string toJson(int64_t i) { - return folly::to(i); +folly::dynamic toJson(int64_t i) { + return i; } -std::string toJson(size_t i) { - return folly::to(i); +folly::dynamic toJson(size_t i) { + return i; } -std::string toJson(bool b) { - return b ? "true" : "false"; +folly::dynamic toJson(bool b) { + return b; } std::string toJson(const HostAddr &addr) { @@ -51,8 +51,39 @@ std::string toJson(const List &list) { return list.toString(); } -std::string toJson(const Value &value) { - return value.toString(); +folly::dynamic toJson(const Value &value) { + switch (value.type()) { + case Value::Type::__EMPTY__: + return "__EMPTY__"; + case Value::Type::BOOL: + return value.getBool(); + case Value::Type::INT: + return value.getInt(); + case Value::Type::FLOAT: + return value.getFloat(); + case Value::Type::STRING: + return value.getStr(); + case Value::Type::DATE: + case Value::Type::TIME: + case Value::Type::DATETIME: + case Value::Type::DURATION: + // TODO store to object + return value.toString(); + case Value::Type::NULLVALUE: + return nullptr; + case Value::Type::VERTEX: + case Value::Type::EDGE: + case Value::Type::PATH: + case Value::Type::LIST: + case Value::Type::MAP: + case Value::Type::SET: + case Value::Type::DATASET: + case Value::Type::GEOGRAPHY: + // TODO store to object or array + return value.toString(); + } + DLOG(FATAL) << "Impossible reach."; + return folly::dynamic::object(); } std::string toJson(const EdgeKeyRef *ref) { @@ -218,7 +249,7 @@ folly::dynamic toJson(const storage::cpp2::VertexProp &prop) { folly::dynamic toJson(const storage::cpp2::EdgeProp &prop) { folly::dynamic obj = folly::dynamic::object(); if (prop.type_ref().is_set()) { - obj.insert("type", toJson(*prop.type_ref())); + obj.insert("type", *prop.type_ref()); } if (prop.props_ref().is_set()) { obj.insert("props", toJson(*prop.props_ref())); @@ -267,11 +298,8 @@ folly::dynamic toJson(const storage::cpp2::IndexColumnHint &hints) { obj.insert("column", hints.get_column_name()); auto scanType = apache::thrift::util::enumNameSafe(hints.get_scan_type()); obj.insert("scanType", scanType); - auto rtrim = [](const std::string &str) { return std::string(str.c_str()); }; - auto begin = toJson(hints.get_begin_value()); - obj.insert("beginValue", rtrim(begin)); - auto end = toJson(hints.get_end_value()); - obj.insert("endValue", rtrim(end)); + obj.insert("beginValue", toJson(hints.get_begin_value())); + obj.insert("endValue", toJson(hints.get_end_value())); auto includeBegin = toJson(hints.get_include_begin()); obj.insert("includeBegin", includeBegin); auto includeEnd = toJson(hints.get_include_end()); diff --git a/src/graph/util/ToJson.h b/src/graph/util/ToJson.h index 67c34326ae3..3bef38013c6 100644 --- a/src/graph/util/ToJson.h +++ b/src/graph/util/ToJson.h @@ -54,15 +54,15 @@ namespace util { template folly::dynamic toJson(const std::vector &arr); -std::string toJson(const std::string &str); -std::string toJson(int32_t i); -std::string toJson(int64_t i); -std::string toJson(size_t i); -std::string toJson(bool b); +folly::dynamic toJson(const std::string &str); +folly::dynamic toJson(int32_t i); +folly::dynamic toJson(int64_t i); +folly::dynamic toJson(size_t i); +folly::dynamic toJson(bool b); std::string toJson(const HostAddr &addr); std::string toJson(const List &list); -std::string toJson(const Value &value); +folly::dynamic toJson(const Value &value); std::string toJson(const EdgeKeyRef *ref); std::string toJson(const Expression *expr); diff --git a/tests/common/plan_differ.py b/tests/common/plan_differ.py index 132cbb6a809..a4002426357 100644 --- a/tests/common/plan_differ.py +++ b/tests/common/plan_differ.py @@ -4,6 +4,7 @@ import re import json +import deepdiff class PlanDiffer: ID = "id" @@ -128,14 +129,31 @@ def _is_subdict_nested(self, expect, resp): if extracted_expected_dict == expect: key_list.append(list(expect.keys())[0]) + def _try_convert_json(j): + try: + return json.loads(j) + except: + return j + extracted_resp_dict = {} if len(key_list) == 1: - extracted_resp_dict = resp + + for k in resp: + extracted_resp_dict[k] = _try_convert_json(resp[k]) else: extracted_resp_dict = self._convert_jsonStr_to_dict(resp, key_list) + + for k in extracted_expected_dict: + extracted_expected_dict[k] = _try_convert_json(extracted_expected_dict[k]) def _is_subdict(small, big): - return dict(big, **small) == big + new_big = dict(big, **small) + print(big) + print(small) + print(new_big) + diff = deepdiff.DeepDiff(big, new_big) + print(diff) + return not bool(diff) return _is_subdict(extracted_expected_dict, extracted_resp_dict) diff --git a/tests/common/utils.py b/tests/common/utils.py index 9d7c5d68a8a..631eacbfad7 100644 --- a/tests/common/utils.py +++ b/tests/common/utils.py @@ -426,7 +426,8 @@ def load_csv_data( create_space(space_desc, sess) schemas = config['schema'] - for line in schemas.splitlines(): + sorted_schema_lines = sorted(schemas.splitlines()) # sort to make sure schema is created in fixed order + for line in sorted_schema_lines: resp_ok(sess, line.strip(), True) # wait heartbeat_interval_secs + 1 seconds for schema synchronization diff --git a/tests/nebula-test-run.py b/tests/nebula-test-run.py index b131f84acbf..bf23e452941 100755 --- a/tests/nebula-test-run.py +++ b/tests/nebula-test-run.py @@ -132,7 +132,8 @@ def start_nebula(nb, configs): with open(SPACE_TMP_PATH, "w") as f: spaces = [] folder = os.path.join(CURR_PATH, "data") - for space in os.listdir(folder): + data_dirs = sorted(os.listdir(folder)) # sort to make sure schema id is created by fixed order + for space in data_dirs: if not os.path.exists(os.path.join(folder, space, "config.yaml")): continue data_dir = os.path.join(folder, space) @@ -179,7 +180,8 @@ def start_standalone(nb, configs): with open(SPACE_TMP_PATH, "w") as f: spaces = [] folder = os.path.join(CURR_PATH, "data") - for space in os.listdir(folder): + data_dirs = sorted(os.listdir(folder)) # sort to make sure schema id is created by fixed order + for space in data_dirs: if not os.path.exists(os.path.join(folder, space, "config.yaml")): continue data_dir = os.path.join(folder, space) diff --git a/tests/requirements.txt b/tests/requirements.txt index 4ec34f8a59e..b04b8c5a448 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -14,3 +14,4 @@ filelock==3.0.12 ply==3.10 pyyaml==5.4 fastcov==1.13 +deepdiff==5.7.0 diff --git a/tests/tck/features/optimizer/PrunePropertiesRule.feature b/tests/tck/features/optimizer/PrunePropertiesRule.feature index 33c686ab882..a9e7facd662 100644 --- a/tests/tck/features/optimizer/PrunePropertiesRule.feature +++ b/tests/tck/features/optimizer/PrunePropertiesRule.feature @@ -6,6 +6,8 @@ Feature: Prune Properties rule Background: Given a graph with space named "nba" + # The schema id is not fixed in standalone cluster, so we skip it + @distonly Scenario: Single Match When profiling query: """ @@ -17,13 +19,13 @@ Feature: Prune Properties rule | 42 | | 33 | | 41 | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 8 | Project | 4 | | - # | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"age\"],\"tagId\":2}]" } | - # | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":5}]" } | - # | 7 | IndexScan | 2 | | - # | 2 | Start | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"age\"],\"tagId\":9}]" } | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]->(v2) @@ -34,13 +36,13 @@ Feature: Prune Properties rule | "Tony Parker" | | "Tony Parker" | | "Tony Parker" | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 8 | Project | 4 | | - # | 4 | AppendVertices | 3 | { "props": } | - # | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\", \"age\"],\"tagId\":2}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":5}]" } | - # | 7 | IndexScan | 2 | | - # | 2 | Start | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"name\", \"age\", \"_tag\"]}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]"} | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]-(v2) @@ -56,13 +58,13 @@ Feature: Prune Properties rule | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 8 | Project | 4 | | - # | 4 | AppendVertices | 3 | { "props": } | - # | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\", \"age\"],\"tagId\":2}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":5}]" } | - # | 7 | IndexScan | 2 | | - # | 2 | Start | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"name\", \"age\", \"_tag\"]}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]" } | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\": [\"name\", \"age\", \"_tag\"], \"tagId\": 9}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]->(v2) @@ -73,13 +75,13 @@ Feature: Prune Properties rule | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | | ("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"}) | | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 8 | Project | 4 | | - # | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":3}, {\"props\":[\"name\", \"name\", \"age\", \"_tag\"],\"tagId\":2}, {\"props\":[\"name\", \"speciality\", \"_tag\"], \"tagId\":4}]" } | - # | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":5}]" } | - # | 7 | IndexScan | 2 | | - # | 2 | Start | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"], \"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | # The rule will not take affect in this case because it returns the whole path When executing query: """ @@ -106,13 +108,13 @@ Feature: Prune Properties rule | "like" | | "like" | | "like" | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 8 | Project | 4 | | - # | 4 | AppendVertices | 3 | { "props": } | - # | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":5}]" } | - # | 7 | IndexScan | 2 | | - # | 2 | Start | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"], \"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When executing query: """ MATCH (v:player{name: "Tony Parker"})-[:like]-(v2)--(v3) @@ -145,6 +147,8 @@ Feature: Prune Properties rule | ("Spurs" :team{name: "Spurs"}) | | ("Hornets" :team{name: "Hornets"}) | + # The schema id is not fixed in standalone cluster, so we skip it + @distonly Scenario: Multi Path Patterns When profiling query: """ @@ -165,22 +169,23 @@ Feature: Prune Properties rule | "Tim Duncan" | "Boris Diaw" | "Spurs" | | "Tim Duncan" | "Boris Diaw" | "Suns" | | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 15 | DataCollect | 16 | | - # | 16 | TopN | 12 | | - # | 12 | Project | 18 | | - # | 18 | Project | 17 | | - # | 17 | Filter | 9 | | - # | 9 | BiInnerJoin | 5, 8 | | - # | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 2 | Dedup | 1 | | - # | 1 | PassThrough | 3 | | - # | 3 | Start | | | - # | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":2,\"props\":[\"name\"]}, {\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":2,\"props\":[\"name\"]}]" } | - # | 6 | Argument | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 15 | DataCollect | 16 | | + | 16 | TopN | 12 | | + | 12 | Project | 17 | | + | 17 | Filter | 9 | | + | 9 | BiInnerJoin | 22, 23 | | + | 22 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 23 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}, {\"tagId\":10,\"props\":[\"name\"]}]" } | + | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | + | 6 | Argument | | | When profiling query: """ MATCH (m)-[]-(n), (n)-[]-(l), (l)-[]-(h) WHERE id(m)=="Tim Duncan" @@ -199,26 +204,32 @@ Feature: Prune Properties rule | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | + And the execution plan should be: + | id | name | dependencies | operator info | + | 19 | DataCollect | 20 | | + | 20 | TopN | 23 | | + | 23 | Project | 21 | | + | 21 | Filter | 13 | | + | 13 | BiInnerJoin | 9, 30 | | + | 9 | BiInnerJoin | 28, 29 | | + | 28 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 29 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | + | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | + | 6 | Argument | | | + | 31 | Start | | | + | 30 | Project | 12 | | + | 12 | AppendVertices | 11 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 11 | Traverse | 10 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":10}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | + | 10 | Argument | | | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 19 | DataCollect | 20 | | - # | 20 | TopN | 23 | | - # | 23 | Project | 21 | | - # | 21 | Filter | 13 | | - # | 13 | BiInnerJoin | 9, 12 | | - # | 9 | BiInnerJoin | 5, 8 | | - # | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 2 | Dedup | 1 | | - # | 1 | PassThrough | 3 | | - # | 3 | Start | | | - # | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":2,\"props\":[\"name\"]}, {\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":2,\"props\":[\"name\"]}]" } | - # | 6 | Argument | | | - # | 12 | AppendVertices | 11 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 11 | Traverse | 10 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":3}]" } | - # | 10 | Argument | | | + # The schema id is not fixed in standalone cluster, so we skip it + @distonly Scenario: Multi Match When profiling query: """ @@ -240,23 +251,24 @@ Feature: Prune Properties rule | "Tim Duncan" | "Boris Diaw" | "Spurs" | | "Tim Duncan" | "Boris Diaw" | "Suns" | | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 16 | DataCollect | 17 | | - # | 17 | TopN | 13 | | - # | 13 | Project | 12 | | - # | 12 | BiInnerJoin | 19, 11 | | - # | 19 | Project | 18 | | - # | 18 | Filter | 5 | | - # | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 2 | Dedup | 1 | | - # | 1 | PassThrough | 3 | | - # | 3 | Start | | | - # | 11 | Project | 10 | | - # | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":2,\"props\":[\"name\"]}, {\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":2,\"props\":[\"name\"]}]" } | - # | 8 | Argument | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 16 | DataCollect | 17 | | + | 17 | TopN | 13 | | + | 13 | Project | 12 | | + | 12 | BiInnerJoin | 19, 11 | | + | 19 | Project | 18 | | + | 18 | Filter | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 11 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}, {\"tagId\":10,\"props\":[\"name\"]}]" } | + | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | + | 8 | Argument | | | + | 33 | Start | | | When profiling query: """ MATCH (m)-[]-(n) WHERE id(m)=="Tim Duncan" @@ -276,27 +288,30 @@ Feature: Prune Properties rule | "Tim Duncan" | "Aron Baynes" | "Pistons" | "Grant Hill" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 20 | DataCollect | 21 | | - # | 21 | TopN | 17 | | - # | 17 | Project | 16 | | - # | 16 | BiInnerJoin | 23, 15 | | - # | 23 | Project | 22 | | - # | 22 | Filter | 5 | | - # | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 2 | Dedup | 1 | | - # | 1 | PassThrough | 3 | | - # | 3 | Start | | | - # | 15 | Project | 14 | | - # | 14 | BiInnerJoin | 10, 13 | | - # | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":2,\"props\":[\"name\"]}, {\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":2,\"props\":[\"name\"]}]" } | - # | 8 | Argument | | | - # | 13 | AppendVertices | 12 | { "props": "[{\"tagId\":2,\"props\":[\"name\"]}, {\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 12 | Traverse | 11 | { "vertexProps": "[{\"tagId\":3,\"props\":[\"name\"]}]" } | - # | 11 | Argument | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 20 | DataCollect | 21 | | + | 21 | TopN | 17 | | + | 17 | Project | 16 | | + | 16 | BiInnerJoin | 23, 14 | | + | 23 | Project | 22 | | + | 22 | Filter | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 14 | BiInnerJoin | 33, 34 | | + | 33 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | + | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | + | 8 | Argument | | | + | 35 | Start | | | + | 34 | Project | 13 | | + | 13 | AppendVertices | 12 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | + | 12 | Traverse | 11 | { "vertexProps": "[{\"tagId\":10,\"props\":[\"name\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -5}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 5}, {\"type\": -3, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -4}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | + | 11 | Argument | | | + | 36 | Start | | | When profiling query: """ MATCH (v:player{name:"Tony Parker"}) @@ -308,19 +323,21 @@ Feature: Prune Properties rule | o.player.name | | "Tim Duncan" | | "Tim Duncan" | + And the execution plan should be: + | id | name | dependencies | operator info | + | 10 | Project | 11 | | + | 11 | BiInnerJoin | 14, 9 | | + | 14 | Project | 3 | | + | 3 | AppendVertices | 12 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 12 | IndexScan | 2 | | + | 2 | Start | | | + | 9 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 7 | Traverse | 6 | { "vertexProps": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"start_year\", \"end_year\"]}, {\"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"likeness\"], \"type\": -3}, {\"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"start_year\", \"end_year\"], \"type\": -4}]" } | + | 6 | Argument | | | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 10 | Project | 11 | | - # | 11 | BiInnerJoin | 14, 9 | | - # | 14 | Project | 3 | | - # | 3 | AppendVertices | 12 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 12 | IndexScan | 2 | | - # | 2 | Start | | | - # | 9 | Project | 8 | | - # | 8 | AppendVertices | 7 | { "props": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":3}, {\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":2}, {\"props\":[\"name\", , \"speciality\", \"_tag\"],\"tagId\":4}, {\"props\":[\"id\", \"ts\", \"_tag\"],\"tagId\":6}]" } | - # | 7 | Traverse | 6 | { "vertexProps": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":3}, {\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":2}, {\"props\":[\"name\", , \"speciality\", \"_tag\"],\"tagId\":4}, {\"props\":[\"id\", \"ts\", \"_tag\"],\"tagId\":6}]" } | - # | 6 | Argument | | | + # The schema id is not fixed in standalone cluster, so we skip it + @distonly Scenario: Optional Match When profiling query: """ @@ -340,23 +357,23 @@ Feature: Prune Properties rule | "Tim Duncan" | "Manu Ginobili" | NULL | | "Tim Duncan" | "Manu Ginobili" | NULL | | "Tim Duncan" | "Manu Ginobili" | NULL | - # And the execution plan should be: - # | id | name | dependencies | operator info | - # | 16 | DataCollect | 17 | | - # | 17 | TopN | 13 | | - # | 13 | Project | 12 | | - # | 12 | BiLeftJoin | 19, 11 | | - # | 19 | Project | 18 | | - # | 18 | Filter | 5 | | - # | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 4 | Traverse | 2 | | - # | 2 | Dedup | 1 | | - # | 1 | PassThrough | 3 | | - # | 3 | Start | | | - # | 11 | Project | 10 | | - # | 10 | AppendVertices | 9 | { "props": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":3}, {\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":2}, {\"props\":[\"name\", , \"speciality\", \"_tag\"],\"tagId\":4}, {\"props\":[\"id\", \"ts\", \"_tag\"],\"tagId\":6}]" } | - # | 9 | Traverse | 8 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | - # | 8 | Argument | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 16 | DataCollect | 17 | | + | 17 | TopN | 13 | | + | 13 | Project | 12 | | + | 12 | BiLeftJoin | 19, 11 | | + | 19 | Project | 18 | | + | 18 | Filter | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 11 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 9 | Traverse | 8 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 8 | Argument | | | When profiling query: """ MATCH (m:player{name:"Tim Duncan"})-[:like]-(n)--() @@ -367,18 +384,17 @@ Feature: Prune Properties rule Then the result should be, in order: | scount | | 270 | - -# And the execution plan should be: -# | id | name | dependencies | operator info | -# | 12 | Aggregate | 13 | | -# | 13 | BiInnerJoin | 15, 11 | | -# | 15 | Project | 5 | | -# | 5 | AppendVertices | 4 | { "props": "[]" } | -# | 4 | Traverse | 3 | { "vertexProps": "" } | -# | 3 | Traverse | 14 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | -# | 14 | IndexScan | 2 | | -# | 2 | Start | | | -# | 11 | Project | 10 | | -# | 10 | AppendVertices | 9 | { "props": "[]" } | -# | 9 | Traverse | 8 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":2}]" } | -# | 8 | Argument | | | + And the execution plan should be: + | id | name | dependencies | operator info | + | 12 | Aggregate | 13 | | + | 13 | BiInnerJoin | 15, 11 | | + | 15 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 4 | Traverse | 3 | { "vertexProps": "" } | + | 3 | Traverse | 14 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 14 | IndexScan | 2 | | + | 2 | Start | | | + | 11 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 9 | Traverse | 8 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 8 | Argument | | |