Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
PostgreSQL quad store and search index for Worlds.
Standalone PostgreSQL quad store and search index package extracted for the
@worlds ecosystem.
Install
Package managers
# Deno (first-class JSR support) deno add jsr:@worlds/postgres # Bun / npm / pnpm / Yarn (via JSR npm compatibility layer) npx jsr add @worlds/postgres
CDN (browser / no build step)
esm.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";
Usage
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";
Hybrid search
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).
Parity
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.
Development
deno task ci
Dry-run a JSR publish locally:
deno task publish:dry
Publishing to JSR
Releases publish automatically when changes merge to main.
Add Package
deno add jsr:@worlds/postgres
Import symbol
import * as postgres from "@worlds/postgres";
Import directly with a jsr specifier
import * as postgres from "jsr:@worlds/postgres@^0.3.0";