PostgreSQL quad store and search index for Worlds.
Standalone PostgreSQL quad store and search index package extracted for the
@worlds ecosystem.
# Deno (first-class JSR support)
deno add jsr:@worlds/postgres
# Bun / npm / pnpm / Yarn (via JSR npm compatibility layer)
npx jsr add @worlds/postgresesm.sh serves JSR packages as ES modules — no install, no bundler needed.
import { createPostgresWorldsSdk } from "https://esm.sh/jsr/@worlds/postgres@0.2.0";With an import map:
<script type="importmap">
{
"imports": {
"@worlds/postgres": "https://esm.sh/jsr/@worlds/postgres@0.2.0"
}
}
</script>
<script type="module">
import { createPostgresWorldsSdk } from "@worlds/postgres";
</script>Pin to an exact build for deterministic caching:
import { createPostgresWorldsSdk } from "https://esm.sh/jsr/@worlds/postgres@0.2.0?pin=v1724100000";The SDK factory assembles the full facade (quad store + keyword search + SPARQL
engine) over a shared postgres.Sql surface:
import postgres from "postgres";
import { createPostgresWorldsSdk } from "@worlds/postgres/sdk";
const sql = postgres("postgres://localhost/worlds");
const sdk = await createPostgresWorldsSdk({ sql });Reference subpaths mirror the other Worlds backends:
import { PostgresQuadStore } from "@worlds/postgres/quad-store";
import { PostgresSearchIndex } from "@worlds/postgres/search-index";
import { PostgresRdfjsStore } from "@worlds/postgres/rdfjs-store";PostgresSearchIndex.search has two modes:
- Keyword-only (no embedding service): the reference's exact keyword semantics — case-insensitive substring over textual literals in the live quads table. This is the parity path.
- Hybrid (embedding service configured): Reciprocal Rank Fusion over the
reindexed chunks table — a tsvector keyword branch
(
to_tsvector/plainto_tsqueryin the configuredftsLanguage, default"english") and a pgvector cosine branch, each ranked 1..topK and fused as1/(60 + rank)summed, consistent with@worlds/libsql. A query-time embedding failure degrades to the keyword branch; an empty query runs the vector branch alone.
import { createPostgresWorldsSdk } from "@worlds/postgres/sdk";
const sdk = await createPostgresWorldsSdk({
sql,
embeddingService: myEmbeddingService,
vectorDimensions: 1536,
ftsLanguage: "english",
textSplitter: new RecursiveCharacterTextSplitter({ chunkSize: 1000 }),
});
await sdk.reindex(); // populate chunk embeddings + tsvectors (keyset-paginated)
const { results } = await sdk.search({ query: "hybrid query" });reindex() slices long literal values into chunk rows via the optional
textSplitter (any TextSplitterInterface, e.g. LangChain's
RecursiveCharacterTextSplitter) — each piece is embedded and FTS-indexed
separately, consistent with @worlds/libsql. Without a splitter, one chunk row
per textual literal is written (the identity default).
deno task ci runs a full-corpus parity suite (runParitySuite from
@worlds/sdk/testing) comparing createPostgresWorldsSdk against the portable
in-memory reference (@worlds/sdk/memory) over PGlite — search ordering is
compared set-wise, since SQL keyword-scan order is not a parity contract.
deno task ciDry-run a JSR publish locally:
deno task publish:dryReleases publish automatically when changes merge to main.