feat(discover,provenance): graph discovery queries + @cite scan + code_links + bib export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
64
codex/discover.py
Normal file
64
codex/discover.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Discovery queries over the citation graph stored in PostgreSQL."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from codex.db import get_conn
|
||||
|
||||
|
||||
def discovery_leads(limit: int = 20) -> list[dict[str, int | str]]:
|
||||
"""Return papers referenced by already-ingested papers but not yet collected.
|
||||
|
||||
Returns list of dicts with keys:
|
||||
cited_id (str) — arXiv ID, DOI, or OpenAlex W-ID of the target
|
||||
pull (int) — how many already-ingested papers cite this target
|
||||
|
||||
Ordered by pull DESC, limited to `limit` rows.
|
||||
"""
|
||||
sql = """
|
||||
SELECT cited_id, count(*) AS pull
|
||||
FROM citations
|
||||
WHERE cited_id NOT IN (SELECT id FROM papers)
|
||||
GROUP BY cited_id
|
||||
ORDER BY pull DESC
|
||||
LIMIT %(limit)s
|
||||
"""
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(sql, {"limit": limit}).fetchall()
|
||||
return [{"cited_id": row["cited_id"], "pull": row["pull"]} for row in rows]
|
||||
|
||||
|
||||
def citing_papers(paper_id: str) -> list[str]:
|
||||
"""Return IDs of all papers that cite `paper_id` (in-graph reverse citations)."""
|
||||
sql = "SELECT citing_id FROM citations WHERE cited_id = %(paper_id)s"
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(sql, {"paper_id": paper_id}).fetchall()
|
||||
return [row["citing_id"] for row in rows]
|
||||
|
||||
|
||||
def cited_by(paper_id: str) -> list[str]:
|
||||
"""Return IDs of all papers that `paper_id` cites (forward citations)."""
|
||||
sql = "SELECT cited_id FROM citations WHERE citing_id = %(paper_id)s"
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(sql, {"paper_id": paper_id}).fetchall()
|
||||
return [row["cited_id"] for row in rows]
|
||||
|
||||
|
||||
def cocited_papers(paper_id: str, limit: int = 10) -> list[dict[str, int | str]]:
|
||||
"""Return papers frequently co-cited with `paper_id`.
|
||||
|
||||
A paper X is co-cited with `paper_id` if some paper cites both.
|
||||
Returns list of dicts: {paper_id: str, co_citations: int}, ordered DESC.
|
||||
"""
|
||||
sql = """
|
||||
SELECT c2.cited_id AS paper_id, count(*) AS co_citations
|
||||
FROM citations c1
|
||||
JOIN citations c2 ON c1.citing_id = c2.citing_id
|
||||
WHERE c1.cited_id = %(paper_id)s
|
||||
AND c2.cited_id != %(paper_id)s
|
||||
GROUP BY c2.cited_id
|
||||
ORDER BY co_citations DESC
|
||||
LIMIT %(limit)s
|
||||
"""
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(sql, {"paper_id": paper_id, "limit": limit}).fetchall()
|
||||
return [{"paper_id": row["paper_id"], "co_citations": row["co_citations"]} for row in rows]
|
||||
Reference in New Issue
Block a user