Skip to content

Commit bae3e7f

Browse files
authored
Merge pull request #683 from FlorentinD/cypher-proj-empty-graph
Check for empty aggregation result
2 parents c0608bf + c849a66 commit bae3e7f

File tree

4 files changed

+24
-9
lines changed

4 files changed

+24
-9
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@
2222

2323
## Improvements
2424

25+
* Improved the error message if `gds.graph.project.cypher` produces an empty graph.
26+
2527

2628
## Other changes

graphdatascience/graph/graph_cypher_runner.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
from __future__ import annotations
2+
13
import re
24
from itertools import chain, zip_longest
3-
from typing import Any, Optional
5+
from typing import Any, Dict, Optional
6+
7+
from pandas import Series
48

59
from ..caller_base import CallerBase
610
from ..query_runner.query_runner import QueryRunner
@@ -41,7 +45,10 @@ def project(
4145

4246
GraphCypherRunner._verify_query_ends_with_return_clause(self._namespace, query)
4347

44-
result = self._query_runner.run_cypher(query, params, database, False).squeeze()
48+
result: Optional[Dict[str, Any]] = self._query_runner.run_cypher(query, params, database, False).squeeze()
49+
50+
if not result:
51+
raise ValueError("Projected graph cannot be empty.")
4552

4653
try:
4754
graph_name = str(result["graphName"])
@@ -50,7 +57,7 @@ def project(
5057
f"Invalid query, the query must end with the `RETURN {self._namespace}(...)` call: {query}"
5158
)
5259

53-
return GraphCreateResult(Graph(graph_name, self._query_runner, self._server_version), result)
60+
return GraphCreateResult(Graph(graph_name, self._query_runner, self._server_version), Series(data=result))
5461

5562
__separators = re.compile(r"[,(.]")
5663

graphdatascience/tests/integration/test_graph_ops.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ def test_cypher_projection(gds: GraphDataScience) -> None:
9494
assert result["exists"]
9595

9696

97+
@pytest.mark.filterwarnings("ignore: One of the labels in your query is not available in the database")
98+
def test_cypher_projection_empty_graph(gds: GraphDataScience) -> None:
99+
with pytest.raises(ValueError, match="Projected graph cannot be empty"):
100+
gds.graph.cypher.project("MATCH (n:MISSING_LABEL) RETURN gds.graph.project('some-graph', n, null)")
101+
102+
97103
def test_beta_project_subgraph(runner: QueryRunner, gds: GraphDataScience) -> None:
98104
from_G, _ = gds.graph.project(GRAPH_NAME, {"Node": {"properties": "x"}}, "*")
99105

graphdatascience/tests/unit/test_graph_cypher.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
1111
def test_simple(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
12-
runner.set__mock_result(DataFrame([{"graphName": "g", "don't squeeze": "me now"}]))
12+
runner.set__mock_result(DataFrame([[{"graphName": "g", "don't squeeze": "me now"}]]))
1313

1414
G, _ = gds.graph.cypher.project("MATCH (s)-->(t) RETURN gds.graph.project('g', s, t)")
1515

@@ -21,7 +21,7 @@ def test_simple(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
2121

2222
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
2323
def test_fstring(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
24-
runner.set__mock_result(DataFrame([{"graphName": "g", "don't squeeze": "me now"}]))
24+
runner.set__mock_result(DataFrame([[{"graphName": "g", "don't squeeze": "me now"}]]))
2525

2626
graph_name = "g"
2727
G, _ = gds.graph.cypher.project(f"MATCH (s)-->(t) RETURN gds.graph.project('{graph_name}', s, t)")
@@ -34,7 +34,7 @@ def test_fstring(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
3434

3535
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
3636
def test_expression(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
37-
runner.set__mock_result(DataFrame([{"graphName": "gg", "don't squeeze": "me now"}]))
37+
runner.set__mock_result(DataFrame([[{"graphName": "gg", "don't squeeze": "me now"}]]))
3838

3939
G, _ = gds.graph.cypher.project("WITH 'g' AS suffix MATCH (s)-->(t) RETURN gds.graph.project('g' + suffix, s, t)")
4040

@@ -46,7 +46,7 @@ def test_expression(runner: CollectingQueryRunner, gds: GraphDataScience) -> Non
4646

4747
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
4848
def test_with_parameter(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
49-
runner.set__mock_result(DataFrame([{"graphName": "g", "don't squeeze": "me now"}]))
49+
runner.set__mock_result(DataFrame([[{"graphName": "g", "don't squeeze": "me now"}]]))
5050

5151
G, _ = gds.graph.cypher.project("MATCH (s)-->(t) RETURN gds.graph.project($the_graph, s, t)", the_graph="g")
5252

@@ -58,7 +58,7 @@ def test_with_parameter(runner: CollectingQueryRunner, gds: GraphDataScience) ->
5858

5959
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
6060
def test_with_lots_of_whitespace(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
61-
runner.set__mock_result(DataFrame([{"graphName": "g", "don't squeeze": "me now"}]))
61+
runner.set__mock_result(DataFrame([[{"graphName": "g", "don't squeeze": "me now"}]]))
6262

6363
G, _ = gds.graph.cypher.project("MATCH (s)-->(t) RETURN gds .graph. project\n(\t'g' ,s, t)")
6464

@@ -70,7 +70,7 @@ def test_with_lots_of_whitespace(runner: CollectingQueryRunner, gds: GraphDataSc
7070

7171
@pytest.mark.parametrize("server_version", [ServerVersion(2, 4, 0)])
7272
def test_extracting_graph_name(runner: CollectingQueryRunner, gds: GraphDataScience) -> None:
73-
runner.set__mock_result(DataFrame([{"graphName": "the graph", "don't squeeze": "me now"}]))
73+
runner.set__mock_result(DataFrame([[{"graphName": "the graph", "don't squeeze": "me now"}]]))
7474

7575
G, _ = gds.graph.cypher.project("MATCH (s)-->(t) RETURN gds.graph.project('g', s, t)")
7676

0 commit comments

Comments
 (0)