Add E2E test for optimizing merge on supernodes
This commit is contained in:
@@ -750,7 +750,7 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
|||||||
auto maybe_label = FindBestLabelIndex(labels);
|
auto maybe_label = FindBestLabelIndex(labels);
|
||||||
if (!maybe_label) return nullptr;
|
if (!maybe_label) return nullptr;
|
||||||
const auto &label = *maybe_label;
|
const auto &label = *maybe_label;
|
||||||
if (max_vertex_count && db_->VerticesCount(GetLabel(label)) > *max_vertex_count) {
|
if (max_vertex_count && *max_vertex_count >= 1 && db_->VerticesCount(GetLabel(label)) > *max_vertex_count) {
|
||||||
// Don't create an indexed lookup, since we have more labeled vertices
|
// Don't create an indexed lookup, since we have more labeled vertices
|
||||||
// than the allowed count.
|
// than the allowed count.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from common import connect, execute_and_fetch_all
|
from common import connect, execute_and_fetch_all
|
||||||
|
from gqlalchemy import Memgraph
|
||||||
|
|
||||||
# E2E tests for checking query semantic
|
# E2E tests for checking query semantic
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
@@ -278,5 +279,58 @@ def test_same_avg_group_size_diff_distribution(connect):
|
|||||||
execute_and_fetch_all(cursor, "DROP INDEX ON :Label(id2);")
|
execute_and_fetch_all(cursor, "DROP INDEX ON :Label(id2);")
|
||||||
|
|
||||||
|
|
||||||
|
def test_given_supernode_when_expanding_then_expand_other_way_around():
|
||||||
|
memgraph = Memgraph()
|
||||||
|
|
||||||
|
memgraph.execute("FOREACH (i in range(1, 20000) | CREATE (:Node {id: i}));")
|
||||||
|
memgraph.execute("CREATE (:SuperNode {id: 1});")
|
||||||
|
memgraph.execute("CREATE INDEX ON :SuperNode(id);")
|
||||||
|
memgraph.execute("CREATE INDEX ON :SuperNode;")
|
||||||
|
memgraph.execute("CREATE INDEX ON :Node(id);")
|
||||||
|
memgraph.execute("CREATE INDEX ON :Node;")
|
||||||
|
memgraph.execute("match (n:Node) match (s:SuperNode {id: 1}) merge (n)<-[:HAS_REL_TO]-(s);")
|
||||||
|
|
||||||
|
result_without_analysis = list(
|
||||||
|
memgraph.execute_and_fetch("explain match (n:Node) match (s:SuperNode {id: 1}) merge (n)<-[:HAS_REL_TO]-(s);")
|
||||||
|
)
|
||||||
|
result_without_analysis = [x["QUERY PLAN"] for x in result_without_analysis]
|
||||||
|
expected_explain_before_analysis = [
|
||||||
|
f" * EmptyResult",
|
||||||
|
f" * Merge",
|
||||||
|
f" |\\ On Match",
|
||||||
|
f" | * Expand (s)-[anon3:HAS_REL_TO]->(n)",
|
||||||
|
f" | * Once",
|
||||||
|
f" |\\ On Create",
|
||||||
|
f" | * CreateExpand (n)<-[anon3:HAS_REL_TO]-(s)",
|
||||||
|
f" | * Once",
|
||||||
|
f" * ScanAllByLabel (n :Node)",
|
||||||
|
f" * ScanAllByLabelPropertyValue (s :SuperNode {{id}})",
|
||||||
|
f" * Once",
|
||||||
|
]
|
||||||
|
|
||||||
|
memgraph.execute("analyze graph;")
|
||||||
|
|
||||||
|
result_with_analysis = list(
|
||||||
|
memgraph.execute_and_fetch("explain match (n:Node) match (s:SuperNode {id: 1}) merge (n)<-[:HAS_REL_TO]-(s);")
|
||||||
|
)
|
||||||
|
result_with_analysis = [x["QUERY PLAN"] for x in result_with_analysis]
|
||||||
|
expected_explain_after_analysis = [
|
||||||
|
f" * EmptyResult",
|
||||||
|
f" * Merge",
|
||||||
|
f" |\\ On Match",
|
||||||
|
f" | * Expand (n)<-[anon3:HAS_REL_TO]-(s)",
|
||||||
|
f" | * Once",
|
||||||
|
f" |\\ On Create",
|
||||||
|
f" | * CreateExpand (n)<-[anon3:HAS_REL_TO]-(s)",
|
||||||
|
f" | * Once",
|
||||||
|
f" * ScanAllByLabel (n :Node)",
|
||||||
|
f" * ScanAllByLabelPropertyValue (s :SuperNode {{id}})",
|
||||||
|
f" * Once",
|
||||||
|
]
|
||||||
|
|
||||||
|
assert expected_explain_before_analysis == result_without_analysis
|
||||||
|
assert expected_explain_after_analysis == result_with_analysis
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(pytest.main([__file__, "-rA"]))
|
sys.exit(pytest.main([__file__, "-rA"]))
|
||||||
|
|||||||
@@ -175,8 +175,8 @@ startup_config_dict = {
|
|||||||
"query_cost_planner": ("true", "true", "Use the cost-estimating query planner."),
|
"query_cost_planner": ("true", "true", "Use the cost-estimating query planner."),
|
||||||
"query_plan_cache_ttl": ("60", "60", "Time to live for cached query plans, in seconds."),
|
"query_plan_cache_ttl": ("60", "60", "Time to live for cached query plans, in seconds."),
|
||||||
"query_vertex_count_to_expand_existing": (
|
"query_vertex_count_to_expand_existing": (
|
||||||
"10",
|
"-1",
|
||||||
"10",
|
"-1",
|
||||||
"Maximum count of indexed vertices which provoke indexed lookup and then expand to existing, instead of a regular expand. Default is 10, to turn off use -1.",
|
"Maximum count of indexed vertices which provoke indexed lookup and then expand to existing, instead of a regular expand. Default is 10, to turn off use -1.",
|
||||||
),
|
),
|
||||||
"query_max_plans": ("1000", "1000", "Maximum number of generated plans for a query."),
|
"query_max_plans": ("1000", "1000", "Maximum number of generated plans for a query."),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ PIP_DEPS=(
|
|||||||
"pyyaml==5.4.1"
|
"pyyaml==5.4.1"
|
||||||
"six==1.15.0"
|
"six==1.15.0"
|
||||||
"networkx==2.4"
|
"networkx==2.4"
|
||||||
|
"gqlalchemy==1.3.3"
|
||||||
)
|
)
|
||||||
cd "$DIR"
|
cd "$DIR"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user