pg-smart-search
Core Concepts

Turbo Mode (AOT)

How pg-smart-search uses Stored Generated Columns (tsvector) to shift CPU cost from read-time to write-time for massive performance gains.

Turbo Mode (AOT)

For datasets > 1M rows, indices are not enough. Computing tsvector on the fly during search becomes a CPU bottleneck. Turbo Mode solves this using PostgreSQL's Stored Generated Columns.

How Turbo Mode Works

Instead of computing the tsvector on the fly like this:

-- Without Turbo Mode (Computed on read)
SELECT * FROM products
WHERE to_tsvector('english', coalesce(name, '') || ' ' || coalesce(description, '')) @@ plainto_tsquery('laptop');

Turbo Mode pre-computes and stores the vector in the table:

-- With Turbo Mode (Computed on write)
SELECT * FROM products
WHERE search_vector @@ plainto_tsquery('laptop');

This shifts the CPU cost from read-time to write-time. Since most applications read far more than they write, this results in dramatic latency improvements.

If searchColumns has more than one column and ftsColumn isn't set, the on-the-fly query above is what actually runs, OR'd per column — and the CLI only ever generates one GIN index per individual column, never a matching combined-expression index, so that query can't use any of them and sequential-scans every time. The engine logs a warning for this configuration. Turbo Mode (ftsColumn) is the fix: one pre-computed column, one matching index.

Setup

You can enable Turbo Mode easily using the interactive CLI:

npx pg-smart-init
# Confirm "Enable Turbo Mode?" when prompted

Or configure it manually in your engine initialization:

const engine = new TrigramSearchEngine(adapter, {
  tableName: "products",
  searchColumns: ["name", "description"],
  ftsColumn: "search_vector", // This activates Turbo Mode
});

Using ftsColumn requires that the column exists in your table and is defined as a GENERATED ALWAYS AS column storing tsvector data.