Skip to content

Commit bfafe1a

Browse files
committed
test(gfql/cypher): amplify #1395 sequential MATCH coverage
1 parent b2f385d commit bfafe1a

1 file changed

Lines changed: 92 additions & 3 deletions

File tree

graphistry/tests/compute/gfql/cypher/test_lowering.py

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14302,8 +14302,7 @@ def test_issue_996_case_r_when_null_searched_form_still_works() -> None:
1430214302
# Issue #1395: sequential MATCH reply-author row-shaping joins (IC8 / IS7)
1430314303
# ---------------------------------------------------------------------------
1430414304

14305-
def test_issue_1395_sequential_match_recent_replies_row_shaping_ic8() -> None:
14306-
"""IC8 shape: two non-optional MATCH clauses preserve reply-author projection fields."""
14305+
def _mk_issue_1395_reply_author_ic8_graph(*, cudf_mode: bool = False) -> _CypherTestGraph:
1430714306
nodes = pd.DataFrame({
1430814307
"id": [
1430914308
"viewer",
@@ -14337,7 +14336,17 @@ def test_issue_1395_sequential_match_recent_replies_row_shaping_ic8() -> None:
1433714336
"HAS_CREATOR",
1433814337
],
1433914338
})
14340-
g = _mk_graph(nodes, edges)
14339+
if cudf_mode:
14340+
pytest.importorskip("cudf")
14341+
import cudf # type: ignore
14342+
nodes = cudf.DataFrame.from_pandas(nodes)
14343+
edges = cudf.DataFrame.from_pandas(edges)
14344+
return _mk_graph(nodes, edges)
14345+
14346+
14347+
def test_issue_1395_sequential_match_recent_replies_row_shaping_ic8() -> None:
14348+
"""IC8 shape: two non-optional MATCH clauses preserve reply-author projection fields."""
14349+
g = _mk_issue_1395_reply_author_ic8_graph()
1434114350

1434214351
query = (
1434314352
"MATCH (:Person {id: $personId})<-[:HAS_CREATOR]-(message:Message) "
@@ -14374,6 +14383,86 @@ def test_issue_1395_sequential_match_recent_replies_row_shaping_ic8() -> None:
1437414383
]
1437514384

1437614385

14386+
def test_issue_1395_sequential_match_recent_replies_row_shaping_ic8_on_cudf() -> None:
14387+
"""cuDF parity: IC8 sequential MATCH row-shaping stays aligned on GPU engine."""
14388+
g = _mk_issue_1395_reply_author_ic8_graph(cudf_mode=True)
14389+
14390+
query = (
14391+
"MATCH (:Person {id: $personId})<-[:HAS_CREATOR]-(message:Message) "
14392+
"MATCH (message)<-[:REPLY_OF]-(comment:Comment)-[:HAS_CREATOR]->(commentAuthor:Person) "
14393+
"RETURN "
14394+
"commentAuthor.id AS commentAuthorId, "
14395+
"commentAuthor.firstName AS commentAuthorFirstName, "
14396+
"commentAuthor.lastName AS commentAuthorLastName, "
14397+
"comment.creationDate AS commentCreationDate, "
14398+
"comment.id AS commentId, "
14399+
"comment.content AS commentContent "
14400+
"ORDER BY commentCreationDate DESC, commentId ASC "
14401+
"LIMIT 20"
14402+
)
14403+
result = g.gfql(query, params={"personId": "viewer"}, engine="cudf")
14404+
14405+
assert type(result._nodes).__module__.startswith("cudf")
14406+
assert result._nodes.to_pandas().to_dict(orient="records") == [
14407+
{
14408+
"commentAuthorId": "author1",
14409+
"commentAuthorFirstName": "Ann",
14410+
"commentAuthorLastName": "One",
14411+
"commentCreationDate": 110.0,
14412+
"commentId": "c1",
14413+
"commentContent": "reply-1",
14414+
},
14415+
{
14416+
"commentAuthorId": "author2",
14417+
"commentAuthorFirstName": "Bob",
14418+
"commentAuthorLastName": "Two",
14419+
"commentCreationDate": 105.0,
14420+
"commentId": "c2",
14421+
"commentContent": "reply-2",
14422+
},
14423+
]
14424+
14425+
14426+
def test_issue_1395_sequential_match_where_boundary_lock_ic8() -> None:
14427+
"""Boundary lock: intermediate WHERE stays explicitly unsupported for sequential MATCH merge."""
14428+
g = _mk_issue_1395_reply_author_ic8_graph()
14429+
14430+
query = (
14431+
"MATCH (:Person {id: $personId})<-[:HAS_CREATOR]-(message:Message) "
14432+
"WHERE message.creationDate >= 95 "
14433+
"MATCH (message)<-[:REPLY_OF]-(comment:Comment)-[:HAS_CREATOR]->(commentAuthor:Person) "
14434+
"RETURN comment.id AS commentId, commentAuthor.id AS commentAuthorId "
14435+
"ORDER BY commentId"
14436+
)
14437+
with pytest.raises(
14438+
GFQLValidationError,
14439+
match="WHERE on intermediate MATCH clauses is not yet supported for sequential MATCH merge",
14440+
):
14441+
_ = g.gfql(query, params={"personId": "viewer"})
14442+
14443+
14444+
def test_issue_1395_sequential_match_equivalent_to_single_match_comma_pattern() -> None:
14445+
"""Shape equivalence: sequential MATCH and single-MATCH comma pattern return identical rows."""
14446+
g = _mk_issue_1395_reply_author_ic8_graph()
14447+
14448+
query_sequential = (
14449+
"MATCH (:Person {id: $personId})<-[:HAS_CREATOR]-(message:Message) "
14450+
"MATCH (message)<-[:REPLY_OF]-(comment:Comment)-[:HAS_CREATOR]->(commentAuthor:Person) "
14451+
"RETURN comment.id AS commentId, commentAuthor.id AS commentAuthorId "
14452+
"ORDER BY commentId"
14453+
)
14454+
query_single_match = (
14455+
"MATCH (:Person {id: $personId})<-[:HAS_CREATOR]-(message:Message), "
14456+
"(message)<-[:REPLY_OF]-(comment:Comment)-[:HAS_CREATOR]->(commentAuthor:Person) "
14457+
"RETURN comment.id AS commentId, commentAuthor.id AS commentAuthorId "
14458+
"ORDER BY commentId"
14459+
)
14460+
14461+
seq_rows = g.gfql(query_sequential, params={"personId": "viewer"})._nodes.to_dict(orient="records")
14462+
comma_rows = g.gfql(query_single_match, params={"personId": "viewer"})._nodes.to_dict(orient="records")
14463+
assert seq_rows == comma_rows
14464+
14465+
1437714466
def test_issue_1395_sequential_match_message_replies_row_shaping_is7() -> None:
1437814467
"""IS7 shape: sequential MATCH keeps comment + replyAuthor + messageAuthor rows aligned."""
1437914468
nodes = pd.DataFrame({

0 commit comments

Comments
 (0)