Back to BlogArchitecture & Engineering

Ecommerce site search: Postgres, Meilisearch, Typesense or Algolia

Rupak Amin

Founder & Lead Engineer, RAITHub

14 min read

For most stores whose catalogue already lives in Postgres, start with Postgres full-text search plus pg_trgm for typo tolerance. Move to Meilisearch or Typesense when you need facets, synonyms and instant results (Meilisearch Cloud starts at $20 a month). Choose Algolia when merchandising tools matter more than cost: its Grow plan charges $0.50 per extra 1,000 searches.

If you would rather have it built for you, see how RAITHub would build this below.

There are five realistic choices, and they fall into three groups: search inside the database you already run, a dedicated open-source search engine you host or rent, or a hosted search service you pay per use.

  • Postgres full-text search and pg_trgm. Full-text search matches words after stemming and ranks them. The pg_trgm extension splits text into trigrams (groups of three characters) and scores how similar two strings are, which gives you basic typo tolerance. No extra service, no sync job.
  • Meilisearch. An open-source search engine, MIT-licensed for self-hosting with no usage limits, with typo tolerance, facets and synonyms on by default.
  • Typesense. An open-source search engine with a similar feature set. Typesense Cloud bills by cluster size, with no limits on records or operations.
  • Algolia. A hosted search service with merchandising, analytics and personalisation tools, billed per search request and per record. See Algolia pricing.
  • Elasticsearch or OpenSearch. General-purpose search and analytics engines. Elastic Cloud offers resource-based hosted and usage-based serverless pricing; OpenSearch is the open-source fork. Powerful, but the most to operate.

Postgres vs Meilisearch vs Typesense vs Algolia: how do they compare?

The short version: the further down this table you go, the more you pay in money or operations, and the more search features arrive ready-made.

EnginePricing modelTypo toleranceFacets and filtersMerchandisingExtra service to sync
Postgres FTS + pg_trgmFree; runs in your existing databaseTrigram similarity; you tune the thresholdYou write the SQL (GROUP BY counts)Build it yourselfNo
MeilisearchFree self-hosted (MIT); Cloud from $20/month, 14-day trial (source)On by default, by word lengthBuilt inListed as an Enterprise plan featureYes
TypesenseFree self-hosted; Cloud billed by RAM and vCPU, no per-search charge (source)Built inBuilt inPinning and curation rulesYes
AlgoliaFree tier; Grow includes 10K searches and 100K records, then $0.50 per 1K searches and $0.40 per 1K records (source)Built inBuilt inRules, analytics and AI ranking on higher plansYes
Elasticsearch / OpenSearchElastic Cloud hosted or serverless (source); OpenSearch open sourceFuzzy queries you configureAggregations, very flexibleBuild it or buy add-onsYes, plus cluster operations

To make the Algolia line concrete: a store doing 500,000 searches a month on Grow pays for 490,000 searches above the included 10,000, which is 490 × $0.50 = $245 a month before records, at the published rate. That is often fine for a store with that much traffic. It becomes a real line item when every keystroke of a search-as-you-type box counts as a request, so debounce the input.

Is Postgres full-text search good enough for an online store?

Often, yes, for catalogues in the low tens of thousands of products and a single language with a stemmer. It is the lowest-cost option because there is nothing new to run, and search results can never disagree with stock or prices, because they come from the same tables.

The pattern that works is two indexes: a full-text index for word matches and ranking, and a trigram index on the product name to catch typos such as "moisturiser" typed as "moistuizer". The pg_trgm docs set the default similarity threshold at 0.3 and the word-similarity threshold at 0.6, and both GIN and GiST indexes speed up these operators and unanchored LIKE and ILIKE queries.

CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- Weighted full-text column: name matters more than brand
ALTER TABLE products ADD COLUMN search tsvector
  GENERATED ALWAYS AS (
    setweight(to_tsvector('simple', coalesce(name, '')), 'A') ||
    setweight(to_tsvector('simple', coalesce(brand, '')), 'B')
  ) STORED;

CREATE INDEX products_search_gin ON products USING GIN (search);
CREATE INDEX products_name_trgm ON products USING GIN (name gin_trgm_ops);

-- $1 is the shopper's query
SELECT id, name,
       ts_rank(search, websearch_to_tsquery('simple', $1)) AS text_rank,
       word_similarity($1, name) AS fuzzy
FROM products
WHERE is_active
  AND (search @@ websearch_to_tsquery('simple', $1) OR $1 <% name)
ORDER BY text_rank DESC, fuzzy DESC
LIMIT 24;

The 'simple' configuration lowercases words without stemming, which is the safe default for mixed-language catalogues. For an English-only store, use the 'english' configuration so "serums" matches "serum". Postgres also offers synonym and thesaurus dictionaries, though editing them means touching files on the database server, which managed hosts may not allow. Most teams keep a small synonyms table and expand the query in application code instead.

Where Postgres runs out: facet counts across many attributes get slow and fiddly, ranking is harder to tune than in a search engine, and instant search on every keystroke puts load on your primary database.

When should you move to Meilisearch, Typesense or Algolia?

Move when shoppers filter by many attributes at once, when you need instant results under every keystroke, or when someone other than an engineer needs to tune synonyms and rankings. Those are the jobs dedicated engines do out of the box.

The price of moving is a second copy of your catalogue. Every change to a product, price or stock level has to reach the search index, or shoppers will click through to an item that is out of stock or priced differently. Treat the database as the source of truth, push changes to the index from the same code path that writes them (or from a queue), and run a nightly full reindex to catch anything missed.

Here is a minimal Meilisearch settings payload, sent as PATCH /indexes/products/settings. The default ranking rules are kept, with a custom stock rule at the end so in-stock items win ties.

{
  "searchableAttributes": ["name", "brand", "category", "description"],
  "filterableAttributes": ["brand", "category", "price", "skin_type", "stock_rank"],
  "sortableAttributes": ["price", "created_at"],
  "rankingRules": [
    "words", "typo", "proximity", "attributeRank",
    "sort", "wordPosition", "exactness", "stock_rank:desc"
  ],
  "synonyms": {
    "sunscreen": ["sunblock", "spf"],
    "moisturiser": ["moisturizer"]
  },
  "typoTolerance": {
    "minWordSizeForTypos": { "oneTypo": 5, "twoTypos": 9 },
    "disableOnAttributes": ["sku"]
  },
  "localizedAttributes": [
    { "attributePatterns": ["name_bn", "description_bn"], "locales": ["ben"] },
    { "attributePatterns": ["name_ar", "description_ar"], "locales": ["ara"] }
  ]
}

The typo settings shown are the documented defaults: one typo allowed from 5 characters, two from 9. Switching typos off for SKUs matters, because a product code one character away is a different product, not a misspelling.

Relevance in a store means the shopper finds something they can buy, quickly. That is a different goal from document search, and it changes the rules.

  • Weight fields. A match in the product name beats a match in the description. Brand and category sit in between.
  • Prefer what can be sold. Push out-of-stock items down rather than hiding them, so shoppers who search for them see that they exist.
  • Typo tolerance, with limits. Short words and product codes should match exactly. Long words can tolerate one or two typos.
  • Synonyms from real queries. Build the list from your search logs, not from a thesaurus. UK and US spellings, brand nicknames and local terms are where the wins are.
  • Facets that match how people choose. Price, brand, size and, in skincare, skin type. Show counts, and never show a facet value that returns zero results.
  • Merchandising, used sparingly. Pinning a product to the top of a query is useful for launches and stock clearance. Too many pins and search stops reflecting what shoppers want.

How do you handle Bengali, Arabic and other multilingual product search?

Store each language in its own field, tell the engine which language each field is in, and expect shoppers to mix scripts. That last part is the one teams miss.

In Bangladesh, shoppers type product names in Bengali script, in English, and in Bengali written with Latin letters. A search for "sunscreen" and one for the same word in Bengali script should land on the same product, which usually means indexing a transliterated or English name alongside the Bengali one and adding synonyms between them. Meilisearch lets you assign locales such as ben and ara per attribute through localizedAttributes, so each field is tokenised for its language.

Arabic adds normalisation: optional diacritics and several forms of the same letter should match each other, and the results page must lay out right to left. Postgres has no Bengali stemmer, so Bengali text goes through the 'simple' configuration; check which stemmers your Postgres version ships before relying on one for Arabic. The display side is covered in the Arabic RTL web app QA checklist.

What are zero-result searches, and why should you track them?

A zero-result search is a query that returned nothing. Each one is a shopper telling you, in their own words, what they wanted and could not find. It is the most useful search report you can run.

Log every query with its result count, then review the top zero-result queries weekly. Each fix falls into one of four buckets: add a synonym, fix a misspelt product name, adjust typo tolerance, or note demand for a product you do not stock. Hosted services such as Algolia include search analytics; with Postgres or self-hosted engines you need a small table like this one.

CREATE TABLE search_log (
  id         bigserial PRIMARY KEY,
  query      text        NOT NULL,
  results    integer     NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);

-- Top zero-result queries in the last 7 days
SELECT lower(trim(query)) AS q, count(*) AS searches
FROM search_log
WHERE results = 0 AND created_at > now() - interval '7 days'
GROUP BY 1
ORDER BY searches DESC
LIMIT 50;

Buy, build or hire?

OptionExamplesChoose this whenWatch out for
Off-the-shelf hosted searchAlgolia, Meilisearch Cloud, Typesense CloudYou want facets, analytics and merchandising now, and your team can write the sync jobPer-search costs with search-as-you-type; the index drifting from stock and prices
Platform search or an appYour store platform's built-in search, or a search app from its marketplaceYou run Shopify or similar and the catalogue is ordinaryLimited control over ranking, languages and B2B price visibility
Custom build on Postgres or a self-hosted enginePostgres FTS + pg_trgm, self-hosted Meilisearch, Typesense or OpenSearchYour catalogue has rules a plugin cannot model: gated trade prices, multiple languages, verification statesYou own relevance tuning, monitoring and upgrades

If you are still deciding whether your store needs custom work at all, read Shopify limitations: when to go custom first.

How long does it take to build ecommerce search yourself?

For a developer who knows SQL, Postgres full-text plus trigram search on one language takes about 2–4 days, including the search log. Adding Meilisearch or Typesense with a reliable sync job, facets and a search-as-you-type interface takes about 1–2 weeks. Multilingual search with transliteration and synonyms takes longer, because most of the time goes into testing real queries.

The main risk of doing it yourself is not the search itself. It is the index drifting out of step with stock and prices, so shoppers see items they cannot buy at prices that are wrong. The same discipline that prevents overselling inventory applies to keeping a search index honest. In a gated B2B store there is a second risk: a search index that holds trade prices can leak them to buyers who should not see them, because the index sits outside your database's access rules. Keep per-buyer prices out of the index and work them out on the server.

Why RAITHub for this

  • Commerce rules first. RAITHub built and runs TheSkinProof, the founder's own multi-vendor marketplace selling in Bangladesh, with 217 API endpoints and 750+ automated tests. It is the founder's venture, not a client reference, but it means the team works daily with Bangladeshi shoppers, local payments and stock that has to stay correct.
  • Gated catalogues. Sundor Skin, a B2B wholesale platform RAITHub built, runs 146 PostgreSQL tables with row-level security and tier pricing, so the team designs search that cannot leak one buyer's prices to another.
  • Tested relevance. RAITHub works QA-first, so on a search build your top real queries become a test set in CI, and a synonym or ranking tweak that breaks one does not ship. The wider approach is in the guide to building a marketplace or B2B store.

When you don't need us

  • Your store runs on a hosted platform and its built-in search, or a search app, already returns good results.
  • You have a developer who knows SQL and one language to support: the Postgres sample above will take you a long way.
  • You want Algolia configured and nothing else. Algolia's own documentation and partner network cover that well.

How RAITHub would build this

  • Audit your queries. Pull your current search logs, list the top queries and zero-result queries, and agree what "good results" means for each.
  • Choose the engine on evidence. Postgres first if it can meet the agreed queries; Meilisearch or Typesense if facets, instant search or languages demand it; a hosted service if your team wants its merchandising tools.
  • Sync and safety. Index updates from the same code path that writes products, prices and stock, a nightly reindex, and no per-buyer prices in the index.
  • Languages and synonyms. Per-language fields, transliteration where shoppers mix scripts, and a synonyms list built from your logs.
  • Analytics. A zero-result report your team can act on weekly.

Timeline: search added to an existing store is a backend and API engagement, typically 6–12 weeks at fixed scope. As part of a new store, it fits inside a 4–6 week fixed-scope MVP. A slow or broken search on a live store is often a 2–4 week code rescue.

You receive: automated tests and CI, including a relevance test set of your real queries; handover docs and runbooks for reindexing and tuning; and full IP, under an NDA signed before detailed discussion. See API and backend development and ecommerce development for what is included.

Next step: a free 15-minute technical audit, then a written fixed quote. Book the audit and bring a list of the searches your shoppers run most.

Frequently asked questions

Which search engine should an ecommerce site use?

There is no single answer. Postgres with pg_trgm suits stores whose catalogue is already in Postgres and needs basic typo tolerance. Meilisearch or Typesense suit stores that need facets and instant results. Algolia suits teams that want merchandising and analytics tools and accept per-search pricing.

How much does Algolia cost for an online store?

Algolia's Grow plan includes 10,000 search requests and 100,000 records a month, then charges $0.50 per extra 1,000 searches and $0.40 per extra 1,000 records, according to its pricing page. A store doing 500,000 searches a month would pay about $245 for searches alone.

Is Meilisearch free?

Self-hosted Meilisearch is open source under the MIT licence with no usage limits. Meilisearch Cloud, the managed service, starts at $20 a month with a 14-day free trial.

Can Postgres do typo-tolerant search?

Yes. The pg_trgm extension compares strings by trigrams and returns a similarity score, with a default threshold of 0.3. A GIN index with gin_trgm_ops makes those queries fast enough for product search on most small and mid-sized catalogues.

How do you make search work in Bengali?

Keep Bengali text in its own field, tell the engine its language (Meilisearch accepts the ben locale per attribute), and index an English or transliterated name alongside it, with synonyms between them. Shoppers in Bangladesh often type the same product in Bengali script, English and Latin-letter Bengali.

What is a zero-result search rate?

It is the share of searches that return no products. Tracking which queries return nothing, and fixing them weekly with synonyms, spelling fixes or typo settings, is one of the lowest-effort ways to improve search.

How long does it take to add search to an existing store?

Basic Postgres search takes a skilled developer about 2–4 days. A dedicated engine with sync, facets and instant search takes about 1–2 weeks. As a RAITHub fixed-scope backend engagement with tests, multilingual support and handover, plan for 6–12 weeks.

Ecommerce searchSite searchPostgres full-text searchpg_trgmMeilisearchTypesenseAlgoliaElasticsearch

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.