|
11 | 11 | import pytest |
12 | 12 |
|
13 | 13 | import graphistry |
14 | | -from graphistry.compute.ast import n, e_forward |
| 14 | +from graphistry.compute.ast import n, e_forward, e_reverse |
| 15 | +from graphistry.compute.gfql.index.api import _engine_mismatch_reason |
15 | 16 | from graphistry.compute.gfql.index import ( |
16 | 17 | CreateIndex, DropIndex, ShowIndexes, index_op_from_json, parse_index_ddl, |
17 | 18 | get_registry, |
@@ -1180,3 +1181,63 @@ def spy(series, rows, eng): |
1180 | 1181 | monkeypatch.setenv("GFQL_INDEX_CANDIDATE_EDGE_MASK", "0") |
1181 | 1182 | gi.hop(nodes=seeds, **kwargs) |
1182 | 1183 | assert not seen, "OFF side still gathered candidate rows — the switch does nothing" |
| 1184 | + |
| 1185 | + |
| 1186 | +@pytest.mark.parametrize( |
| 1187 | + "index_kinds, edge, expected_kinds", |
| 1188 | + [ |
| 1189 | + (("edge_out_adj",), e_forward, "edge_out_adj"), |
| 1190 | + (("edge_in_adj",), e_reverse, "edge_in_adj"), |
| 1191 | + ], |
| 1192 | +) |
| 1193 | +@pytest.mark.parametrize( |
| 1194 | + "resident_engine, requested_engine", |
| 1195 | + [("pandas", "polars"), ("polars", "pandas")], |
| 1196 | +) |
| 1197 | +def test_explain_reports_bidirectional_engine_mismatch( |
| 1198 | + graph, index_kinds, edge, expected_kinds, resident_engine, requested_engine |
| 1199 | +): |
| 1200 | + """#1767: a valid foreign-engine index must explain its scan, not fail silently.""" |
| 1201 | + pytest.importorskip("polars") |
| 1202 | + gi = graph |
| 1203 | + for index_kind in index_kinds: |
| 1204 | + gi = gi.create_index(index_kind, engine=resident_engine) |
| 1205 | + shown = gi.show_indexes() |
| 1206 | + assert all(bool(valid) for valid in shown["valid"].tolist()) |
| 1207 | + |
| 1208 | + report = gi.gfql_explain( |
| 1209 | + [n({"id": 0}), edge(hops=1)], |
| 1210 | + index_policy="use", |
| 1211 | + engine=requested_engine, |
| 1212 | + ) |
| 1213 | + |
| 1214 | + assert report["used_index"] is False, report |
| 1215 | + assert report["decision_reason"] == ( |
| 1216 | + f"resident {expected_kinds} index " |
| 1217 | + f"engine={resident_engine}, requested engine={requested_engine} -> scan" |
| 1218 | + ), report |
| 1219 | + |
| 1220 | + |
| 1221 | +@pytest.mark.parametrize( |
| 1222 | + "resident_engine, requested_engine", |
| 1223 | + [("pandas", "polars"), ("polars", "pandas")], |
| 1224 | +) |
| 1225 | +def test_engine_mismatch_reason_reports_all_undirected_indexes( |
| 1226 | + graph, resident_engine, requested_engine |
| 1227 | +): |
| 1228 | + """The undirected diagnostic reports every valid resident foreign-engine index.""" |
| 1229 | + from graphistry.Engine import Engine |
| 1230 | + |
| 1231 | + pytest.importorskip("polars") |
| 1232 | + gi = graph |
| 1233 | + for index_kind in ("edge_out_adj", "edge_in_adj"): |
| 1234 | + gi = gi.create_index(index_kind, engine=resident_engine) |
| 1235 | + shown = gi.show_indexes() |
| 1236 | + assert all(bool(valid) for valid in shown["valid"].tolist()) |
| 1237 | + |
| 1238 | + reason = _engine_mismatch_reason(get_registry(gi), "undirected", Engine(requested_engine)) |
| 1239 | + |
| 1240 | + assert reason == ( |
| 1241 | + "resident edge_out_adj, edge_in_adj index " |
| 1242 | + f"engine={resident_engine}, requested engine={requested_engine} -> scan" |
| 1243 | + ), reason |
0 commit comments