MySQL's FULLTEXT index is fine for tiny tables and disastrous for anything
that scales. Three things break in sequence:
- Relevance is poor. There's no per-field weighting; minimum word length defaults to 4 and excludes too much.
- Typo tolerance is non-existent.
tractorasreturns nothing when the table hastractor. - Index rebuilds lock the table. Adding a column for search is expensive.
If you're searching more than ~50k rows or your search query joins more than one table, you're paying for it on every page render.
# Migration in three steps
# 1. Dump the columns you want to search
SELECT id, title, brand, category, price, in_stock
FROM products
WHERE deleted_at IS NULL;
Stream the result to JSON. Most ORMs (Laravel, ActiveRecord, Sequelize) make this a one-liner.
# 2. Push to Skryx with the batch endpoint
POST /v1/indexes/products/documents/batch.
Use the row's primary key as id.
# 3. Replace your MATCH … AGAINST with a /query call
// Before
$rows = DB::select(
"SELECT * FROM products WHERE MATCH(title, brand) AGAINST(? IN BOOLEAN MODE)",
["+wireless +headphones"]
);
// After
$resp = Http::withToken(env('SKRYX_API_KEY'))
->post('https://api.skryx.io/v1/indexes/products/query', [
'q' => 'wireless headphones',
'query_by' => 'title,brand,category',
'per_page' => 20,
])->json();
$rows = collect($resp['data']['hits'])->pluck('document');
# Keep MySQL as the source of truth
Skryx is your search index, not your database. Keep writing to MySQL exactly as before; sync to Skryx via:
- A small webhook on row create/update/delete (lowest-lag), or
- A nightly batch dump → batch upsert.
The data sources feature also accepts a JSON URL, so a static dump cron job is enough to keep things in sync.