Clément Corbin software engineering / data / knowledge

Walking the IdRef authority graph

IdRef is normally experienced as a lookup system. Search for a person, an organization, a publication; get an authority record; read its fields; follow a link if you need somewhere else.

But structurally, IdRef is already a graph. It contains millions of authority records for people, organizations, conferences, places, concepts and works. Those records are connected through identifiers, bibliographic relationships, affiliations, roles and links to other datasets. A person's record can lead to the organizations they belonged to and the publications they contributed to; those publications lead to other contributors; an organization's record leads back to its members.

The information is there. What is missing is a way to experience it as a structure rather than as a sequence of individual records: Academic Graph Explorer is an attempt to walk that structure.

Screenshot of the Academic Graph Explorer

The domain first

IdRef (Identifiants et Référentiels) is the French national authority file for higher education and research, maintained by ABES, the agency behind the shared cataloguing infrastructure of French university libraries. An authority record exists to solve a problem that is old but never quite closed: the same person, institution or work appears under different names in different catalogues, and you need a stable identifier that lets those references converge on the same entity.

An IdRef record therefore does more than identify something. It describes it: names, dates, biographical information, fields of activity and other attributes. It disambiguates it: distinguishing one Jean Martin from another Jean Martin. It aggregates information around it: publications, affiliations, roles and other relationships recorded by cataloguing systems. And it links out to other entities.

That last part is what interested me. The normal interface makes those links feel incidental. You look up a person, see their affiliations, click an organization, arrive at another record, and continue one hop at a time. The graph is always present but mostly invisible.

So I started with a simple question: What happens if you treat an authority file as a graph you can walk, rather than a collection of records you look up?

The first attempt: connect everything

The first prototype approached the problem from the opposite direction: Rather than relying on a single source, I tried to assemble the largest scholarly graph I could by federating several Linked Data sources: IdRef, HAL, Persée and SUDOC. This was possible because the data is exposed as RDF and queryable through SPARQL. A single query could cross from one dataset into another, follow identifiers, resolve equivalent entities and collect relationships from several infrastructures. It was technically satisfying but it was also the wrong direction.

Every additional source brought useful information, but also another ontology, another identifier system, another set of assumptions and another way for a query to fail. The resulting graph was larger, but its semantics became harder to reason about. Was a relationship meaningful because it existed in the underlying data, or because several sources happened to expose compatible-looking predicates? Was an entity the same entity across datasets, or merely something that could be connected through owl:sameAs?

The more I tried to make the graph comprehensive, the less clear the object I was actually exploring became. So I removed the other three sources and kept IdRef. That turned out to be a much more productive constraint. The goal was not to build the largest possible scholarly graph. It was to understand and expose one coherent graph whose structure already had meaning.

Linked Data as an interface to the graph

IdRef is part of the Linked Data ecosystem. Its entities have persistent HTTP identifiers; their descriptions are available as RDF; relationships are represented as triples and can be queried with SPARQL.

That matters because the application does not need to own the underlying dataset: Academic Graph Explorer queries IdRef directly as the user explores it. There is no imported copy of the authority file sitting behind the application, no precomputed graph database and no locally maintained representation of the whole dataset. The application is a window onto the source.

That choice has obvious costs. Remote queries introduce latency. The endpoint can be slow or unavailable. There is no local index to optimize everything around the application's needs. Large traversals have to be bounded. But it also preserves something important: the graph remains the graph of the source. The application does not have to decide in advance which relationships matter, flatten the RDF into a convenient relational model, or construct a private interpretation of the dataset. It asks the source what is connected to what, and translates the answer into something that can be explored.

That became one of the project's underlying principles: when the interesting thing is the structure of the data itself, it is worth resisting the temptation to replace that structure with an application-specific one too early.

The publication is the hinge

A conventional social-network representation would probably create an edge directly between two researchers:

Person A ── co-author ── Person B

IdRef does not contain that edge. Instead, it contains something closer to:

Person A ── author ── Publication ── author ── Person B

The publication is the hinge: Co-authorship is something you infer by walking the graph, not necessarily a relationship stored as a first-class edge. That distinction ended up shaping the traversal engine. A graph explorer should not only ask "which nodes are connected to this node?" It needs to understand what kind of path produced that connection. The publication is not just another node along the way. It is part of the explanation for why two people appear in the same neighborhood.

The semantic model behind the graph

The application translates RDF triples into a navigable structure, but that translation is only as good as the underlying semantics. IdRef's model is coherent but not always tidy. To understand what the edges actually mean, you have to look at which predicates are used, how entities are typed, and where the model is explicit or implicit. The backend does not replace those semantics; it maps them.

Identity and typing

Every node is identified by its IdRef URI, of the form http://www.idref.fr/{PPN}/id. Raw PPNs are normalized to this form before any query is issued. That URI is the entity's only stable anchor.

Types are straightforward for people and organizations: they carry rdf:type assertions of foaf:Person and foaf:Organization respectively. Publications are different. IdRef does not assign them an explicit class in the data I query. Instead, the backend recognizes a publication by the presence of dcterms:bibliographicCitation. This is a deliberate narrowing: the application's model is a hard-coded three-way discriminator (person, organization, publication) that is smaller and more regular than the source ontology. I did not try to model the full range of IdRef entity types; I needed a structure the traversal engine could navigate without guessing.

The vocabularies in use

The SPARQL queries draw on a handful of standard vocabularies:

VocabularyPrefixWhat we use
SKOSskos:prefLabel for names and titles; note for biographical notes
FOAFfoaf:Person, Organization
Dublin Core Termsdcterms:bibliographicCitation for publication titles
BIBObibo:doi, uri
ORGorg:memberOf for institutional affiliation
OWLowl:sameAs for equivalence links to other datasets
RDFSrdfs:label
MARC Relatorsmarcrel:The predicates themselves, representing bibliographic roles

Relators as predicates

In a typical application ontology, you might define a fixed predicate like :author or :contributor. IdRef does not do this. The bibliographic role is the predicate, drawn from the MARC relator vocabulary (http://id.loc.gov/vocabulary/relators/). A person is connected to a document not by a generic relationship, but by a specific relator such as marcrel:aut (author), marcrel:trl (translator), or marcrel:edt (editor).

This means the queries cannot hard-code the predicate. They have to use a variable:

PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT DISTINCT ?title ?role ?doc ?contributor ?contributor_name ?contributor_role WHERE {
  ?doc ?relator $person; dcterms:bibliographicCitation ?title.
  ?relator skos:prefLabel ?role.
  ?doc ?person_relator ?contributor .
  ?contributor a foaf:Person .
  OPTIONAL {
    ?contributor skos:prefLabel ?contributor_name .
    ?person_relator skos:prefLabel ?contributor_role .
  }
} GROUP BY ?doc

The role is recovered by querying the relator's own label (?relator skos:prefLabel ?role). The edge type is therefore dynamic, read directly from the source vocabulary, not imposed by the application. When the interface shows that someone is a "translator" rather than a generic "contributor," that label comes from the authority file itself.

From variable predicates to typed relationships

The backend translates these query results into a small application-level Relationship model with four fields: source, target, type, and source_dataset. The type field is a free-form string, deliberately not a closed enum, because the role is relayed directly from the source. I wanted the application to inherit the bibliographic semantics of the authority file rather than flattening them into a private taxonomy.

Not every query returns a readable role label. When the source vocabulary does not provide one, the traversal engine falls back to a generic type:

  • Person → Organization: affiliatedWith
  • Person → Publication: authorOf
  • Person → Person (co-contributor via a shared document): contributor
  • Organization → Person: memberOf
  • Organization → Publication: produced

These are pragmatic substitutions. They keep the graph navigable without pretending that every edge has been fully semantically annotated by the source.

Two families of edges

The graph contains two distinct kinds of relationships. The first is institutional: org:memberOf links a person to an organization. This is returned inline by the person detail query; there is no separate round trip to discover it.

The second is bibliographic: MARC relator predicates link documents to people and organizations, with roles recovered from the relator's own label. These require variable-predicate queries and produce the more varied edge types.

This distinction is what makes the shape of the graph depend on which paths you choose to traverse. Expanding affiliations produces a different neighborhood from expanding publications, not just because the nodes differ, but because the underlying semantics are drawn from different vocabularies.

The publication as hinge, formalized

The earlier observation that co-authorship is a path rather than an edge has a direct expression in the triples. IdRef does not store a "co-author" relationship. It stores two separate bibliographic contributions:

# The publication is the subject; the relator points at each author:
<idref.fr/253920481/id> marcrel:aut <idref.fr/121375307/id> .
<idref.fr/253920481/id> marcrel:aut <idref.fr/089365823/id> .
# => Person A ←aut— Publication —aut→ Person B

The traversal engine materializes a direct contributor edge between two people only by walking through the shared document. The source_key="doc" mechanism in the traversal logic records that this edge was inferred via a publication node, preserving the path semantics even when the visualization collapses it into a single link.

Finally, owl:sameAs links IdRef entities to equivalent records in other datasets. The publication query returns ?sameAs alongside bibo:uri and bibo:doi. This was the seam that let the original multi-source prototype attempt to federate IdRef with HAL, Persée and SUDOC: follow owl:sameAs, cross into another dataset, continue the traversal. Each source brought its own ontology, its own relator vocabulary, its own way of naming things. Keeping the project within IdRef meant giving up that federation, but it also meant keeping the semantics coherent.

Walking the graph

The application performs bounded breadth-first traversals over the live SPARQL graph: Starting from a person, organization or publication, it resolves the entity and expands its relationships. The process continues outward up to a configurable depth and within limits on the number of nodes and edges returned. Those limits are necessary. A scholarly graph has the same basic problem as any large graph: unrestricted expansion quickly stops being an exploration and becomes a data dump. The interface therefore works with neighborhoods rather than complete graphs: Start with one entity. Expand it. See what surrounds it. Follow a publication to its contributors. Follow an organization to its members. Move from one part of the graph to another.

The visible graph is always partial, and deliberately so. There is an important consequence to this approach. A truncated graph should not pretend to be complete. When a traversal reaches its limits while there are still unexplored entities in the frontier, the backend keeps enough state to resume the traversal later. The client can request another chunk and extend the neighborhood without starting again or duplicating nodes and edges. The mechanism is relatively mundane. The conceptual point is not: the graph presented to the user is a bounded view onto a potentially much larger structure, and the application should make that incompleteness explicit rather than hiding it.

A thin translation layer

The architecture follows from the same constraint: The backend talks to SPARQL; the frontend does not.

The source data is RDF, but the client does not need to understand RDF or SPARQL. The backend translates it into a small application-level model: entities, relationships and neighborhoods. That model is intentionally narrower than the source. A person is a person. An organization is an organization. A publication is a publication. A relationship has a source, a target, a type and a source dataset. A neighborhood contains the nodes and edges discovered during a traversal. This gives the frontend something stable to work with without pretending that the application's model is the underlying knowledge graph.

The implementation is Python and FastAPI on the backend, React and TypeScript on the frontend, with D3 handling the force-directed layout. SPARQL queries are kept separate from the traversal logic, so the traversal engine is concerned with exploring a graph rather than with the mechanics of a particular query endpoint. There is also some unavoidable engineering around a remote graph: responses are cached, individual failures are allowed to degrade a traversal rather than bringing down the whole request, and remote queries are retried when appropriate. None of that is particularly novel. It is the infrastructure required to make the conceptual experiment behave like an actual piece of software.

What the graph makes visible

The interesting results are less about the visualization itself than about what becomes apparent when the data is treated as a navigable structure.

Relationships are often paths rather than edges

The graph does not need to contain a "co-author" relationship for co-authorship to become visible. It emerges naturally when publications are treated as part of the structure rather than as metadata attached to people.

The shape of the graph depends on what you choose to traverse

Starting from a person and expanding affiliations produces a different neighborhood from starting with their publications. Following organizational relationships reveals institutional structure; following bibliographic relationships reveals networks of production and contribution. There is no single "academic graph" waiting to be displayed. There are many possible views through the same underlying structure, depending on the paths you choose to follow.

More data does not necessarily produce a better graph

The version using four sources had more entities and more relationships. But it also had more competing semantics and more ambiguity about what the resulting graph meant. Restricting the project to IdRef made the graph smaller in scope while making the exploration more coherent.

From lookup to traversal

The original interface to IdRef answers questions such as: Who is this person? Academic Graph Explorer asks a different kind of question: What is around this person, and what relationships connect them to the rest of the scholarly structure? That difference is small in terms of interface, but significant in terms of how the data is understood.

An authority file is usually encountered as a collection of records. Seen as a graph, it becomes a structure of entities, roles and paths. Publications become connective tissue. Affiliations become branches. Bibliographic roles become semantic edges. And the act of navigating from one entity to another becomes a way of discovering relationships that are difficult to perceive in a record-by-record interface. The project is therefore less an attempt to build a better authority-file interface than an experiment in making an existing knowledge structure walkable.

It started with the assumption that the interesting graph might need to be assembled from many scholarly datasets. It ended with a more modest proposition: sometimes the most useful thing you can do with a rich knowledge source is not add more data, but find a better way to move through the structure that is already there. Academic Graph Explorer is that experiment.

An older project about visualizing knowledge as a graph: