# Meilisearch AI Synonym Automation

This project now includes a production-safe synonym pipeline for **all Meilisearch indexes**.

## What It Does

- Detects indexes dynamically from Meilisearch (`/indexes` API).
- Reads per-index settings and sample documents.
- Extracts useful terms from searchable content (name/title/category/brand/tags/description-like fields).
- Calls AI to generate multilingual synonyms (English, Bangla, Bangla transliteration, common variants).
- Merges generated synonyms with existing Meilisearch synonyms safely (non-destructive).
- Persists generated terms in JSON state so repeated runs only process missing terms.
- Runs automatically with Laravel scheduler (cron-driven).
- Supports queue dispatch mode and dry-run mode.

## Core Components

- `app/Services/MeiliSynonyms/MeilisearchClient.php`
  - Reads indexes/settings/documents/synonyms and updates settings.
- `app/Services/MeiliSynonyms/TermExtractor.php`
  - Extracts terms and phrases while skipping stopwords.
- `app/Services/MeiliSynonyms/AiSynonymGenerator.php`
  - Calls primary AI provider (Arena/Open WebUI style) with Ollama fallback.
  - Includes built-in request rate-limiting and retry logic.
- `app/Services/MeiliSynonyms/SynonymStateStore.php`
  - Persists term-level generation state in JSON.
- `app/Services/MeiliSynonyms/SynonymMerger.php`
  - Merges existing + new synonyms safely.
- `app/Services/MeiliSynonyms/SynonymScanner.php`
  - Orchestrates full scan and logging.
- `app/Console/Commands/MeiliSynonymsScanCommand.php`
  - Run manually or via queue.
- `app/Jobs/RunMeiliSynonymScanJob.php`
  - Background job entrypoint.

## Environment Variables

See `.env.example` under the `Meilisearch synonym automation` section.

Most important:

- `MEILISEARCH_HOST`
- `MEILISEARCH_KEY`
- `MEILI_SYNONYM_ENABLED`
- `MEILI_SYNONYM_STATE_PATH`
- `MEILI_SYNONYM_AI_PRIMARY_URL`
- `MEILI_SYNONYM_AI_PRIMARY_MODEL` (set `arena` or your preferred model)
- `MEILI_SYNONYM_AI_PRIMARY_API_KEY`
- `MEILI_SYNONYM_AI_FALLBACK_URL`
- `MEILI_SYNONYM_AI_FALLBACK_MODEL`

## Run Manually

```bash
php artisan meili:synonyms:scan
```

Dry run (no Meilisearch settings write):

```bash
php artisan meili:synonyms:scan --dry-run
```

Only specific indexes:

```bash
php artisan meili:synonyms:scan --index=index_products --index=index_categories
```

Queue execution:

```bash
php artisan meili:synonyms:scan --queue
```

## Scheduler / Cron

The scheduler is registered in `routes/console.php` using `MEILI_SYNONYM_SCHEDULE_*` settings.

Make sure server cron runs Laravel scheduler:

```bash
* * * * * php /path/to/project/artisan schedule:run >> /dev/null 2>&1
```

## Logging

Synonym logs are written to:

- `storage/logs/meili-synonyms.log`

Events include:

- indexes scanned
- terms found
- synonyms generated
- settings update actions
- errors

## State File

Generated term cache is persisted at:

- `storage/app/meili-synonyms-state.json` (default)

This prevents regenerating the same term repeatedly.

## Hooking Into Document Update Flow

This repository currently has no direct Meilisearch indexing hook in app code, so automation is implemented by recurring scan + optional queue trigger.

If you have a product sync event later, call:

```php
RunMeiliSynonymScanJob::dispatch([$indexUid]);
```

right after indexing completes.
