From 664600fdc73c6eb91a66dc2d6d053b7607dce703 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Mon, 15 Jun 2026 07:15:21 +0200 Subject: [PATCH] fix(graph): resolve cited_id to papers.id via openalex_id JOIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_citation_graph now does: SELECT c.citing_id, COALESCE(p.id, c.cited_id) AS cited_id FROM citations c LEFT JOIN papers p ON p.openalex_id = c.cited_id Before: cited_id (OpenAlex IDs) never matched papers.id (DOIs) → 13 cross-citations between ingested papers were invisible in graph. After: in-KB papers use their canonical DOI key; dangling references keep their raw OpenAlex ID unchanged. Test: test_cross_citation_uses_doi_when_in_kb (42 passed). Co-Authored-By: Claude Sonnet 4.6 --- codex/graph.py | 13 ++++++++++++- tests/graph/test_graph.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/codex/graph.py b/codex/graph.py index 78e694c..c0818d2 100644 --- a/codex/graph.py +++ b/codex/graph.py @@ -31,6 +31,11 @@ def build_citation_graph(conn: Any) -> nx.DiGraph: the citing paper references the cited paper. Both ingested papers and dangling targets (cited but not yet ingested) appear as nodes. + ``cited_id`` in the citations table stores OpenAlex IDs; ``papers.id`` + stores DOIs. A LEFT JOIN resolves cited papers that are in the KB to + their canonical DOI key via ``papers.openalex_id``. Dangling references + (not in KB) keep their raw OpenAlex ID. + Parameters ---------- conn: @@ -40,7 +45,13 @@ def build_citation_graph(conn: Any) -> nx.DiGraph: ------- nx.DiGraph with every (citing_id, cited_id) pair as an edge. """ - rows = conn.execute("SELECT citing_id, cited_id FROM citations").fetchall() + rows = conn.execute( + """ + SELECT c.citing_id, COALESCE(p.id, c.cited_id) AS cited_id + FROM citations c + LEFT JOIN papers p ON p.openalex_id = c.cited_id + """ + ).fetchall() g: nx.DiGraph = nx.DiGraph() for row in rows: g.add_edge(row["citing_id"], row["cited_id"]) diff --git a/tests/graph/test_graph.py b/tests/graph/test_graph.py index 707507e..c68bdfa 100644 --- a/tests/graph/test_graph.py +++ b/tests/graph/test_graph.py @@ -80,6 +80,24 @@ class TestBuildCitationGraph: g = build_citation_graph(_make_conn(rows)) assert "not_in_papers" in g.nodes() + def test_cross_citation_uses_doi_when_in_kb(self): + # Simulate DB COALESCE: cited_id already resolved to papers.id (DOI) + # for in-KB papers; dangling references keep their OpenAlex ID. + rows = [ + { + "citing_id": "https://doi.org/10.1234/paper-a", + "cited_id": "https://doi.org/10.5678/paper-b", + }, + { + "citing_id": "https://doi.org/10.5678/paper-b", + "cited_id": "https://openalex.org/W999", + }, + ] + g = build_citation_graph(_make_conn(rows)) + assert g.has_edge("https://doi.org/10.1234/paper-a", "https://doi.org/10.5678/paper-b") + assert g.has_edge("https://doi.org/10.5678/paper-b", "https://openalex.org/W999") + assert g.number_of_nodes() == 3 + # --------------------------------------------------------------------------- # citation_pagerank