351 lines
10 KiB
Markdown
351 lines
10 KiB
Markdown
# Slot Naming Conventions for Hyper-Modular Schema
|
|
|
|
**Version**: 0.1.0
|
|
**Date**: 2025-11-21
|
|
**Schema**: Heritage Custodian Observation and Reconstruction Pattern
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
In a hyper-modular schema where each slot is defined in its own file, we need clear conventions for handling **semantically identical slots with different ranges** (typing constraints).
|
|
|
|
**Problem**: The same ontological property (e.g., `prov:wasRevisionOf`) may need to be applied to multiple classes with different range constraints:
|
|
- `was_revision_of` for `CustodianReconstruction` → previous `CustodianReconstruction`
|
|
- `was_revision_of` for `Record` → previous `Record`
|
|
- `was_revision_of` for `Collection` → previous `Collection`
|
|
|
|
**Solution**: Co-locate range-specialized variants in the **same file**, using hyphenated suffixes to distinguish slot names.
|
|
|
|
---
|
|
|
|
## Naming Convention
|
|
|
|
### Rule 1: Base Slot Name = Primary/First Use Case
|
|
|
|
The **first defined slot** uses the base name without suffix:
|
|
|
|
```yaml
|
|
# File: modules/slots/was_revision_of.yaml
|
|
|
|
slots:
|
|
was_revision_of:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianReconstruction # Primary use case
|
|
description: "Previous version of this reconstruction (if updated)"
|
|
```
|
|
|
|
**File location**: `/modules/slots/was_revision_of.yaml`
|
|
**Slot name**: `was_revision_of` (no suffix)
|
|
|
|
---
|
|
|
|
### Rule 2: Range-Specialized Variants = Hyphenated Suffix
|
|
|
|
Subsequent slots with **different ranges** but the **same `slot_uri`** are added to the **same file** with hyphenated suffixes:
|
|
|
|
```yaml
|
|
# File: modules/slots/was_revision_of.yaml (SAME FILE)
|
|
|
|
slots:
|
|
was_revision_of:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianReconstruction
|
|
description: "Previous version of this reconstruction (if updated)"
|
|
|
|
was_revision_of-record:
|
|
slot_uri: prov:wasRevisionOf # SAME ontological property
|
|
range: Record # DIFFERENT range
|
|
description: "Previous version of this record (if updated)"
|
|
|
|
was_revision_of-collection:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: Collection
|
|
description: "Previous version of this collection (if updated)"
|
|
```
|
|
|
|
**Hyphen placement**: `{base_slot_name}-{range_type_lowercase}`
|
|
|
|
**Examples**:
|
|
- `was_revision_of` → `CustodianReconstruction` (base)
|
|
- `was_revision_of-record` → `Record` (specialized)
|
|
- `was_revision_of-collection` → `Collection` (specialized)
|
|
- `was_revision_of-observation` → `CustodianObservation` (specialized)
|
|
|
|
---
|
|
|
|
### Rule 3: File Organization
|
|
|
|
**Single file per ontological property**, containing all range variants:
|
|
|
|
```
|
|
modules/slots/
|
|
├── was_revision_of.yaml # Contains: was_revision_of, was_revision_of-record, was_revision_of-collection
|
|
├── was_derived_from.yaml # Contains: was_derived_from, was_derived_from-entity
|
|
├── identifier_scheme.yaml # Contains: identifier_scheme (single variant - no specialization needed)
|
|
└── ...
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Easy to find all variants of a property (one file)
|
|
- ✅ Centralized documentation of ontological alignment
|
|
- ✅ Clear relationship between semantically identical slots
|
|
- ✅ Reduces file count (59 files vs. potentially 100+ with separate files per variant)
|
|
|
|
---
|
|
|
|
## Why Hyphens?
|
|
|
|
### ✅ Hyphens ARE allowed in LinkML slot names
|
|
|
|
LinkML permits hyphens (`-`) in slot identifiers:
|
|
|
|
```python
|
|
from linkml_runtime.linkml_model import SlotDefinition
|
|
|
|
# All valid:
|
|
SlotDefinition(name="was_revision_of") # Underscores
|
|
SlotDefinition(name="was-revision-of") # Hyphens
|
|
SlotDefinition(name="was_revision_of-record") # Mixed
|
|
```
|
|
|
|
### Advantages of Hyphen Suffix Convention
|
|
|
|
1. **Visual Separation**: `was_revision_of-record` clearly shows:
|
|
- `was_revision_of` = base semantic concept
|
|
- `-record` = range specialization suffix
|
|
|
|
2. **Avoids Double Underscores**: Using underscores would create:
|
|
- ❌ `was_revision_of_record` (ambiguous: is it "was_revision" of "of_record"?)
|
|
- ✅ `was_revision_of-record` (clear: "was_revision_of" specialized for "record")
|
|
|
|
3. **Consistency with LinkML Module Names**: LinkML uses hyphens in module IDs:
|
|
- `was-revision-of-slot` (module name)
|
|
- `was_revision_of-record` (slot name)
|
|
|
|
---
|
|
|
|
## Exceptions and Edge Cases
|
|
|
|
### Exception 1: Single-Range Slots
|
|
|
|
If a slot is **only used with one range**, no suffix is needed:
|
|
|
|
```yaml
|
|
# File: modules/slots/legal_name.yaml
|
|
|
|
slots:
|
|
legal_name:
|
|
slot_uri: cpov:legalName
|
|
range: string # Single, unchanging range
|
|
description: "Official legal name as registered"
|
|
```
|
|
|
|
**No variants**: `legal_name-string` is NOT needed (strings are the universal default).
|
|
|
|
---
|
|
|
|
### Exception 2: Slots with Multiple Acceptable Ranges (Union Types)
|
|
|
|
If a slot accepts **multiple ranges simultaneously** (union type), use `any_of`:
|
|
|
|
```yaml
|
|
# File: modules/slots/subject.yaml
|
|
|
|
slots:
|
|
subject:
|
|
slot_uri: dcterms:subject
|
|
any_of:
|
|
- range: string
|
|
- range: Concept
|
|
- range: uriorcurie
|
|
description: "Subject or topic (string, SKOS Concept, or URI)"
|
|
```
|
|
|
|
**No hyphenated variants needed**: This is a single polymorphic slot, not multiple specialized slots.
|
|
|
|
---
|
|
|
|
### Exception 3: Ontologically Distinct Properties
|
|
|
|
If two slots have **different `slot_uri` values**, they belong in **separate files**:
|
|
|
|
```yaml
|
|
# File: modules/slots/was_revision_of.yaml
|
|
slots:
|
|
was_revision_of:
|
|
slot_uri: prov:wasRevisionOf # PROV-O revision
|
|
|
|
# File: modules/slots/replaces.yaml (SEPARATE FILE)
|
|
slots:
|
|
replaces:
|
|
slot_uri: dcterms:replaces # DIFFERENT ontological property
|
|
```
|
|
|
|
Even if both represent "replaces previous version" semantically, different ontology properties = different files.
|
|
|
|
---
|
|
|
|
## Implementation Guidelines
|
|
|
|
### When Adding a New Range Variant
|
|
|
|
1. **Check if slot file exists**: Look for `modules/slots/{base_slot_name}.yaml`
|
|
2. **Check ontological alignment**: Does the new slot use the **same `slot_uri`**?
|
|
3. **If YES**: Add to existing file with hyphenated suffix
|
|
4. **If NO**: Create new file with appropriate base name
|
|
|
|
### Example: Adding `was_revision_of-observation`
|
|
|
|
```bash
|
|
# 1. Check existing file
|
|
cat modules/slots/was_revision_of.yaml
|
|
|
|
# 2. Verify slot_uri matches (prov:wasRevisionOf)
|
|
|
|
# 3. Add new variant to SAME file
|
|
```
|
|
|
|
```yaml
|
|
# File: modules/slots/was_revision_of.yaml
|
|
|
|
id: https://nde.nl/ontology/hc/slot/was_revision_of
|
|
name: was-revision-of-slot
|
|
|
|
imports:
|
|
- ../classes/CustodianReconstruction
|
|
- ../classes/CustodianObservation # NEW import
|
|
|
|
slots:
|
|
was_revision_of:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianReconstruction
|
|
description: "Previous version of this reconstruction (if updated)"
|
|
|
|
was_revision_of-observation: # NEW variant
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianObservation
|
|
description: "Previous version of this observation (if updated)"
|
|
```
|
|
|
|
**Update aggregator** (`modules/slots_all.yaml`):
|
|
|
|
```yaml
|
|
imports:
|
|
- slots/was_revision_of # Already imports file (contains both variants now)
|
|
```
|
|
|
|
**No change needed**: Aggregator imports the file, which now contains both slots.
|
|
|
|
---
|
|
|
|
## Documentation Requirements
|
|
|
|
Each slot file containing multiple variants MUST include:
|
|
|
|
1. **File-level comment** explaining the ontological property
|
|
2. **Import statements** for ALL range classes
|
|
3. **Slot-level descriptions** distinguishing each variant
|
|
|
|
**Example**:
|
|
|
|
```yaml
|
|
# CustodianReconstruction Slot: was_revision_of
|
|
# PROV-O wasRevisionOf property with multiple range specializations
|
|
|
|
id: https://nde.nl/ontology/hc/slot/was_revision_of
|
|
name: was-revision-of-slot
|
|
|
|
imports:
|
|
- ../classes/CustodianReconstruction
|
|
- ../classes/CustodianObservation
|
|
- ../classes/Record
|
|
- ../classes/Collection
|
|
|
|
slots:
|
|
was_revision_of:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianReconstruction
|
|
description: >-
|
|
Previous version of this reconstruction (if updated).
|
|
PROV-O: wasRevisionOf for CustodianReconstruction versioning.
|
|
|
|
was_revision_of-observation:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: CustodianObservation
|
|
description: >-
|
|
Previous version of this observation (if updated).
|
|
PROV-O: wasRevisionOf for CustodianObservation versioning.
|
|
|
|
was_revision_of-record:
|
|
slot_uri: prov:wasRevisionOf
|
|
range: Record
|
|
description: >-
|
|
Previous version of this record (if updated).
|
|
PROV-O: wasRevisionOf for Record versioning.
|
|
|
|
comments:
|
|
- "All variants map to PROV-O prov:wasRevisionOf"
|
|
- "Range specialization enables type-safe versioning"
|
|
- "Hyphenated suffix pattern: {base_name}-{range_lowercase}"
|
|
```
|
|
|
|
---
|
|
|
|
## Rationale: Why Co-location?
|
|
|
|
### Alternative 1: Separate Files Per Variant ❌
|
|
|
|
```
|
|
modules/slots/
|
|
├── was_revision_of.yaml
|
|
├── was_revision_of_record.yaml
|
|
├── was_revision_of_collection.yaml
|
|
├── was_revision_of_observation.yaml
|
|
```
|
|
|
|
**Problems**:
|
|
- ❌ File explosion (59 slots → 150+ files with variants)
|
|
- ❌ Difficult to see all variants of a property
|
|
- ❌ Duplicated ontology documentation across files
|
|
- ❌ Import bloat in aggregator
|
|
|
|
### Alternative 2: Co-location with Hyphenated Suffixes ✅
|
|
|
|
```
|
|
modules/slots/
|
|
├── was_revision_of.yaml # Contains all 4 variants
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Manageable file count
|
|
- ✅ Single source of truth for ontological property
|
|
- ✅ Easy maintenance (one file to update)
|
|
- ✅ Clear relationship between variants
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Convention | Rule | Example |
|
|
|------------|------|---------|
|
|
| **Base slot** | No suffix | `was_revision_of` |
|
|
| **Range variant** | Hyphenated suffix | `was_revision_of-record` |
|
|
| **File location** | Single file per ontological property | `/slots/was_revision_of.yaml` |
|
|
| **File contains** | All range variants | `was_revision_of`, `was_revision_of-record`, ... |
|
|
| **Suffix format** | `-{range_lowercase}` | `-record`, `-observation`, `-collection` |
|
|
| **When to create variant** | Same `slot_uri`, different `range` | `slot_uri: prov:wasRevisionOf` |
|
|
| **When to create new file** | Different `slot_uri` | `prov:wasRevisionOf` vs `dcterms:replaces` |
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- LinkML Slot Definition: https://linkml.io/linkml/schemas/slots.html
|
|
- PROV-O wasRevisionOf: https://www.w3.org/TR/prov-o/#wasRevisionOf
|
|
- Heritage Custodian Schema: `/schemas/20251121/linkml/01_custodian_name_modular.yaml`
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-11-21
|
|
**Maintainer**: GLAM Data Extraction Project
|