From MySQL FULLTEXT

Move off MySQL FULLTEXT before it bites you.

MySQL's FULLTEXT index is fine for tiny tables and disastrous for anything that scales. Three things break in sequence:

  1. Relevance is poor. There's no per-field weighting; minimum word length defaults to 4 and excludes too much.
  2. Typo tolerance is non-existent. tractoras returns nothing when the table has tractor.
  3. 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

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.

esc