w3c / json-ld-syntax

JSON-LD 1.1 Specification
https://w3c.github.io/json-ld-syntax/
Other
111 stars 22 forks source link

Support multiple IRIs for `@reverse` #372

Open alexkreidler opened 3 years ago

alexkreidler commented 3 years ago

Hi all,

It would be great to allow multiple property IRIs in the @reverse property. This would allow users to transform JSON-LD to other formats like N-Quads more easily without having to manipulate the output format to add additional triples with the extra properties they want to add.

For example, I have some JSON-LD like this:

{
  "@id": "ex:BEA/code/FREQ",
  "@type": "skos:ConceptScheme",
  "skos:notation": "FREQ",
  "entries": [
    {
      "@id": "ex:BEA/code/FREQ/A",
      "@type": "skos:Concept",
      "skos:prefLabel": "Annually",
      "skos:notation": "A"
    },
    {
      "@id": "ex:BEA/code/FREQ/Q",
      "@type": "skos:Concept",
      "skos:prefLabel": "Quarterly",
      "skos:notation": "Q"
    }
  ]
}

I'd like to have a context entry like this:

    "entries": {"@reverse": ["skos:hasTopConcept", "skos:inScheme"]}

This use-case is based on Example 14 from the RDF Data Cube Specification.

I know that the feature set is frozen for 1.1, I just wanted to put this out there for consideration for the next version.

I guess the trickiest part would be handling the expanded form, which looks like this currently:

{
  "@reverse": {
    "http://www.w3.org/2004/02/skos/core#hasTopConcept": [
        {
          "@id": "ex:BEA/code/FREQ/A",
          "@type": "skos:Concept",
          "skos:prefLabel": "Annually",
          "skos:notation": "A"
        },
    ]
  }
}

We can't have keys that are arrays, so we'd need to restructure it. I'm not sure if it's possible to do that in a backwards-compatible way, which might be a deal-breaker.

Let me know what you think. Thanks!

pchampin commented 3 years ago

Just to be clear, could you give an example in Turtle of the graph that you expect from your example above?

alexkreidler commented 3 years ago

Sure, here's the output:

@prefix skos: <http://www.w3.org/2004/02/skos/core#> .

<https://example.com/BEA/code/FREQ/A>
  a skos:Concept ;
  skos:hasTopConcept <https://example.com/BEA/code/FREQ> ;
  skos:inScheme <https://example.com/BEA/code/FREQ> ;
  skos:notation "A" ;
  skos:prefLabel "Annually" .

<https://example.com/BEA/code/FREQ/Q>
  a skos:Concept ;
  skos:hasTopConcept <https://example.com/BEA/code/FREQ> ;
  skos:inScheme <https://example.com/BEA/code/FREQ> ;
  skos:notation "Q" ;
  skos:prefLabel "Quarterly" .

<https://example.com/BEA/code/FREQ>
  a skos:ConceptScheme ;
  skos:notation "FREQ" .

It's effectively just the sum of the two graphs created by {"@reverse": "skos:hasTopConcept"} and {"@reverse": skos:inScheme"}, because the graphs are identical except for that property, and we just merge duplicate triples.

gkellogg commented 3 years ago

As you likely know, @reverse creates a reverse property relationships, so mapping "entries" to both skos:inScheme and skos:hasTopConcept creates a problem, if they're used in the same entity.

Of course, you could use a scoped context to map "entries" to one for some types of entities (or property values) and the other for different uses, which would allow you to consistently use "entries", but where the values in any given usage all have the same property relationship. Mapping to both would not allow the property relationship to be maintained.

alexkreidler commented 3 years ago

@gkellogg Thanks for your thoughts on how this could be done with the scoped context and different types. You also helped me realize that I constructed this example with the wrong SKOS property.

Wherever I have skos:hasTopConcept, it should be skos:topConceptOf (they are inverse properties). Sorry about that!

I found this part of the spec:

If the node object contains the @reverse key, its value MUST be a map containing entries representing reverse properties. Each value of such a reverse property MUST be an IRI reference, a compact IRI, a blank node identifier, a node object or an array containing a combination of these.

And ended up with this: JSON-LD Playground

{
  "@context": {
    "ex": "https://example.com/",
    "skos": "http://www.w3.org/2004/02/skos/core#"
  },
  "@id": "ex:BEA/code/FREQ",
  "@type": "skos:ConceptScheme",
  "skos:notation": "FREQ",
  "@reverse": {
    "skos:topConceptOf": [
      {
        "@id": "ex:BEA/code/FREQ/A",
        "@type": "skos:Concept",
        "skos:prefLabel": "Annually",
        "skos:notation": "A"
      },
      {
        "@id": "ex:BEA/code/FREQ/Q",
        "@type": "skos:Concept",
        "skos:prefLabel": "Quarterly",
        "skos:notation": "Q"
      }
    ],
    "skos:inScheme": [
      {
        "@id": "ex:BEA/code/FREQ/A"
      },
      {
        "@id": "ex:BEA/code/FREQ/Q"
      }
    ]
  }
}

So I guess I'm wondering if we could update the spec to convert this:

{
  "@context": {
    "ex": "https://example.com/",
    "skos": "http://www.w3.org/2004/02/skos/core#",
    "entries": {
       "@reverse": ["skos:hasTopConcept", "skos:inScheme"]
    }
  },
  "@id": "ex:BEA/code/FREQ",
  "@type": "skos:ConceptScheme",
  "skos:notation": "FREQ",
  "entries": [
    {
      "@id": "ex:BEA/code/FREQ/A",
      "@type": "skos:Concept",
      "skos:prefLabel": "Annually",
      "skos:notation": "A"
    },
    {
      "@id": "ex:BEA/code/FREQ/Q",
      "@type": "skos:Concept",
      "skos:prefLabel": "Quarterly",
      "skos:notation": "Q"
    }
  ]
}

into the above representation whenever we expand, compact, etc.

Thanks to all for your feedback.

gkellogg commented 3 years ago

So, in theory, the expansion case would work, where "entries" would map to two reversed IRIs. But, to be symmetric "@id" would be allowed an array of IRIs, as well, and then any use of that term would result in multiple outputs when expanding. Then, when compacting, you could only use that term if both property/values were present.

It could work, but it sounds like a fair amount of complexity to an already complex spec. Term selection when compacting is a nightmare, and we're quite wary about adding a feature that works when expanding that doesn't work when compacting. Typically, such use cases are left to framing, but I'm not sure how you would handle that in this case.

I'll add it as a request for a future group to consider.

VladimirAlexiev commented 2 years ago

@alexkreidler So you're proposing one JSON term to produce two prop instances. This is not limited to @reverse since in your same example, skos:inScheme and skos:topConceptOf are parallel props when it comes to top-level concepts.

I side with @dkellogg that such "addition of triples" will be quite difficult. I also think it goes against the "spirit" (principle?) of JSONLD that it doesn't produce new data.

Either specify all 2-3 of your prop instances per top concept, or use inference (subproperty and inverse) to infer them