On a conceptual level, a data schema is lens through which you query RDF data. It is similar to a data model for standard ORM libraries. Through schema, you describe a class of entities and their properties to be retrieved from an RDF source.
LDkit schemas are based on JSON-LD context format, and simple JSON-LD contexts can usually be easily transformed to schemas.
Below is a simple example of a schema to retrieve persons, including their name and birt date.
// `schema` is `https://schema.org/` namespace
import { schema } from "ldkit/namespaces";
const PersonSchema = {
"@type": schema.Person, // setting `@type` is required
name: schema.name, // retrieve property `schema.name` and alias it as `name`
birthDate: { // retrieve property `schema.birthDate` and alias it as `birthDate`
"@id": schema.birthDate,
"@type": xsd.date,
},
} as const; // this is important for proper type hints
The schema translates to the following:
- I want to query entities of RDF type
schema:Person
. - I want each entity to have
schema:name
property that will be available as a propertyname
of typestring
. - I want each entity to have
schema:birthDate
property that will be available as a propertybirthDate
of typeDate
.
Using the schema, the resulting entity in TypeScript will conform to the following type:
type PersonType = {
$id: string; // IRI
$type: string[]; // defined in `@type`
name: string;
birthDate: Date;
};
When defining a schema, you can specify:
- one or more entity RDF types
- property data type (and corresponding TypeScript native type)
- whether a property is optional or required
- whether a property is an array
- whether to retrieve multi-language string literals
- nested schemas within other schemas
Each schema must have at least one entity type defined.
const MySchema = {
"@type": "http://example.com/ontology/MyClass",
} as const;
const MyOtherSchema = {
"@type": [
"http://example.com/ontology/MyClass",
"http://example.com/ontology/MyOtherClass",
],
} as const;
Each property defined in a schema must correspond to the following markup:
import { rdf, xsd } from "ldkit/namespaces";
const MySchema = {
propertyShortName: {
"@id": rdf.label, // RDF name of the property
"@type": xsd.string, // RDF type of the property
"@optional": true, // if present, the property is optional
"@array": true, // if present, the resulting property is always an array
"@multilang": true, // if present, the resulting property is a map of languages and literals
},
} as const;
Unless specified otherwise, it is assumed that any property is of type
xsd:string
, required, and not an array. You can override these default
per-property.
In addition, there is a shortcut to specify default properties. The following two schemas are equivalent:
import { schema } from "ldkit/namespaces";
const SchemaOne = {
"@type": schema.Person,
name: schema.name,
} as const;
const SchemaTwo = {
"@type": schema.Person,
name: {
"@id": schema.name,
},
};
LDkit supports retrieving RDF literals with @language
annotations. The example
bellow shows sample input data, schema and how the data is transformed using the
schema.
const inputRdfData = `
x:A
a x:Item ;
x:multilang "CS"@cs, "EN"@en, "Unknown" .
`;
const schema = {
"@type": x.Item,
multilangProperty: {
"@id": x.multilang,
"@multilang": true,
},
};
const convertedData = {
$id: x.A,
$type: [x.Item],
multilangProperty: {
cs: "CS",
en: "EN",
[""]: "Unknown",
},
};
If you want the resulting data to be as close as possible to the original RDF data, you can use RDF identifiers instead of aliases.
const MySchema = {
"@type": schema.Person,
[schema.name]: schema.name,
};
The following schema showcases all possible supported variations.
const Thing = {
"@type": x.X,
required: x.required,
optional: {
"@id": x.optional,
"@optional": true,
},
array: {
"@id": x.array,
"@array": true,
},
multilang: {
"@id": x.multilang,
"@multilang": true,
},
multilangArray: {
"@id": x.multilangArray,
"@multilang": true,
"@array": true,
},
number: {
"@id": x.number,
"@type": xsd.integer,
},
boolean: {
"@id": x.boolean,
"@type": xsd.boolean,
},
date: {
"@id": x.date,
"@type": xsd.date,
},
nested: {
"@id": x.nested,
"@context": {
"@type": x.Nested,
nestedValue: x.nestedValue,
},
},
} as const;
And the resulting type will be:
type ThingType = {
$id: string;
$type: string[];
required: string;
optional: string | undefined;
array: string[];
multilang: Record<string, string>;
multilangArray: Record<string, string[]>;
number: number;
boolean: boolean;
date: Date;
nested: {
$id: string;
$type: string[];
nestedValue: string;
};
};