npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@area37/vendure-plugin-default-search-plus

v1.1.0

Published

Adds price-range filtering, price facets (range + histogram) and optional pg_trgm fuzzy search to Vendure's DefaultSearchPlugin (Postgres) — the Elasticsearch-only search features, without Elasticsearch

Readme

Vendure DefaultSearch Plus

Extends Vendure's built-in DefaultSearchPlugin (Postgres) with the search features that otherwise only the Elasticsearch plugin provides — without an external search server:

  • Price-range filteringpriceRange / priceRangeWithTax on SearchInput
  • Price aggregationprices { range, rangeWithTax, buckets, bucketsWithTax } on SearchResponse (for price sliders / histograms), resolved lazily
  • Optional fuzzy (typo-tolerant) matching via the PostgreSQL pg_trgm extension
  • Optional cross-language matching — a term in one language finds products named in another, still displayed in the visitor's language

It plugs into DefaultSearchPlugin as a custom searchStrategy (extending the public PostgresSearchStrategy) plus a small set of shop-api resolvers. The standard search query, facets, collections, sorting and inStock keep working unchanged.

Postgres only. Requires @vendure/core ^3.1.0.

Install

npm install @area37/vendure-plugin-default-search-plus

Usage

import { DefaultSearchPlugin } from '@vendure/core';
import { PostgresPriceSearchPlugin } from '@area37/vendure-plugin-default-search-plus';

// Call .init() first so `searchStrategy` exists when DefaultSearchPlugin reads it.
const priceSearch = PostgresPriceSearchPlugin.init({
  indexStockStatus: true,   // must match DefaultSearchPlugin below
  fuzzy: true,              // requires the pg_trgm extension (see below)
  fuzzyThreshold: 0.25,
  priceRangeBucketCount: 10,
  crossLanguage: true,      // search every language, display the current one
  textSearchConfig: 'simple',
});

export const config: VendureConfig = {
  plugins: [
    DefaultSearchPlugin.init({
      indexStockStatus: true,
      searchStrategy: PostgresPriceSearchPlugin.searchStrategy,
    }),
    priceSearch,
  ],
};

Query:

query {
  search(input: { collectionSlug: "watches", priceRangeWithTax: { min: 0, max: 500000 } }) {
    totalItems
    items { productName priceWithTax { ... on PriceRange { min max } } inStock }
    prices { rangeWithTax { min max } buckets { to count } }
  }
}

Fuzzy search (pg_trgm)

fuzzy: true adds a pg_trgm word_similarity fallback to the term match so typos still return results (exact/prefix matches still rank highest). It requires the extension — run once (e.g. in a migration):

CREATE EXTENSION IF NOT EXISTS pg_trgm;

With fuzzy: false (the default) the strategy behaves exactly like the stock PostgresSearchStrategy term matching and pg_trgm is not required.

Cross-language search

On a multilingual shop the stock strategy searches one language: the row for the current language, or — outside the channel's default language — the current language falling back to the default. So on a Russian storefront "minecraft" finds nothing, even though the product is indexed as Minecraft in English and Mainkrafts in Latvian.

crossLanguage: true matches the term against the same variant's rows in every language while still returning the row for the current one. The visitor types in whatever language they think in and gets results labelled in theirs.

PostgresPriceSearchPlugin.init({ crossLanguage: true, textSearchConfig: 'simple' });

Vendure already writes one index row per available language, so there is nothing extra to index — but do create the indexes below, and note:

  • No dilution. A term in the page's own language returns exactly the same set as before; the extra branch only adds products that were unreachable.
  • Ranking. Cross-language-only hits get no ts_rank_cd score, so they sort after same-language matches.
  • Fuzzy stays same-language. word_similarity inside a correlated subquery cannot use an index (measured ~8x slower), and typos in a language the visitor does not write are not worth that. The cross-language branch is exact/prefix.

Indexes

to_tsvector(col) — the single-argument form the stock strategy emits — is only STABLE, so it can never be indexed. Setting textSearchConfig switches to the IMMUTABLE two-argument form, which makes GIN expression indexes possible:

import { searchIndexDdl } from '@area37/vendure-plugin-default-search-plus';

// in a Vendure migration
for (const sql of searchIndexDdl({ textSearchConfig: 'simple', crossLanguage: true })) {
  await queryRunner.query(sql);
}

The DDL is generated from the same config the strategy uses, because Postgres only picks up an expression index when the expressions match exactly. On a 2,600-variant catalogue this took a cross-language term query from 650 ms to ~15 ms. searchIndexDdlDown() returns the DROP INDEX counterparts.

Nothing breaks without the indexes — queries just fall back to a sequential scan.

Options

| Option | Default | Description | | --- | --- | --- | | indexStockStatus | false | Set to match DefaultSearchPlugin.init({ indexStockStatus }). | | fuzzy | false | Enable pg_trgm typo tolerance (requires the extension). | | fuzzyThreshold | 0.25 | word_similarity threshold [0..1]; lower = more permissive. Values below ~0.4 pull in unrelated products. | | crossLanguage | false | Match the term against every language's index rows; display the current language. | | textSearchConfig | 'simple' | Text-search config for to_tsvector/to_tsquery. null emits the un-indexable single-argument form (pre-1.1 behaviour). | | priceRangeBucketCount | 10 | Buckets returned in prices.buckets. |

Notes / limitations

  • Postgres only (uses width_bucket, to_tsquery, and — for fuzzy — word_similarity).
  • After enabling, reindex the search index (reindex mutation / dashboard).
  • fuzzy matching is never index-backed (word_similarity(a, b) > t is not an indexable predicate, and OR-ing it in prevents the GIN indexes from being used for the whole term clause). Budget for a sequential scan when it is on.

Compatibility & maintenance

Tested against @vendure/core 3.1.x and 3.7.x (e2e). The strategy extends the public PostgresSearchStrategy and references only the search_index_item table name — no deep dist/ imports — so it is reasonably portable across Vendure 3.x.

One thing to know: Vendure's PostgresSearchStrategy.applyTermAndFilters is private, so this plugin overrides it with a copy of that method (based on the 3.7 implementation) plus the price/fuzzy additions. If a future Vendure release changes term/facet/collection query building in that method, the copy can drift and would need to be re-synced. The e2e test guards against regressions — run it against the Vendure version you target before upgrading:

DB=postgres yarn e2e