Skip to content

Commit

Permalink
Implemented custom bindings factory (#25)
Browse files Browse the repository at this point in the history
Resolves #22
  • Loading branch information
karelklima authored Sep 30, 2022
1 parent 7fff076 commit 6f40ab1
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 27 deletions.
119 changes: 94 additions & 25 deletions library/rdf.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import type {
Bindings,
BlankNode,
Literal,
NamedNode,
Quad,
Term,
Variable,
} from "https://esm.sh/[email protected]";
export type { Bindings, BlankNode, Literal, NamedNode, Quad, Term, Variable };

import type * as RDF from "https://esm.sh/[email protected]";

export type { RDF };
Expand All @@ -18,8 +7,6 @@ export { fromRdf, toRdf } from "https://esm.sh/[email protected]";
import { DataFactory } from "https://esm.sh/[email protected]";
export { DataFactory };

import { BindingsFactory as ComunicaBindingsFactory } from "https://esm.sh/@comunica/[email protected]";

import type {
IDataSource,
IQueryContextCommon,
Expand All @@ -43,11 +30,11 @@ export type IQueryEngine = RDF.StringSparqlQueryable<

export type Iri = string;

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

export type Graph = Map<Iri, Node>;

export const quadsToGraph = (quads: Quad[]) => {
export const quadsToGraph = (quads: RDF.Quad[]) => {
const graph: Graph = new Map();
for (const quad of quads) {
const s = quad.subject.value;
Expand All @@ -69,6 +56,7 @@ export declare namespace RDFJSON {
datatype?: string;
};
type Bindings = Record<string, Term>;
type Triple = [Iri, Iri, Term];
type SparqlResultsJsonFormat = {
head: {
vars?: string[];
Expand All @@ -86,7 +74,7 @@ export declare namespace RDFJSON {
fromJson(jsonBindings: Bindings): RDF.Bindings;
}
interface QuadFactory {
fromJson(jsonRdf: [Iri, Iri, Term]): RDF.Quad;
fromJson(jsonRdf: Triple): RDF.Quad;
}
}

Expand Down Expand Up @@ -116,17 +104,98 @@ export class TermFactory implements RDFJSON.TermFactory {
}
}

export class BindingsFactory extends ComunicaBindingsFactory
implements RDFJSON.BindingsFactory {
protected readonly localDataFactory: RDF.DataFactory;
export class ReadOnlyBindings implements RDF.Bindings {
public readonly type = "bindings";

protected readonly dataFactory: RDF.DataFactory;
protected readonly entries: Map<RDF.Variable, RDF.Term>;
protected readonly variables: Map<string, RDF.Variable>;

constructor(
bindings: Map<RDF.Variable, RDF.Term>,
dataFactory: RDF.DataFactory = new DataFactory(),
) {
this.entries = bindings;
this.dataFactory = dataFactory;
this.variables = new Map();
for (const variable of bindings.keys()) {
this.variables.set(variable.value, variable);
}
}

has(key: string | RDF.Variable) {
const stringKey = typeof key === "string" ? key : key.value;
const variableKey = this.variables.get(stringKey);
return this.entries.has(variableKey!);
}

get(key: string | RDF.Variable) {
const stringKey = typeof key === "string" ? key : key.value;
const variableKey = this.variables.get(stringKey);
return this.entries.get(variableKey!);
}

set(_key: string | RDF.Variable, _value: RDF.Term): RDF.Bindings {
throw new Error("Method not implemented.");
}

delete(_key: string | RDF.Variable): RDF.Bindings {
throw new Error("Method not implemented.");
}

keys() {
return this.entries.keys();
}

values() {
return this.entries.values();
}

forEach(fn: (value: RDF.Term, key: RDF.Variable) => unknown) {
return this.entries.forEach(fn);
}

get size() {
return this.entries.size;
}

[Symbol.iterator]() {
return this.entries.entries();
}

equals(_other: RDF.Bindings | null | undefined): boolean {
throw new Error("Method not implemented.");
}

filter(_fn: (value: RDF.Term, key: RDF.Variable) => boolean): RDF.Bindings {
throw new Error("Method not implemented.");
}

map(_fn: (value: RDF.Term, key: RDF.Variable) => RDF.Term): RDF.Bindings {
throw new Error("Method not implemented.");
}

merge(_other: RDF.Bindings): RDF.Bindings | undefined {
throw new Error("Method not implemented.");
}

mergeWith(
_merger: (self: RDF.Term, other: RDF.Term, key: RDF.Variable) => RDF.Term,
_other: RDF.Bindings,
): RDF.Bindings {
throw new Error("Method not implemented.");
}
}

export class BindingsFactory implements RDFJSON.BindingsFactory {
protected readonly dataFactory: RDF.DataFactory;
protected readonly termFactory: RDFJSON.TermFactory;

constructor(
dataFactory: RDF.DataFactory = new DataFactory(),
termFactory: RDFJSON.TermFactory = new TermFactory(),
termFactory: RDFJSON.TermFactory = new TermFactory(dataFactory),
) {
super(dataFactory);
this.localDataFactory = dataFactory;
this.dataFactory = dataFactory;
this.termFactory = termFactory;
}

Expand All @@ -135,11 +204,11 @@ export class BindingsFactory extends ComunicaBindingsFactory
[varName, jsonTerm],
) => {
return [
this.localDataFactory.variable!(varName),
this.dataFactory.variable!(varName),
this.termFactory.fromJson(jsonTerm),
] as [RDF.Variable, RDF.Term];
});
return this.bindings(bindingsEntries) as unknown as RDF.Bindings;
return new ReadOnlyBindings(new Map(bindingsEntries), this.dataFactory);
}
}

Expand All @@ -148,7 +217,7 @@ export class QuadFactory implements RDFJSON.QuadFactory {
protected readonly termFactory: RDFJSON.TermFactory;
constructor(
dataFactory: RDF.DataFactory = new DataFactory(),
termFactory: RDFJSON.TermFactory = new TermFactory(),
termFactory: RDFJSON.TermFactory = new TermFactory(dataFactory),
) {
this.dataFactory = dataFactory;
this.termFactory = termFactory;
Expand Down
1 change: 0 additions & 1 deletion specs/engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Deno.test("Boolean query with FALSE outcome", async () => {
"ASK { ?s <https://probablynonexistentnode> ?o }",
context,
);
console.log("RESPONSE", response);
assertEquals(response, false);
});

Expand Down
63 changes: 63 additions & 0 deletions specs/rdf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { assert, assertEquals } from "./test_deps.ts";
import {
BindingsFactory,
DataFactory,
QuadFactory,
type RDF,
type RDFJSON,
} from "../library/rdf.ts";

Deno.test("RDF / Quad Factory", () => {
const df = new DataFactory();
const quadFactory = new QuadFactory(df);

const q = (s: string, p: string, o: RDF.Quad_Object) => {
return df.quad(df.namedNode(s), df.namedNode(p), o);
};

const equalQuads = (triple: RDFJSON.Triple, quad: RDF.Quad) => {
const createdQuad = quadFactory.fromJson(triple);
assertEquals(createdQuad, quad);
};

equalQuads(
["s", "p", { type: "literal", value: "o" }],
q("s", "p", df.literal("o")),
);

equalQuads(
["s", "p", { type: "uri", value: "o" }],
q("s", "p", df.namedNode("o")),
);
});

Deno.test("RDF / Bindings Factory", () => {
const df = new DataFactory();
const bindingsFactory = new BindingsFactory(df);

const equalBindings = (
jsonBindings: RDFJSON.Bindings,
bindingsEntries: [string, RDF.Term][],
) => {
const createdBindings = bindingsFactory.fromJson(jsonBindings);
const bindings = new Map(bindingsEntries);
assertEquals(createdBindings.size, bindings.size);
bindings.forEach((term, variable) => {
assert(createdBindings.has(variable));
assertEquals(term, createdBindings.get(variable));
assertEquals(term, createdBindings.get(df.variable(variable)));
});
createdBindings.forEach((term, variable) => {
assertEquals(term, bindings.get(variable.value));
});
};

equalBindings(
{
"var": { type: "literal", value: "v" },
},
[
["var", df.literal("v")],
],
);
});
1 change: 0 additions & 1 deletion specs/treeiterator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { type Tree, TreeIterator } from "../library/asynciterator.ts";
async function assertIterator(input: Tree<unknown>, output: unknown[]) {
const i = new TreeIterator(input);
const result = await i.toArray();
console.log("RESULT", result);
assertEquals(result, output);
}

Expand Down

0 comments on commit 6f40ab1

Please sign in to comment.