Skip to content

Commit

Permalink
Added support for IRI type in Schema (#96)
Browse files Browse the repository at this point in the history
Resolves #94.
  • Loading branch information
karelklima authored Jan 11, 2024
1 parent 902ea96 commit e39cad3
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 71 deletions.
12 changes: 6 additions & 6 deletions library/decoder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Options } from "./options.ts";
import { fromRdf, Graph, Iri, Node, type RDF } from "./rdf.ts";
import { fromRdf, Graph, IRI, Node, type RDF } from "./rdf.ts";
import type { ExpandedProperty, ExpandedSchema } from "./schema/mod.ts";
import { ldkit } from "../namespaces/ldkit.ts";
import { rdf } from "../namespaces/rdf.ts";
Expand All @@ -19,7 +19,7 @@ class Decoder {
private schema: ExpandedSchema;
private options: Options;

private cache: Map<ExpandedSchema, Map<Iri, DecodedNode>> = new Map();
private cache: Map<ExpandedSchema, Map<IRI, DecodedNode>> = new Map();

private constructor(graph: Graph, schema: ExpandedSchema, options: Options) {
this.graph = graph;
Expand All @@ -31,15 +31,15 @@ class Decoder {
return new Decoder(graph, schema, options).decode();
}

getCachedNode(nodeIri: Iri, schema: ExpandedSchema) {
getCachedNode(nodeIri: IRI, schema: ExpandedSchema) {
if (!this.cache.has(schema)) {
this.cache.set(schema, new Map());
}
return this.cache.get(schema)!.get(nodeIri);
}

setCachedNode(
nodeIri: Iri,
nodeIri: IRI,
schema: ExpandedSchema,
decodedNode: DecodedNode,
) {
Expand Down Expand Up @@ -72,7 +72,7 @@ class Decoder {
return output;
}

decodeNode(nodeIri: Iri, schema: ExpandedSchema) {
decodeNode(nodeIri: IRI, schema: ExpandedSchema) {
const cachedNode = this.getCachedNode(nodeIri, schema);
if (cachedNode) {
return cachedNode;
Expand Down Expand Up @@ -109,7 +109,7 @@ class Decoder {
}

decodeNodeProperty(
nodeIri: Iri,
nodeIri: IRI,
node: Node,
propertyKey: string,
property: ExpandedProperty,
Expand Down
22 changes: 17 additions & 5 deletions library/encoder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Options } from "./options.ts";
import { DataFactory, type Iri, type RDF, toRdf } from "./rdf.ts";
import { DataFactory, type IRI, type RDF, toRdf } from "./rdf.ts";
import type { ExpandedProperty, ExpandedSchema } from "./schema/mod.ts";
import { xsd } from "../namespaces/xsd.ts";
import { rdf } from "../namespaces/rdf.ts";
import { ldkit } from "../namespaces/ldkit.ts";

type DecodedNode = Record<string, unknown>;

Expand All @@ -24,6 +25,19 @@ export const encode = (
);
};

export const encodeValue = (
value: unknown,
datatype: string,
df: DataFactory,
) => {
if (datatype === ldkit.IRI) {
return df.namedNode(value as string);
}
return toRdf(value, {
datatype: df.namedNode(datatype),
});
};

class Encoder {
private readonly options: Options;
private readonly includeType: boolean;
Expand Down Expand Up @@ -105,7 +119,7 @@ class Encoder {
});
}

encodeNodeType(node: DecodedNode, requiredTypes: Iri[], nodeId: NodeId) {
encodeNodeType(node: DecodedNode, requiredTypes: IRI[], nodeId: NodeId) {
const finalTypes = new Set([...this.getNodeTypes(node), ...requiredTypes]);

finalTypes.forEach((type) => {
Expand Down Expand Up @@ -177,9 +191,7 @@ class Encoder {
}
}

const rdfValue = toRdf(val, {
datatype: this.df.namedNode(propertyType),
});
const rdfValue = encodeValue(val, propertyType, this.df);
this.push(nodeId, propertyId, rdfValue);
});
}
Expand Down
8 changes: 4 additions & 4 deletions library/lens/lens.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Graph, Iri, RDF } from "../rdf.ts";
import type { Graph, IRI, RDF } from "../rdf.ts";
import {
type Options,
resolveOptions,
Expand Down Expand Up @@ -260,7 +260,7 @@ export class Lens<T extends Schema> {
* @param iri IRI of the entity to find
* @returns Entity if found, null otherwise
*/
async findByIri(iri: Iri): Promise<Unite<SchemaInterface<T>> | null> {
async findByIri(iri: IRI): Promise<Unite<SchemaInterface<T>> | null> {
const results = await this.findByIris([iri]);
return results.length > 0 ? results[0] : null;
}
Expand Down Expand Up @@ -294,7 +294,7 @@ export class Lens<T extends Schema> {
* @param iris IRIs of the entities to find
* @returns Array of found entities, empty array if there are no matches
*/
async findByIris(iris: Iri[]): Promise<Unite<SchemaInterface<T>>[]> {
async findByIris(iris: IRI[]): Promise<Unite<SchemaInterface<T>>[]> {
const q = this.queryBuilder.getByIrisQuery(iris);
this.log(q);
const graph = await this.engine.queryGraph(q);
Expand Down Expand Up @@ -442,7 +442,7 @@ export class Lens<T extends Schema> {
* @param identities Identities or IRIs of the entities to delete
* @returns Nothing
*/
delete(...identities: Identity[] | Iri[]): Promise<void> {
delete(...identities: Identity[] | IRI[]): Promise<void> {
const iris = identities.map((identity) => {
return typeof identity === "string" ? identity : identity.$id;
});
Expand Down
6 changes: 3 additions & 3 deletions library/lens/query_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
sparql as $,
type SparqlValue,
} from "../sparql/mod.ts";
import { DataFactory, type Iri, type RDF } from "../rdf.ts";
import { DataFactory, type IRI, type RDF } from "../rdf.ts";
import { ldkit } from "../../namespaces/ldkit.ts";
import { rdf } from "../../namespaces/rdf.ts";

Expand Down Expand Up @@ -199,7 +199,7 @@ export class QueryBuilder {
return query;
}

getByIrisQuery(iris: Iri[]) {
getByIrisQuery(iris: IRI[]) {
const query = CONSTRUCT`
${this.getResourceSignature()}
${this.getShape(Flags.UnwrapOptional | Flags.IgnoreInverse)}
Expand All @@ -222,7 +222,7 @@ export class QueryBuilder {
return INSERT.DATA`${quads}`.build();
}

deleteQuery = (iris: Iri[]) => {
deleteQuery = (iris: IRI[]) => {
return DELETE`
?s ?p ?o
`.WHERE`
Expand Down
7 changes: 3 additions & 4 deletions library/lens/search_helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DataFactory, toRdf } from "../rdf.ts";
import { DataFactory } from "../rdf.ts";
import { sparql as $, type SparqlValue } from "../sparql/mod.ts";
import { type ExpandedProperty, type SearchSchema } from "../schema/mod.ts";
import { encodeValue } from "../encoder.ts";
import { xsd } from "../../namespaces/xsd.ts";

export class SearchHelper {
Expand Down Expand Up @@ -115,9 +116,7 @@ export class SearchHelper {
}

private encode(value: unknown) {
return toRdf(value, {
datatype: this.df.namedNode(this.propertyType),
});
return encodeValue(value, this.propertyType, this.df);
}

private isPlainObject(value: unknown) {
Expand Down
12 changes: 6 additions & 6 deletions library/rdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export { DataFactory, DefaultGraph };
// @deno-types="npm:@types/n3"
export * as N3 from "npm:n3@1.17.2";

export type Iri = string;
export type IRI = string;

export type Node = Map<Iri, RDF.Term[]>;
export type Node = Map<IRI, RDF.Term[]>;

export type Graph = Map<Iri, Node>;
export type Graph = Map<IRI, Node>;

export const quadsToGraph = (quads: Iterable<RDF.Quad>) => {
const graph: Graph = new Map();
Expand All @@ -38,7 +38,7 @@ export declare namespace RDFJSON {
datatype?: string;
};
type Bindings = Record<string, Term>;
type Triple = [Iri, Iri, Term];
type Triple = [IRI, IRI, Term];
type SparqlResultsJsonFormat = {
head: {
vars?: string[];
Expand All @@ -48,7 +48,7 @@ export declare namespace RDFJSON {
};
boolean?: boolean;
};
type RdfJsonFormat = Record<Iri, Record<Iri, Term[]>>;
type RdfJsonFormat = Record<IRI, Record<IRI, Term[]>>;
interface TermFactory {
fromJson(jsonTerm: Term): RDF.Term;
}
Expand Down Expand Up @@ -205,7 +205,7 @@ export class QuadFactory implements RDFJSON.QuadFactory {
this.termFactory = termFactory;
}

fromJson(jsonRdf: [Iri, Iri, RDFJSON.Term]) {
fromJson(jsonRdf: [IRI, IRI, RDFJSON.Term]) {
const [s, p, o] = jsonRdf;
return this.dataFactory.quad(
this.dataFactory.namedNode(s),
Expand Down
81 changes: 41 additions & 40 deletions library/schema/data_types.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
import { xsd } from "../../namespaces/xsd.ts";
import { rdf } from "../../namespaces/rdf.ts";

const SupportedDataTypesPrototype = {
[xsd.dateTime]: new Date(),
[xsd.date]: new Date(),
[xsd.gDay]: new Date(),
[xsd.gMonthDay]: new Date(),
[xsd.gYear]: new Date(),
[xsd.gYearMonth]: new Date(),
[xsd.boolean]: true,
[xsd.double]: 0.0,
[xsd.decimal]: 0.0,
[xsd.float]: 0.0,
[xsd.integer]: 0,
[xsd.long]: 0,
[xsd.int]: 0,
[xsd.byte]: 0,
[xsd.short]: 0,
[xsd.negativeInteger]: 0,
[xsd.nonNegativeInteger]: 0,
[xsd.nonPositiveInteger]: 0,
[xsd.positiveInteger]: 0,
[xsd.unsignedByte]: 0,
[xsd.unsignedInt]: 0,
[xsd.unsignedLong]: 0,
[xsd.unsignedShort]: 0,
[xsd.string]: "",
[xsd.normalizedString]: "",
[xsd.anyURI]: "",
[xsd.base64Binary]: "",
[xsd.language]: "",
[xsd.Name]: "",
[xsd.NCName]: "",
[xsd.NMTOKEN]: "",
[xsd.token]: "",
[xsd.hexBinary]: "",
[rdf.langString]: "",
[xsd.time]: "",
[xsd.duration]: "",
};
import { ldkit } from "../../namespaces/ldkit.ts";
import { type IRI } from "../rdf.ts";

/** Map of supported RDF data types and their JavaScript native counterparts */
export type SupportedDataTypes = typeof SupportedDataTypesPrototype;
export type SupportedDataTypes = {
[xsd.dateTime]: Date;
[xsd.date]: Date;
[xsd.gDay]: Date;
[xsd.gMonthDay]: Date;
[xsd.gYear]: Date;
[xsd.gYearMonth]: Date;
[xsd.boolean]: boolean;
[xsd.double]: number;
[xsd.decimal]: number;
[xsd.float]: number;
[xsd.integer]: number;
[xsd.long]: number;
[xsd.int]: number;
[xsd.byte]: number;
[xsd.short]: number;
[xsd.negativeInteger]: number;
[xsd.nonNegativeInteger]: number;
[xsd.nonPositiveInteger]: number;
[xsd.positiveInteger]: number;
[xsd.unsignedByte]: number;
[xsd.unsignedInt]: number;
[xsd.unsignedLong]: number;
[xsd.unsignedShort]: number;
[xsd.string]: string;
[xsd.normalizedString]: string;
[xsd.anyURI]: string;
[xsd.base64Binary]: string;
[xsd.language]: string;
[xsd.Name]: string;
[xsd.NCName]: string;
[xsd.NMTOKEN]: string;
[xsd.token]: string;
[xsd.hexBinary]: string;
[rdf.langString]: string;
[xsd.time]: string;
[xsd.duration]: string;
[ldkit.IRI]: IRI;
};

/** List of supported native JavaScript types */
export type SupportedNativeTypes = SupportedDataTypes[keyof SupportedDataTypes];
2 changes: 1 addition & 1 deletion namespaces/ldkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export const ldkit = createNamespace(
{
iri: "https://ldkit.io/ontology/",
prefix: "ldkit:",
terms: ["Resource"],
terms: ["Resource", "IRI"],
} as const,
);
32 changes: 30 additions & 2 deletions tests/e2e/data_types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { assert, assertTypeSafe, Equals } from "../test_deps.ts";

import { initStore, x } from "../test_utils.ts";

import { createLens } from "ldkit";
import { xsd } from "ldkit/namespaces";
import { createLens, type Schema } from "ldkit";
import { ldkit, rdf, xsd } from "ldkit/namespaces";

Deno.test("Data Type / Boolean / xsd:boolean", async () => {
const { options, assertStore } = initStore();
Expand Down Expand Up @@ -1025,3 +1025,31 @@ Deno.test("Data Type / Date / xsd:gYearMonth", async () => {
assert(typeof record.property === "object");
assert(record.property.getTime() === new Date(2012, 2, 1).getTime());
});

Deno.test("Data Type / Iri / ldkit:IRI", async () => {
const { options, assertStore } = initStore();
const ResourceSchema = {
type: {
"@id": rdf.type,
"@type": ldkit.IRI,
},
} satisfies Schema;

const Resource = createLens(ResourceSchema, options);

await Resource.insert({
$id: x.Resource,
type: x.Resource,
});

assertStore(`
x:Resource a x:Resource .
`);

const record = await Resource.findByIri(x.Resource);

assert(record !== null);
assertTypeSafe<Equals<typeof record.type, string>>();
assert(typeof record.type === "string");
assert(record.type === x.Resource);
});

0 comments on commit e39cad3

Please sign in to comment.