@faubulous/mentor-rdf 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,6 +34,62 @@ An [RDF.js](https://rdf.js.org/)-compatible triple store with:
34
34
  - Named graph management
35
35
  - Bundled W3C ontologies (RDF, RDFS, OWL, SKOS, SHACL, XSD)
36
36
 
37
+ ## SKOS Concept Schemes and Collections
38
+
39
+ SKOS hierarchies are not trees, and the standard does not require them to stay inside a concept scheme. The repository therefore applies explicit rules so that hierarchical views neither lose resources nor show them in the wrong place.
40
+
41
+ ### Which concepts belong to a scheme
42
+
43
+ A concept is **associated** with a scheme when it carries `skos:inScheme` or `skos:topConceptOf` for it — `skos:broader` and `skos:narrower` are deliberately *not* considered, because they may cross scheme boundaries.
44
+
45
+ `getTopConcepts` returns the concepts designated by `skos:hasTopConcept`/`skos:topConceptOf`, **plus** every concept of the scheme that has no broader concept *within the same scheme*. The second part matters for vocabularies like this one:
46
+
47
+ ```turtle
48
+ :TaskScheme a skos:ConceptScheme ; skos:hasTopConcept :task--root .
49
+
50
+ :task--paint a skos:Concept ;
51
+ skos:inScheme :TaskScheme ; # belongs to the task scheme …
52
+ skos:broader :group--finishing . # … but hangs off a concept of another scheme
53
+ ```
54
+
55
+ Following only `skos:broader` would leave `:task--paint` reachable exclusively under `:GroupScheme`, and `:TaskScheme` would appear to contain nothing but `:task--root`. Because the broader concept is not of `:TaskScheme`, `:task--paint` is a top concept of the scheme that declares it.
56
+
57
+ When expanding, `getNarrowerConcepts(graphs, conceptUri, { inScheme })` returns narrower concepts that are of that scheme, or that are associated with **no** scheme at all. The second clause keeps concepts visible in vocabularies that only annotate some of their concepts — a strict filter would hide them. It does not feed the top-concept rule, so an unscoped concept never becomes a top concept on its own; it is displayed wherever it is reached from.
58
+
59
+ ### Three different questions
60
+
61
+ | Method | Answers |
62
+ |---|---|
63
+ | `getConcepts(graphs)` | Every `skos:Concept` in the graphs, regardless of scheme. |
64
+ | `getConceptsOfScheme(graphs, scheme)` | Concepts **associated** with the scheme (`skos:inScheme`/`skos:topConceptOf`). One index scan. |
65
+ | `getAllConceptsInScheme(graphs, scheme)` | Concepts a hierarchical view of the scheme can **reach** — the top concepts plus everything narrower that may be displayed below them. |
66
+
67
+ A UI that shows a count next to a scheme should use `getAllConceptsInScheme`, so the number matches what expanding the scheme reveals. Using `getConcepts` reports the document-wide total under every scheme. The traversal indexes the hierarchical relations once per call rather than querying each concept, which keeps it usable on large thesauri (~37 ms for the 4,577-concept UNESCO thesaurus, versus ~6 ms for `getConceptsOfScheme`).
68
+
69
+ ### Nested collections
70
+
71
+ A `skos:Collection` may have other collections as members, so collections form a hierarchy too:
72
+
73
+ - `getCollections` returns every collection; `getRootCollections` returns only those that are not a member of another collection, which is what a hierarchical view should show at the top level.
74
+ - `getSubCollections` returns the members that are collections themselves; `getCollectionMembers` returns all members. Both resolve `skos:member` **and** `skos:memberList`, and preserve member list order for a `skos:OrderedCollection`.
75
+ - `getSuperCollections` resolves the inverse direction. For `skos:memberList` it walks the `rdf:rest` chain backwards from the member, so it also finds collections that share a list.
76
+ - All of these accept `inScheme` to restrict the result to the collections of one concept scheme, or `inScheme: null` for those associated with none.
77
+
78
+ ### Cycles and self-references
79
+
80
+ SKOS forbids hierarchical cycles, but real vocabularies contain them, so the traversals are defensive:
81
+
82
+ - A resource that is its own broader concept or its own member is still a root or top concept — `:A skos:member :A` does not remove `:A` from `getRootCollections`.
83
+ - Every collection returned by `getCollections` is either a root collection or reachable below one. If a group of collections is only reachable through a membership cycle, one of them is returned as a root so that none is lost.
84
+ - Traversals are guarded by a visited set and always terminate, including cyclic `rdf:rest` chains in a member list.
85
+
86
+ `src/rdf/tests/cases/valid-concept-cycle.ttl` and `valid-collection-cycle.ttl` pin this behaviour.
87
+
88
+ **Known limitations:**
89
+
90
+ - A collection identified by a blank node cannot be queried for its members, so it is always reported as a root collection.
91
+ - The "nothing is lost" guarantee above applies to collections. Concepts of a scheme whose broader relations form a *closed* cycle with no top concept have no entry point and are not reached by `getTopConcepts` or `getAllConceptsInScheme`.
92
+
37
93
  ## Installation
38
94
 
39
95
  Works in Node.js (>=22) and modern browsers: