Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quadstore appears to match string literals by "begins with" rather than "equals" #160

Closed
Peeja opened this issue Aug 17, 2023 · 3 comments · Fixed by #161
Closed

Quadstore appears to match string literals by "begins with" rather than "equals" #160

Peeja opened this issue Aug 17, 2023 · 3 comments · Fixed by #161

Comments

@Peeja
Copy link
Contributor

Peeja commented Aug 17, 2023

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",
        ]
    */
  });
});
@jacoscaz
Copy link
Collaborator

jacoscaz commented Aug 17, 2023 via email

@Peeja
Copy link
Contributor Author

Peeja commented Aug 17, 2023

No worries, enjoy your break!

@jacoscaz
Copy link
Collaborator

PR merged and released in 12.1.0!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants