fix(graph): resolve cited_id to papers.id via openalex_id JOIN

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 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-06-15 07:15:21 +02:00
parent a1a6f4590b
commit 664600fdc7
2 changed files with 30 additions and 1 deletions

View File

@@ -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"])

View File

@@ -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