Dawloom
All posts

Search: Postgres full-text, Algolia, or Meilisearch

Dawloom engineering5 min read

People ask us whether they need Algolia or Meilisearch, or whether Postgres can just do it, before they’ve settled what “search” needs to mean for their product. That question has a real answer. It comes down to three things: whether users expect the search box to survive a typo, whether results need to be filtered by facets like brand or price band, and how much work you’re willing to take on keeping a separate index in sync with a database that keeps changing underneath it.

What Postgres full-text search actually does

Postgres has full-text search built in, and it’s more capable than the name suggests. to_tsvector normalizes a document into lexemes, lowercased, stemmed, stripped of stop words, and to_tsquery does the same to a search term. The @@ operator matches one against the other, and per the Postgres full-text search docs, the whole point is to find documents that satisfy a query and optionally rank them by relevance. Store the normalized vector in its own column, index it with GIN, and you get ranked, index-backed search on data you’re already querying.

SELECT id, title, ts_rank(search_vector, query) AS rank
FROM products, to_tsquery('english', 'leather & sofa') AS query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

Store that vector in a generated column, index it with GIN, and lookups stay fast even as the table grows into the tens of thousands of rows. That’s a normal Postgres index, backed by the database you already run, with nothing extra to deploy or keep alive.

What it doesn’t do on its own is forgive a typo. The docs are built around linguistic normalization, matching “satisfies” to “satisfy”, and a misspelled word just won’t stem to the right lexeme. For that you need the separate pg_trgm extension, which breaks strings into three-character trigrams and scores similarity between them. Per the pg_trgm docs, it’s meant to run alongside full-text search specifically to “recognize misspelled input words that will not be matched directly by the full text search mechanism.” It’s a separate extension you install and index on top of what to_tsvector already gives you.

For an admin panel, a support-ticket search box, or a “find this order” field, that combination is usually enough. It stops being enough once search is the main way people browse a catalog, where a typo is the normal case rather than the exception, which is closer to how ecommerce storefronts get used in practice.

Where a dedicated engine earns its cost

Algolia and Meilisearch solve the same core problem differently. Algolia is a managed service: you send it records, it hosts the index, and typo tolerance is on by default. Per Algolia’s documentation, typo count is the primary ranking signal, so an exact match outranks a one-typo match, which outranks a two-typo match, with no configuration required to get that behavior. Faceting is a first-class feature too, built for exactly the “filter by brand, then by price band” pattern a storefront needs.

Meilisearch does the same two things, open source. Typo tolerance is on by default there as well, using a Levenshtein-based algorithm where longer words tolerate more typos, up to two per word by default according to the Meilisearch typo tolerance docs. Faceted search is built in the same way. Where it differs from Algolia is the deployment model: Meilisearch is a single binary you can self-host, or run on Meilisearch Cloud if you’d rather not manage it, per the self-hosting docs.

Both engines put relevance tuning in your hands too: custom ranking rules, synonyms, and searchable-attribute weighting are all settings you configure, no code required. Postgres full-text search doesn’t give you that layer out of the box. Ranking there is ts_rank, computed from term frequency and document length, and any further tuning is SQL you write and maintain yourself.

Decision table comparing Postgres, Algolia, and Meilisearch on typo tolerance, faceted filtering, where each one runs, and index sync cost

The real cost is the sync, not the query

Picking a search engine is the easy decision. The harder one is what happens after: a dedicated index only knows what you last told it, and your source of truth keeps changing. FurnitureAxis, the multi-tenant retail SaaS we built, runs its product search on Algolia, and the index behind it doesn’t update itself. A C# background worker runs on a cron schedule and rebuilds it from the vendor catalog and Shopify data, so what a shopper searches reflects whatever the last sync pulled in, which can lag behind what’s currently in the database.

That’s the trade every team weighs once they add a dedicated search engine: rebuild on a schedule and accept some staleness between syncs, or push updates on every write and accept the added complexity of keeping two systems agreeing in real time. A cron rebuild is simpler to reason about and easier to recover from if something goes wrong mid-sync, at the cost of a shopper occasionally seeing a price or stock count that’s a few minutes old. A per-write push closes that gap but means every code path that touches the catalog now also has to remember to touch the index. Neither Algolia nor Meilisearch removes that decision. They just give you a faster, more forgiving search experience once you’ve made it.

Where we land

Start with Postgres full-text search, and add pg_trgm if the queries are internal or the data set is small enough that occasional missed typos don’t cost you a sale. Move to a dedicated engine once search becomes the primary way customers browse and the catalog is large enough that shoppers expect typo tolerance and faceting as a baseline. Between Algolia and Meilisearch, the deciding factor is usually whether you’d rather pay for a managed index or run the infrastructure yourself. Both give you the same typo tolerance and faceting; they just put the operational cost in different places.

Whichever way you land, budget real time for the sync job. It’s usually the part that gets estimated in an afternoon and takes a week. If you’re scoping a web build with a catalog behind it, get in touch and we’ll help you work out where your search actually needs to live before the index sync becomes its own project.

Got something to build?

Tell us what you need. An engineer replies, not a sales team.

Search the whole site