Quadstore appears to match string literals by "begins with" rather than "equals"
Peeja opened this issue · comments
Quadstore seems to be doing something odd. When I use the literal "ab"
in a query, it matches any string literal which begins with "ab"
, not just "ab"
itself—that is, it also matches "abc"
and "abcd"
. This behavior is present when using SPARQL queries through quadstore-communica
, but also in a raw store.match()
, which is presumably where the behavior originates.
I'm seeing this in both 12.0.2
and 13.0.0-alpha.3
.
The Jest test below reproduces and illustrates the issue, comparing the results with n3
's equivalents.
Jest Test
import { QueryEngine as RdfEngine } from "@comunica/query-sparql-rdfjs";
import { test, describe, expect } from "@jest/globals";
import { MemoryLevel } from "memory-level";
import { DataFactory as DF, Store as N3Store } from "n3";
import { Quadstore } from "quadstore";
import { Engine as QuadstoreEngine } from "quadstore-comunica";
import type * as RDF from "@rdfjs/types";
/**
* Read all results from an RDF Stream and return them as a promise of an array.
*/
export const readAll = <R>(stream: RDF.ResultStream<R>) =>
new Promise<R[]>((resolve) => {
const quads: R[] = [];
stream
.on("data", (result: R) => {
quads.push(result);
})
.on("end", () => {
resolve(quads);
});
});
const values = ["a", "ab", "abc", "abcd"];
const quads = values.map((str) =>
DF.quad(
DF.namedNode(`http://example.com/subject/${str}`),
DF.namedNode("http://example.com/predicate"),
DF.literal(str),
DF.defaultGraph()
)
);
const initStores = async () => {
const n3Store = new N3Store();
const quadstore = new Quadstore({
backend: new MemoryLevel(),
dataFactory: DF,
});
await quadstore.open();
await quadstore.multiPut(quads);
n3Store.addQuads(quads);
return { quadstore, n3Store };
};
describe("Quadstore should return the same results as N3.js", () => {
test("using match()", async () => {
const { quadstore, n3Store } = await initStores();
const findWithMatch = async (store: RDF.Store, v: string) =>
(
await readAll(
store.match(undefined, undefined, DF.literal(v), undefined)
)
).map((quad) => quad.subject.value);
expect(await findWithMatch(quadstore, "ab")).toStrictEqual(
await findWithMatch(n3Store, "ab")
);
/*
- Expected - 0
+ Received + 2
Array [
"http://example.com/subject/ab",
+ "http://example.com/subject/abc",
+ "http://example.com/subject/abcd",
]
*/
});
test("using a query", async () => {
const { quadstore, n3Store } = await initStores();
const rdfEngine = new RdfEngine();
const quadstoreEngine = new QuadstoreEngine(quadstore);
const findWithQuery = async (
engine: RDF.StringSparqlQueryable<RDF.SparqlResultSupport>,
context: RDF.QueryStringContext,
v: string
) => {
const bindingsStream = await engine.queryBindings(
`SELECT * { ?s <http://example.com/predicate> "${v}" . }`,
context
);
return (await readAll(bindingsStream)).map(
(bindings) => bindings.get("s")?.value
);
};
expect(await findWithQuery(quadstoreEngine, {}, "ab")).toStrictEqual(
await findWithQuery(rdfEngine, { sources: [n3Store] }, "ab")
);
/*
- Expected - 0
+ Received + 2
Array [
"http://example.com/subject/ab",
+ "http://example.com/subject/abc",
+ "http://example.com/subject/abcd",
]
*/
});
});
No worries, enjoy your break!
PR merged and released in 12.1.0
!