Graph access via SPARQL
This commit is contained in:
@@ -5,6 +5,7 @@ from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI, HTTPException, Query
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from .graph_export import edge_retrieval_query, graph_from_sparql_bindings
|
||||
from .models import EdgesResponse, GraphResponse, NodesResponse, SparqlQueryRequest, StatsResponse
|
||||
from .rdf_store import RDFStore
|
||||
from .sparql_engine import AnzoGraphEngine, RdflibEngine, SparqlEngine, create_sparql_engine
|
||||
@@ -161,87 +162,13 @@ async def graph(
|
||||
) -> GraphResponse:
|
||||
sparql: SparqlEngine = app.state.sparql
|
||||
|
||||
if settings.graph_backend == "rdflib":
|
||||
store: RDFStore = app.state.store
|
||||
return GraphResponse(
|
||||
nodes=store.node_slice(offset=0, limit=node_limit),
|
||||
edges=store.edge_slice(offset=0, limit=edge_limit),
|
||||
)
|
||||
|
||||
# AnzoGraph mode: return a simple subgraph by pulling the first N triples.
|
||||
assert isinstance(sparql, AnzoGraphEngine)
|
||||
|
||||
edges_bnode_filter = "" if settings.include_bnodes else "FILTER(!isBlank(?s) && !isBlank(?o))"
|
||||
edges_q = f"""
|
||||
SELECT ?s ?p ?o
|
||||
WHERE {{
|
||||
?s ?p ?o .
|
||||
FILTER(!isLiteral(?o))
|
||||
FILTER(?p NOT IN (
|
||||
<http://www.w3.org/2000/01/rdf-schema#label>,
|
||||
<http://www.w3.org/2004/02/skos/core#prefLabel>,
|
||||
<http://www.w3.org/2004/02/skos/core#altLabel>
|
||||
))
|
||||
{edges_bnode_filter}
|
||||
}}
|
||||
LIMIT {edge_limit}
|
||||
"""
|
||||
|
||||
# Use SPARQL for graph export in BOTH modes so callers don't care which backend is in use.
|
||||
edges_q = edge_retrieval_query(edge_limit=edge_limit, include_bnodes=settings.include_bnodes)
|
||||
res = await sparql.query_json(edges_q)
|
||||
bindings = (((res.get("results") or {}).get("bindings")) or [])
|
||||
|
||||
node_id_by_key: dict[tuple[str, str], int] = {}
|
||||
node_meta: list[tuple[str, str]] = [] # (termType, iri)
|
||||
out_edges: list[dict[str, object]] = []
|
||||
|
||||
def _term_to_key_and_iri(term: dict[str, str]) -> tuple[tuple[str, str], tuple[str, str]] | None:
|
||||
t = term.get("type")
|
||||
v = term.get("value")
|
||||
if not t or v is None:
|
||||
return None
|
||||
if t == "literal":
|
||||
return None
|
||||
if t == "bnode" and not settings.include_bnodes:
|
||||
return None
|
||||
if t == "bnode":
|
||||
return (("bnode", v), ("bnode", f"_:{v}"))
|
||||
# Default to "uri".
|
||||
return (("uri", v), ("uri", v))
|
||||
|
||||
def _get_or_add(term: dict[str, str]) -> int | None:
|
||||
out = _term_to_key_and_iri(term)
|
||||
if out is None:
|
||||
return None
|
||||
key, meta = out
|
||||
existing = node_id_by_key.get(key)
|
||||
if existing is not None:
|
||||
return existing
|
||||
if len(node_meta) >= node_limit:
|
||||
return None
|
||||
nid = len(node_meta)
|
||||
node_id_by_key[key] = nid
|
||||
node_meta.append(meta)
|
||||
return nid
|
||||
|
||||
for b in bindings:
|
||||
s_term = b.get("s") or {}
|
||||
o_term = b.get("o") or {}
|
||||
p_term = b.get("p") or {}
|
||||
|
||||
sid = _get_or_add(s_term)
|
||||
oid = _get_or_add(o_term)
|
||||
if sid is None or oid is None:
|
||||
continue
|
||||
|
||||
pred = p_term.get("value")
|
||||
if not pred:
|
||||
continue
|
||||
|
||||
out_edges.append({"source": sid, "target": oid, "predicate": pred})
|
||||
|
||||
out_nodes = [
|
||||
{"id": i, "termType": term_type, "iri": iri, "label": None}
|
||||
for i, (term_type, iri) in enumerate(node_meta)
|
||||
]
|
||||
|
||||
return GraphResponse(nodes=out_nodes, edges=out_edges)
|
||||
nodes, edges = graph_from_sparql_bindings(
|
||||
bindings,
|
||||
node_limit=node_limit,
|
||||
include_bnodes=settings.include_bnodes,
|
||||
)
|
||||
return GraphResponse(nodes=nodes, edges=edges)
|
||||
|
||||
Reference in New Issue
Block a user