Linked data (RDF)¶
Everything in the archive is also linked data. Each asset, collection, namespace, entity and person has a URI, and that URI returns its description in RDF. The description uses Dublin Core (DCMI Metadata Terms), SKOS, FOAF and OWL, plus a small Lens vocabulary for what those don't cover.
The RDF is generated from what Lens already stores: the asset's metadata (see IIIF), its entities and its people. It can't go out of date, and nothing needs migrating.
URIs¶
URIs use the address Lens publishes (iiif.base_url, or the address a request arrived at):
| URI | What it is |
|---|---|
/id/recording/<id> |
an asset, typed lens:Item and a DCMI type (Sound, MovingImage, Text, StillImage) |
/id/collection/<id> |
a collection (dcmitype:Collection), dcterms:isPartOf its parent or its namespace |
/id/namespace/<name> |
a namespace: a dcmitype:Collection and the skos:ConceptScheme of its entities |
/id/entity/<id> |
a skos:Concept, also foaf:Person, foaf:Organization, dcterms:Location or dcmitype:Event depending on its type |
/id/topic/<id> |
a topic: a skos:Concept in /id/namespace/<name>#topics (the namespace's skos:ConceptScheme of topics), with skos:broader, skos:narrower and skos:related; assets about it name it as dcterms:subject (Topics) |
/id/speaker/<id> |
a person (foaf:Person), owl:sameAs the people declared to be the same person |
/id/field/<id> |
a custom field, used as the property its values are given with |
/ns |
the Lens vocabulary (lens:), in Turtle |
Each URI uses content negotiation. An Accept of text/turtle, application/ld+json, application/n-triples or
application/rdf+xml, or ?format=turtle|json-ld|nt|xml, returns RDF. A browser is sent to the page instead
(303 See Other); an entity's page is named by its name, /wiki/<namespace>/<Name> (Notes).
A public asset's URI is open to everyone, just like its IIIF manifest. Visitors get its metadata and published
custom fields. Members also get the entities it mentions (dcterms:references), its access and its internal fields.
Collection, namespace, entity and person URIs are for the namespace's members only. Anyone else gets a 404, as if
they weren't there.
Mapping¶
| Lens metadata | RDF |
|---|---|
| label, summary | dcterms:title, dcterms:description (language-tagged) |
| creators, contributors | dcterms:creator, dcterms:contributor: the person in Lens, the person's URI, or a node with foaf:name |
| subjects | dcterms:subject: the entity, the subject's URI, or its label |
| navDate | dcterms:created (xsd:dateTime) |
| language | dcterms:language (dcterms:RFC5646) |
| rights, attribution | dcterms:license, dcterms:rights |
| provider | dcterms:publisher, a foaf:Organization |
| identifiers | dcterms:identifier (typed ones also as lens:identifier) |
| homepage, related | foaf:homepage, dcterms:relation |
| label/value pairs | lens:property (rdfs:label, rdf:value) |
| terms | the DCMI term itself (dcterms:spatial, dcterms:alternative, …) |
| duration, file type | dcterms:extent (xsd:duration), dcterms:format (dcterms:IMT) |
| where it is | dcterms:isPartOf its collection or namespace |
terms holds every other DCMI term an asset can have: alternative, abstract, audience, coverage, spatial,
temporal, source, issued, modified, rightsHolder, provenance, isVersionOf, replaces, requires and the rest. Each
term holds a list of values. A value that is an http(s) address becomes a link. The oai_dc record (/iiif/<id>/dc.xml)
includes those of them that refine one of its 15 elements (spatial becomes dc:coverage, alternative becomes
dc:title, and so on).
API¶
GET /api/v1/recordings/{id}/rdfreturns an asset with the entities it mentions.GET /api/v1/namespaces/{name}/rdfreturns a whole namespace: its collections, assets, entities and people. Adddownload=trueto save it as a file.
Both accept ?format= or an Accept header, and return Turtle when neither is given.
SPARQL¶
GET /api/v1/namespaces/{name}/sparql?query=… (or POST with {query}) runs a read-only SPARQL query over the
namespace's graph, the same graph /rdf returns. It is for the namespace's members. SELECT and ASK return SPARQL 1.1
JSON results (application/sparql-results+json). CONSTRUCT and DESCRIBE return RDF (Turtle unless format or
Accept asks for another format). The prefixes dcterms, dcmitype, foaf, skos, owl, rdf, rdfs, xsd and lens are
already known. SERVICE and FROM are refused, because a query never reaches outside the archive. A SELECT returns
at most 10,000 rows. The same query is available to agents as the MCP tool sparql. The app runs it from a
Collection page's SPARQL button.
# entities mentioned in the most assets
SELECT ?name (COUNT(?r) AS ?n) WHERE { ?r dcterms:references ?e . ?e skos:prefLabel ?name }
GROUP BY ?name ORDER BY DESC(?n) LIMIT 20
Import¶
POST /api/v1/namespaces/{name}/rdf/import reads Dublin Core descriptions into the namespace's assets. It is for
editors, and the body is {data, format, dry_run}.
- Turtle, N-Triples and JSON-LD are accepted. A JSON-LD document must have its
@contextinline, because Lens doesn't fetch anything while reading. RDF/XML isn't accepted. The limit is 5 MB. - A description is matched to an asset by its URI (
/id/recording/<id>). Failing that, it is matched by an identifier the asset already has (dcterms:identifier,dc:identifierorlens:lensId). Descriptions that match nothing are listed but not created. - DCMI terms and the 15 DC 1.1 elements fill the fields in the mapping above. People and entities named by URI must belong to the namespace. A license that isn't Creative Commons or RightsStatements.org is kept as a statement.
- Any other statement about the asset is kept as it came (
statements) and included again on export. Exporting an asset and importing it back changes nothing. - Terms and statements add to what the asset already has. Other fields replace what it has.
dry_run(the default) only reports what would change. Otherwise every change is a metadata edit that is kept in the asset's history and can be reverted, and the import is written to the audit log.
Refine later¶
- A permanent vocabulary URI shared by every Lens. Today
lens:is<address>/ns#, so two archives use different terms. - Roles qualified per asset. Today a person's role is left out, because the person is the same resource in every asset.
- Collections and entities that visitors can see in public namespaces.
- Importing descriptions that match no asset as new resources, and importing collections, entities and people.
- SPARQL builds the namespace's graph for every query. Cache it, or keep a triple store in step with the database, once namespaces get large. There is also no query timeout yet.