Case study, own product
AI Search: semantic search for WordPress and WooCommerce, as a plugin anyone can install.
A search layer that understands what shoppers mean, shipped as a free plugin with a paid cloud tier behind it. Designed, built and operated by me, from the first embedding call to the Postgres cluster that now answers queries in under 30 ms.
Role
Everything: product, PHP plugin, Node.js embedding service, Postgres + pgvector backend, support
Stack
PHP and WordPress, WooCommerce, OpenAI embeddings, Node.js, Postgres + pgvector
Timeline
June 2025 to today. 51 releases, 1.0 to 1.47, and still shipping
Where
Free on WordPress.org, rated 5/5. Product site wp-search.ai
The problem
WordPress search looks for words. Shoppers search for meaning.
Out of the box, WordPress and WooCommerce search run a LIKE '%query%' against titles and content. It works when the visitor types the exact word that appears in the product. It fails on everything else: “summer wedding guest dress” finds nothing in a store full of midi wraps and linen sets; “cozy winter sweater” misses every merino pullover; “trainers” misses a category called Sneakers. The result is an empty page, and an empty search page is a visitor who leaves.
Vector embeddings solve exactly this. Turn each product into a 1,536-number fingerprint of its meaning, do the same with the query, and compare them. The technique is well understood. What stops most WordPress site owners is everything around it: API keys, a vector store, an indexing pipeline, quotas, failure modes, and the fact that none of it survives contact with a shared host and a 5,000-product catalogue unless it is engineered carefully.
So the brief I set myself was narrow: semantic search that a store owner installs from the plugin directory, that goes live without an account or a key, and that never makes the results they already have worse.
What I built
A plugin, a service, and the rules between them
Three-tier ranking
Every search runs in a fixed order: exact SKU or title matches pinned first, then everything WordPress keyword search would have found, ranked by relevance instead of date, then semantic matches appended below. Installing the plugin never hides a result a store already had. This rule came late, in version 1.28, and it is the single decision that made the product trustworthy.
Indexing pipeline
Products, posts and any public post type are embedded on publish, off the request path, via a queued cron event so saving a product stays fast. Categories, tags and attribute terms get their own embeddings and give products a small boost at query time. Bulk sync runs in batches of 20 texts per HTTP call, persists its state in a transient, and survives a closed tab.
The embedding service
A Node.js service in front of OpenAI’s text-embedding-3-small that gives every site a free quota of 10,000 items with no account, handles batching, timeouts and keep-alive, and tracks usage per origin. Sites that want unlimited indexing bring their own key from the Advanced tab.
Fast Search backend
On the paid tiers, ranking moves off the WordPress host to a Postgres + pgvector cluster running a hybrid lexical + vector query, with the full-text index built per site locale so a Portuguese store keeps its Portuguese keyword hits. The keyword tier and WordPress visibility rules still run in the plugin, so permissions and catalog visibility behave exactly as before.
Live search blocks
Two Gutenberg blocks: a search box with a results dropdown and a header button that opens a full-screen overlay. Keyword matches render on every keystroke; the semantic lookup fires once, when typing pauses, so a ten-character query costs one AI request, not ten. The blocks read the theme’s colours, fonts and radius, and are keyboard and screen-reader accessible.
Operations and admin
A store status panel that says what is indexed, what has SKUs, and what is hidden from search; a Test search tab that shows default results next to AI results; three levels of cache clearing; a 4-tier fallback so a failed AI call degrades to normal WordPress search instead of an error; and seven PHP filters so developers can rewrite queries, pin results or exclude content.
Numbers from production
What the engineering actually changed
90 ms
per-search similarity CPU on a 1,000-product catalogue, down from ~700 ms, after packing vectors into binary at a smaller dimension (v1.45)
<30 ms
semantic results on the Postgres + pgvector Fast Search tier, for catalogues of any size
1 call
per batch of 20 products during sync, down from 20. Bulk sync on 500+ product stores stopped timing out
~100 to 1
database queries per 20-result WooCommerce search after fixing an N+1 in the keyword scorer
4×
less postmeta storage per indexed item with the packed vector format, migrated in the background with zero admin impact
100+
languages, with cross-lingual matching: an English query can find a Portuguese product
What production taught me
The embeddings were the easy part
The first version replaced WordPress search outright and loaded every published post into memory to score it. On 50 posts it was fine. On 5,000 it was a ten-second page. Worse, it registered with the embedding service inside the constructor, so when the service went down every page load on every site waited on a 20-second timeout. I wrote that one up publicly, because it is the kind of mistake you only make once.
The fixes were not clever: move work off the request, cache results, exit early, batch the network, and measure on a real catalogue instead of a local one. Search went from 3 to 8 seconds to under a second, then to 90 ms, then to under 30 ms once ranking moved to Postgres. Each step came from a real site complaining, not from a benchmark.
The product lesson was bigger. Replacing search was wrong; extending it was right. Once keyword results stayed where they were and AI results were only added below, store owners stopped asking whether the plugin was safe to install, because it was. The similarity threshold moved from a 0 to 1 slider to a 0.5 to 1 range after watching what people actually set. And the search endpoint itself turned out to be a security problem, which became a WordCamp Europe talk and a second plugin.
What I’d do differently
Start where version 1.28 ended up
I would design the three-tier ranking on day one, treat the embedding service as production from the first commit (health checks, timeouts, a status page), and ship the batched, resumable sync before the one-click button. I would also run the first hundred sites’ real queries through an evaluation set before touching the ranking, instead of tuning by feel. None of that is novel. It is the difference between a demo that works and a feature a store can depend on, and it is how I build AI features for clients now.
Try it: live demos on three sample stores. Install it: wordpress.org/plugins/ai-search. Read more: the original write-up, what it took to make it production-ready, and the talk at WordPress Day for AI 2026.
Need search that understands intent?
Tell me about your catalogue or content archive. Thirty minutes, no pitch.
Or email hello@samuelsilva.pt