Skip to main content

Overview

Meilisearch is a fast, open-source search engine that provides typo tolerance, relevance ranking, faceted filtering, and sub-50ms search responses. Spree includes a built-in Meilisearch search provider that replaces the default SQL-based search. When to use Meilisearch:
  • Catalogs with 1,000+ products
  • Need typo tolerance and relevance ranking
  • Want faceted filtering with adjusted counts
  • Require sub-second search responses
  • Multi-locale / multi-currency storefronts
When the default Database provider is sufficient:
  • Development environments
  • Small catalogs (< 1,000 products)
  • Simple search requirements

Setup

1. Install Meilisearch

brew install meilisearch
meilisearch
# Running at http://localhost:7700 — no API key needed for development
For local development, Meilisearch runs without authentication by default — no API key is needed. For production, set a master key (minimum 16 bytes) via MEILI_MASTER_KEY when starting Meilisearch. Use this master key as your MEILISEARCH_API_KEY.

2. Add the gem to your Gemfile

gem 'meilisearch', '>= 0.28'
bundle install

3. Configure environment variables

# .env

# Local development — no API key needed
MEILISEARCH_URL=http://localhost:7700

# Production — use the master key you set when starting Meilisearch
# MEILISEARCH_API_KEY=your-master-key-min-16-bytes

4. Set the search provider

# config/initializers/spree.rb
Spree.search_provider = 'Spree::SearchProvider::Meilisearch'

5. Index your products

rake spree:search:reindex
That’s it. Your Store API now uses Meilisearch for all product search, filtering, and faceted navigation.

How It Works

Indexing

Products are indexed as one document per market × locale combination. Each document contains the product’s name and description in that locale, the price in that market’s currency, and all non-translated fields (categories, options, stock, tags). For example, a store with a US market (USD, English) and an EU market (EUR, German/French) creates 3 index documents per product:
Document IDNamePriceLocaleCurrency
prod_abc_en_USDBlue Shirt29.99enUSD
prod_abc_de_EURBlaues Hemd27.50deEUR
prod_abc_fr_EURChemise Bleue27.50frEUR
Products are only indexed for markets where they have a price. If a product has no EUR price, no EUR documents are created. Translations use Mobility with fallback — if a product has no German translation, the default locale (English) is used. Each document includes:
  • Translated name, description, slug
  • Price and compare-at price in the market’s currency
  • Category IDs, option type/value IDs (all prefixed)
  • Stock status, tags, timestamps
  • Store IDs, locale, currency (for filtering)
Preview what gets indexed:
# Rails console
product = Spree::Product.first
product.search_presentation
# Returns an array of documents — one per market × locale

Search Flow

GET /api/v3/store/products?q[search]=shirt&q[price_gte]=20&sort=price
  1. Controller builds AR scope for security: store.products.active(currency).accessible_by(ability)
  2. Meilisearch provider adds base filters: locale='en' AND currency='USD' AND status='active' AND store_ids='{id}' AND not_discontinued
  3. Provider sends one Meilisearch API call: search + user filters + facets + sort + pagination
  4. Provider intersects Meilisearch product IDs with AR scope (safety net)
  5. Returns products + facets with adjusted counts + pagination
The base filters ensure Meilisearch only returns products visible in the current locale and currency. The AR scope is a safety net — it should not filter out any additional products.

Index Name

Each store gets its own Meilisearch index: {store_code}_products. For a store with code "my-store", the index name is my_store_products.

Manual Operations

Reindex all products

rake spree:search:reindex

Index a single product

product.add_to_search_index

Remove a product from the index

product.remove_from_search_index

Preview indexed data

product.search_presentation

Configuration

Environment VariableDefaultDescription
MEILISEARCH_URLhttp://localhost:7700Meilisearch server URL
MEILISEARCH_API_KEYnilMeilisearch master key. Not needed for local development — only required when Meilisearch is started with MEILI_MASTER_KEY.
The index is automatically configured with the correct filterable, sortable, and searchable attributes when you run rake spree:search:reindex.

Filterable Attributes

The Meilisearch provider automatically configures these filterable attributes:
  • product_id — prefixed product ID (for bulk deletion of locale/currency variants)
  • status — product status (always filtered to active for Store API)
  • in_stock — boolean stock availability
  • store_ids — which stores the product belongs to
  • locale — document locale (filtered per request)
  • currency — document currency (filtered per request)
  • discontinue_on — Unix timestamp for discontinuation filtering
  • price — price in the document’s currency
  • category_ids — prefixed category IDs
  • tags — product tags
  • option_value_ids — prefixed option value IDs

Sortable Attributes

  • name — sort alphabetically (locale-aware via per-locale documents)
  • price — sort by price (currency-aware via per-currency documents)
  • created_at — sort by creation date
  • available_on — sort by availability date
  • units_sold_count — sort by best selling

Searchable Attributes

  • name — product name (in document locale)
  • description — product description (in document locale)
  • sku — variant SKU
  • option_values — option value presentations (e.g., “Red”, “Small”)
  • category_names — category names
  • tags — product tags

Store API Compatibility

The Meilisearch provider is fully compatible with the existing Store API. No client-side changes are needed:
// Same SDK calls work with both Database and Meilisearch providers
const products = await client.products.list({
  search: 'blue shirt',
  price_gte: 20,
  price_lte: 100,
  in_stock: true,
  sort: 'price',
  limit: 12,
})
Locale and currency are automatically resolved from request headers (x-spree-locale, x-spree-currency) — no need to pass them as filter params.

Production Recommendations

Separate search and admin keys

Meilisearch supports tenant tokens for fine-grained access control. Use an admin key for indexing and a search key for queries.

Meilisearch Cloud

For production deployments, consider Meilisearch Cloud for managed hosting, automatic scaling, and monitoring.