Documentation
¶
Overview ¶
Package project turns the canonical BIBFRAME graph into the catalog's derived data: the JSON a static site (the Hugo module's content adapter) and the search index consume (ARCHITECTURE §7). It is a read-only projection -- the graph stays the source of truth -- and it flattens each clustered Work, with its Instances and the union of its feed and editorial statements, into one record.
Index ¶
- Constants
- Variables
- func SanitizeSources(cat *Catalog, public map[string]bool) int
- func SchemeForURI(uri string) string
- func SourceSet(csv string) map[string]bool
- type Catalog
- type Classification
- type ClassificationFacet
- type Contributor
- type FacetValue
- type Facets
- type Instance
- type Item
- type ProviderID
- type Redirect
- type RedirectMap
- type SchemePrefix
- type Subject
- type SubjectFacet
- type Term
- type Work
Constants ¶
const SchemaVersion = 10
SchemaVersion is the catalog.json / facets.json / redirects.json schema version. The Hugo module and search-index builder read it to detect a projector/consumer mismatch. v2 added the per-Instance identifier scheme (ProviderID.Source) for the availability adapter (tasks/004/008). v3 split controlled subjects (authority URIs + resolved labels) from uncontrolled feed tags (tasks/012). v4 added per-Instance format (from the Instance's RDA media type) and the Work-level formats facet, so a clustered mixed-format Work exposes each format (tasks/011). v5 added subject skos:broader parents (Subject.Broader / SubjectFacet.Broader) so consumers render vocabulary hierarchy without re-reading the graph (tasks/015). v6 added the holdings signal (Instance.Held / Work.Held): physical items or a live-availability identifier whose feed still lists the Work (tasks/078). v7 added Work.Summary from bf:summary -- the description/abstract as first-class bibliographic data rather than an adopter extra (tasks/124). v8 added Subject.Scheme / SubjectFacet.Scheme -- the short vocabulary code derived from the authority-URI namespace -- so a multi-vocabulary corpus can facet and mint term pages per scheme instead of colliding same-label terms from different vocabularies (tasks/141). v9 made classifications {value, label} objects (Work.Classifications / Facets.Classifications): value stays the scheme code (what MARC 084 $a carries), label is the human text riding the classification node's rdfs:label -- the display-only channel -- so a facet can show "Fiction / Romance / Contemporary" while exports keep FIC027000 (tasks/142). v10 added Catalog.Terms -- the vocabulary sideband: every referenced subject term plus its transitive skos:broader ancestors, with whatever labels and broader edges the graph carries -- so a consumer can label hierarchy nodes no Work carries directly instead of minting them label-less (tasks/178).
Variables ¶
var SubjectSchemePrefixes = []SchemePrefix{ {"https://homosaurus.org/", "homosaurus"}, {"http://homosaurus.org/", "homosaurus"}, {"http://id.worldcat.org/fast/", "fast"}, {"https://id.worldcat.org/fast/", "fast"}, {"http://id.loc.gov/authorities/subjects/", "lcsh"}, {"https://id.loc.gov/authorities/subjects/", "lcsh"}, {"http://id.loc.gov/authorities/childrensSubjects/", "lcshac"}, {"https://id.loc.gov/authorities/childrensSubjects/", "lcshac"}, {bibframe.LocalAuthorityNS, "local"}, }
SubjectSchemePrefixes is the namespace -> scheme table SchemeForURI consults, first match wins (tasks/141). `lcat project --subject-scheme` prepends deployment-specific entries, so a custom authority (or a different code for a known one) overrides these defaults.
Functions ¶
func SanitizeSources ¶ added in v0.30.0
SanitizeSources rewrites every Work's extra "sources" attribution list to the public allowlist, dropping the key when nothing public remains, and returns how many attributions were stripped (tasks/172). Provenance under lcat:extra/sources may name sources whose attribution must not leak on a public surface; the same allowlist governs the nq download (export package). Values are comma-joined by convention and compared trimmed; kept values are re-joined ", " and keep their order.
func SchemeForURI ¶
SchemeForURI returns the short vocabulary code for an authority URI, or "" when the namespace is not in SubjectSchemePrefixes.
Types ¶
type Catalog ¶
type Catalog struct {
Version int `json:"version"`
Works []Work `json:"works"`
// Terms is the vocabulary sideband (tasks/178): one entry per referenced
// subject term or transitive skos:broader ancestor that the graph
// describes (labels and/or broader edges; bare URIs with nothing to say
// are skipped). Sorted by ID.
Terms []Term `json:"terms,omitempty"`
}
Catalog is the projected corpus: one record per Work, sorted by id.
func Merge ¶ added in v0.30.0
Merge unions per-feed projections by work id (tasks/172). Project views one provenance graph at a time (feed:<provider> + editorial:), so a multi-feed corpus projects each feed separately and merges here: earlier catalogs win a shared id -- list the primary feed first, its records are richer than a sidecar's -- and the result is sorted by id like Project's output. The input catalog.nq must cover every feed's works; after a multi-feed ingest, `lcat serialize` regenerates it, since each ingest run rewrites catalog.nq with only its own run's works.
The vocabulary sideband (Catalog.Terms, tasks/178) merges by term id, field-wise: labels fill per language (earlier catalogs win a language, like works win an id), broader edges union, the first non-empty scheme sticks -- per-feed projections describe the same authority term from different coverage, so the union is at least as rich as any one input (tasks/180).
func Project ¶
Project reads a catalog.nq dataset and projects each Work into a Catalog record. Display/facet fields are drawn from the union of the provider's feed graph and the editorial graph, so curated subjects appear alongside feed data. Editorial lcat:overrides markers shadow the feed first (tasks/042): a property a cataloger claimed shows only its editorial values, and deleting the marker resurfaces the feed untouched.
catalogNQ must not be modified while the returned Catalog is in use: the parse is zero-copy (ParseNQuadsShared, tasks/057) -- one input-sized allocation saved at corpus scale -- so projected strings alias the buffer.
type Classification ¶
Classification is one classification of a Work: the scheme code (bf: classificationPortion -- what MARC 084 $a carries) plus the optional human label riding the classification node's rdfs:label (tasks/142). Facets and term pages key on Value; display prefers Label and falls back to Value, so a corpus without labels renders exactly as before.
type ClassificationFacet ¶
type ClassificationFacet struct {
Value string `json:"value"`
Label string `json:"label,omitempty"`
Count int `json:"count"`
}
ClassificationFacet is one classification facet value: the scheme code (the key), the optional human label (see Classification), and the number of Works carrying it (tasks/142).
type Contributor ¶
Contributor is an agent's display name and role.
type FacetValue ¶
FacetValue is one facet value and the number of Works that carry it.
type Facets ¶
type Facets struct {
Version int `json:"version"`
Languages []FacetValue `json:"languages,omitempty"`
Subjects []SubjectFacet `json:"subjects,omitempty"`
Tags []FacetValue `json:"tags,omitempty"`
Formats []FacetValue `json:"formats,omitempty"`
Contributors []FacetValue `json:"contributors,omitempty"`
Classifications []ClassificationFacet `json:"classifications,omitempty"`
}
Facets is the precomputed facet index: for each facetable dimension, the distinct values and how many Works carry each. Emitting it saves the static site from aggregating the whole corpus in templates at build time.
type Instance ¶
type Instance struct {
ID string `json:"id"`
Format string `json:"format,omitempty"`
ISBNs []string `json:"isbns,omitempty"`
ProviderIDs []ProviderID `json:"providerIds,omitempty"`
// Items are the Instance's physical holdings (tasks/051): call number,
// shelving location, barcode, note -- never circulation state, which
// stays live-only (ARCHITECTURE §5).
Items []Item `json:"items,omitempty"`
// Held is true when this Instance has >=1 item (physical) or a
// live-availability identifier whose feed still lists the Work (digital,
// unless the reconciliation flagged the Work withdrawn) -- tasks/078.
Held bool `json:"held,omitempty"`
}
Instance is one edition/format: its id, format (from its RDA media type), ISBNs, and the scheme-tagged provider ids the runtime availability adapter keys on.
type Item ¶
type Item struct {
CallNumber string `json:"callNumber,omitempty"`
Location string `json:"location,omitempty"`
Barcode string `json:"barcode,omitempty"`
Note string `json:"note,omitempty"`
}
Item is one holding of an Instance (the minimal bf:Item model, tasks/051).
type ProviderID ¶
ProviderID is one non-ISBN identifier with its bf:source scheme, so a client-side availability adapter selects its key by scheme (e.g. OverDrive's "overdrive-reserve" Reserve ID vs the "overdrive" title id) rather than guessing from a flat list (ARCHITECTURE §9, tasks/004). Source is empty for an untagged identifier.
type Redirect ¶
Redirect is one retired-Work -> surviving-Work URL redirect (ARCHITECTURE §4): after an editorial merge the retired id must still resolve, so shared links and SEO survive.
type RedirectMap ¶
RedirectMap is the redirect artifact emitted alongside catalog.json: every retired Work id and the surviving id it now resolves to, chains collapsed to the final survivor and sorted by retired id. The static host turns each into a 301 (per the tasks/001 decision: the projector emits the map, the host serves it).
func Redirects ¶
func Redirects(catalogNQ []byte) (RedirectMap, error)
Redirects builds the redirect map from a catalog.nq dataset by reading the editorial graph's lcat:mergedInto statements and collapsing merge chains (A->B->C yields A->C and B->C) to the final survivor. A merge cycle terminates at the last id reached rather than looping.
catalogNQ must not be modified while the returned map is in use: the parse is zero-copy (ParseNQuadsShared, tasks/057), so ids alias the buffer.
type SchemePrefix ¶
type SchemePrefix struct{ Prefix, Scheme string }
SchemePrefix maps an authority-URI namespace prefix to its short vocabulary code.
type Subject ¶
type Subject struct {
ID string `json:"id"`
Labels map[string]string `json:"labels,omitempty"`
Broader []string `json:"broader,omitempty"`
// Scheme is the short vocabulary code derived from the URI's namespace
// (SchemeForURI, tasks/141) -- "homosaurus", "fast", "lcsh", "local";
// empty for an unrecognized authority.
Scheme string `json:"scheme,omitempty"`
}
Subject is a controlled-vocabulary subject: a stable authority URI plus the human labels resolved from the authority's skos:prefLabel / rdfs:label statements in the graph, keyed by language tag (e.g. "en", "es"; "" for an untagged label). Links and facets key on ID; display uses Labels, falling back to ID when the authority provides none (tasks/012). Distinct from an uncontrolled feed Tag.
Broader holds the authority URIs of this term's skos:broader parents (sorted, deduped), so a consumer can render vocabulary hierarchy (breadcrumb trails, broader/narrower drill-down) without re-reading the graph (tasks/015). It is id-only: a parent's label resolves from the parent's own Subject/authority record.
type SubjectFacet ¶
type SubjectFacet struct {
ID string `json:"id"`
Labels map[string]string `json:"labels,omitempty"`
Broader []string `json:"broader,omitempty"`
// Scheme is the vocabulary code (see Subject.Scheme) so a consumer
// renders one facet group per vocabulary (tasks/141).
Scheme string `json:"scheme,omitempty"`
Count int `json:"count"`
}
SubjectFacet is one controlled-subject facet value: the authority URI (the key), its resolved labels, its skos:broader parents (for hierarchy-aware facet drill-down, tasks/015), and the number of Works carrying it. Facets key on ID so a relabel does not churn the facet; display uses Labels (tasks/012).
type Term ¶ added in v0.34.0
type Term struct {
ID string `json:"id"`
Labels map[string]string `json:"labels,omitempty"`
Broader []string `json:"broader,omitempty"`
Scheme string `json:"scheme,omitempty"`
}
Term is one controlled-vocabulary concept the catalog references -- a Work's subject or one of its skos:broader ancestors -- with the labels and broader edges resolved from the graph. The sideband exists for hierarchy nodes no Work carries directly: the browse-artifact builder unions subtree postings into ancestors (search.BuildBrowse), and without this it can only mint them label-less (tasks/178).
type Work ¶
type Work struct {
ID string `json:"id"`
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
// Summary is the Work's description/abstract (bf:summary), first label wins
// when a record carries several (tasks/124).
Summary string `json:"summary,omitempty"`
Contributors []Contributor `json:"contributors,omitempty"`
Subjects []Subject `json:"subjects,omitempty"`
Tags []string `json:"tags,omitempty"`
Languages []string `json:"languages,omitempty"`
Classifications []Classification `json:"classifications,omitempty"`
// Formats is the union of the Work's Instances' formats (e.g. ebook, audiobook),
// so a clustered mixed-format Work is faceted under each format it offers.
Formats []string `json:"formats,omitempty"`
Instances []Instance `json:"instances,omitempty"`
// Held is true when any Instance is held (tasks/078): physical items, or
// a live-availability identifier whose feed still lists the Work. Whether
// unheld Works are hidden or merely badged is the importing site's call.
Held bool `json:"held,omitempty"`
// Extra holds the Work's non-BIBFRAME adopter display fields (e.g. cover, rating,
// dateRead) a provider carried through the feed graph under bibframe.ExtraPred
// (tasks/026). The Hugo module forwards it to page params (tasks/022). Omitted (nil)
// when the corpus carries none, so a catalog without extras is unchanged.
Extra map[string]string `json:"extra,omitempty"`
}
Work is the discovery unit as the static site sees it -- the display and facet fields of a bf:Work plus its Instances (the borrowable editions/formats).