Skip to content

Adding Recipe for Versioning#284

Open
PonteIneptique wants to merge 1 commit intomasterfrom
cookbook/versioning
Open

Adding Recipe for Versioning#284
PonteIneptique wants to merge 1 commit intomasterfrom
cookbook/versioning

Conversation

@PonteIneptique
Copy link
Member

Fixes #282

I think this addresses how I would do that, and how I talked about it.
One thing I am not sure I like is that if you are linking to version outside of DTS, it becomes unclear. I am wondering if we should allow in 1.0 typed URIs, aka: {'@id': URI, '@type': 'Resource'}, but I would generally think it's a bad idea.

Copy link
Collaborator

@monotasker monotasker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I think we should merge this while we decide what to do about the case of external version links.

@monotasker
Copy link
Collaborator

On the question of external links, I might suggest that we just allow external URIs in the hasVersion array, in which case those external versions don't have a corresponding member object on the Resource. We reserve member for versions accessible via a dts endpoint, but encourage hasVersion to include any other external versions as well.

@hcayless
Copy link
Contributor

I think I'd do this a little bit differently (maybe slightly simpler?)

  • The root Resource represents the current work, with a series of previous versions as members. This is the same as Thibault's proposal except I'm assuming version info in the id URI. Maybe a less opaque version identifier, like "v1" etc. would be better for explanatory purposes.
  • The members property is basically a linked list of previous versions of the work, linked via the hasVersion and isVersionOf properties in the Dublin Core metadata. It is also (presumably) in reverse version order, latest to earliest.
{
  "@context": "https://distributed-text-services.github.io/specifications/context/1.0rc1.json",
  "dtsVersion": "1.0rc1",
  "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_9a18bc",
  "@type": "Resource",
  "title": "Bucolica",
  "totalParents": 1,
  "totalChildren": 2,
  "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_9a18bc(&nav}",
  "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_9a18bc{&ref,start,end,tree,mediaType}",
  "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_9a18bc{&ref,start,end,tree}",
  "dublinCore": {
    "creator": [
      "Calpurnius Siculus"
    ],
    "isVersionOf": [
      "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8"
    ]
  },
  "citationTrees": [
    {
      /* ... */
    }
  ],
  "member": [
    {
      "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8",
      "@type": "Resource",
      "title": "Bucolica",
      "totalParents": 1,
      "totalChildren": 0,
      "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8(&nav}",
      "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8{&ref,start,end,tree,mediaType}",
      "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8{&ref,start,end,tree}",
      "dublinCore": {
        "isVersionOf": [
          "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_2fe456"
        ],
        "hasVersion": [
          "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_9a18bc"
        ]
      },
      "citationTrees": [
        {
          /* ... */
        }
      ]
    },
    {
      "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_2fe456",
      "@type": "Resource",
      "title": "Bucolica",
      "totalParents": 1,
      "totalChildren": 0,
      "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_2fe456(&nav}",
      "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_2fe456{&ref,start,end,tree,mediaType}",
      "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_2fe456{&ref,start,end,tree}",
      "dublinCore": {
        "hasVersion": [
          "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_8c36a8"
        ]
      },
      "citationTrees": [
        {
          /* ... */
        }
      ]
    }
  ]
}

@monotasker
Copy link
Collaborator

@hcayless, so the isVersionOf and hasVersion function like genetic descent pointers? With the isVersionOf on the top-level Resource pointing back to the prior version? I suppose this would work even if we don't have a neat linear descent chain. If a number of parallel versions are made from one ancestor, it could have multiple hasVersion links? And if a later version consolidated a number of earlier ones together it could have multiple isVersionOf links?

My question then would be how someone would handle looser relationships between "editions" of a work--e.g. different translations or alternate manuscripts--where there isn't simple genetic descent. In this case, would someone create a parent Collection representing the notional work, and then create separate Resources representing the different editions? If that's our model, I think we should be very clear about when versioning is appropriate. And we should include a separate recipe for handling multiple editions.

@hcayless
Copy link
Contributor

I think the answer is that what you describe is outside the scope of "versioning" where the versions are basically releases. We could recommend dc:replaces and dc:isReplacedBy instead of the slightly fuzzier 'version' relationships and I'd be happy with that. If what you've got is a group of interrelated Resources, I think it's fine to group them in a Collection by conceptual work and then use metadata to link them. But I think that's a different beast entirely.

@monotasker
Copy link
Collaborator

Agreed. We'll just have to make it clear because people will likely confuse them.

@hcayless
Copy link
Contributor

@PonteIneptique, @monotasker Here's my compromise version. I've replaced the hashes with v1, etc. to try to make it clearer what's happening. To summarize: We're leveraging the ability of a Resource to have members in order to represent previous versions of that Resource using the members property. We're saying that the current version of the Resource will always have the id https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica. In the example, there are three versions: https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v3 (also known as https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica), https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2, and https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1.

{
  "@context": "https://dtsapi.org/specifications/context/v1.0.json",
  "dtsVersion": "v1.0",
  "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica",
  "@type": "Resource",
  "title": "Bucolica",
  "totalParents": 1,
  "totalChildren": 2,
  "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica(&nav}",
  "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica{&ref,start,end,tree,mediaType}",
  "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica{&ref,start,end,tree}",
  "dublinCore": {
    "isVersionOf": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2",
    "identifier": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v3"
  },
  "citationTrees": [
    {
      /* ... */
    }
  ],
  "member": [
    {
      "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2",
      "@type": "Resource",
      "title": "Bucolica",
      "totalParents": 1,
      "totalChildren": 0,
      "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2(&nav}",
      "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2{&ref,start,end,tree,mediaType}",
      "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2{&ref,start,end,tree}",
      "dublinCore": {
        "hasVersion": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica",
        "isVersionOf": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1"
      },
      "citationTrees": [
        {
          /* ... */
        }
      ]
    },
    {
      "@id": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1",
      "@type": "Resource",
      "title": "Bucolica",
      "totalParents": 1,
      "totalChildren": 0,
      "collection": "/api/dts/collection/?id=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1(&nav}",
      "document": "/api/dts/document?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1{&ref,start,end,tree,mediaType}",
      "navigation": "/api/dts/navigation?resource=https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v1{&ref,start,end,tree}",
      "dublinCore": {
        "hasVersion": "https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v2"
      },
      "citationTrees": [
        {
          /* ... */
        }
      ]
    }
  ]
}

Things I don't like:

  1. There isn't actually a good Dublin Core way to say "This is my version number". Using identifier isn't terrible, but it would have to be understood as a convention used by the implementer.
  2. Because of 1. we don't have an unambiguous way to express that if you want to bookmark the version, not just the latest thing, whatever that happens to be, you have to go looking in the Dublin Core section for it.
  3. If I wanted an umbrella, "FRBR Work" type thing, I'd model it as a Collection. So https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica would be the @id of a collection, and then all the versions would hang below it. I might propose that instead.
  4. I still wonder whether replaces and isReplacedBy might not be better ways to express versioning.

Thoughts? Is this clearer?

@PonteIneptique
Copy link
Member Author

If I wanted an umbrella, "FRBR Work" type thing, I'd model it as a Collection. So https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica would be the @id of a collection, and then all the versions would hang below it. I might propose that instead.

I don't like that, because then people note caring about versioning would have to deal with a supplementary level ?

We have also https://www.w3.org/ns/prov, which would require using a custom context and have something like prov:wasRevisionOf between version and owl:sameAs between the canonical and current version ?

@PonteIneptique
Copy link
Member Author

PonteIneptique commented Feb 12, 2026

Actually, I feel like we should use extensions, and use http://purl.org/pav/

-> https://pav-ontology.github.io/pav/#d4e359

@hcayless
Copy link
Contributor

I don't see the extra level thing as a problem. Collections are going to be the standard solution for solving all sorts of organization challenges.

Actually, it probably points at another cookbook recipe: "Here's how you can model stuff according to FRBR (if that's your jam)". I do like the idea of using PAV in extensions, and that would give us a concrete reason for providing an example of how to extend the context, which would be a good thing to have in the Cookbook. BUT, that would mean we can't say we support versioning natively.

Let me try to articulate the discomfort I have with eliding the current version: If the id of the latest version is always https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica, then if I want to peg something to a specific version, how do I a) find the URI for https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v3 and b) more importantly, how do I resolve it? B seems like a real problem, unless we introduce a duplicate node with the id https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v3.

But then I'm at a loss as to how we'd express the relationship between the parent node, https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica and its identical child node https://digitallatin.org/ids/Calpurnius_Siculus-Bucolica_v3. Is using dc:identifier enough? I'm not sure the semantics of it are clear enough.

I should say, I'm not arguing just to be difficult here, I'd really like to get it right. I'm starting to think we should enumerate the use cases for this...

@monotasker
Copy link
Collaborator

The PAV terms do seem to express exactly the semantics we want. So I'd support using it in extensions for the versioned Resources, whatever else we do.

As for using a collection vs a top-level Resource, I think your concern is valid @hcayless. Taking the parallel of our DOI handling, we create a DOI for the current version immediately in addition to the abstract work DOI. We display them both from the start. So people can link to either the specific version or the abstract work.

I'm imagining that in most DTS use cases what people will be doing is accessing the current or canonical version of a work. So what we want to provide is an easy path to that current version. Accessing other versions would come, I imagine when one wants to explore a work at a particular moment in time (for historical reasons), or when comparing versions (to explore genetic development). Those are important, and the PAV terms would allow for it, I think. They would also allow someone to signal versional relationships where a line of genetic descent isn't clear (or is disputed), like in manuscript histories. But we still need, I think, for access to the newest/canonical version to be clear and easily navigated.

One way to do this would be to use a Collection, as Hugh suggests, and use pav:hasCurrentVersion on that collection to indicate the canonical one? From there one could find the previous versions with pav:hasEarlierVersion, etc. But is the pay:hasCurrentVersion a clear enough signal to someone navigating to the collection tree that the collection represents a single notional work, rather than a set of works? Maybe? And we've already said that Collections can be used to represent the internal structure of some works. So maybe the Collection route with pav:hasCurrentVersion and pav:hasVersion in its extensions is good enough?

Sorry for the rambling verbiage. Didn't have time to make it more concise!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a Recipe for Versioning Resources

3 participants

Comments