Database

MeiliSearch

Overview

MeiliSearch is a lightweight and fast full-text search engine developed in Rust. It features excellent typo tolerance and search response times under 50 milliseconds, with simple operations via RESTful API. As a self-hostable open-source software optimized for multiple languages including Japanese, it provides an easy-to-use search solution for developers.

Details

MeiliSearch is developed by Meilisearch company in Paris, France, as a next-generation search engine. Leveraging Rust's safety and high performance, it aims to solve the challenges of traditional search engines. It was open-sourced in 2020, with stable version v1.0 released in July 2023.

Key features of MeiliSearch:

  • Ultra-fast search: Average 4 millisecond search response time
  • Strong typo tolerance: Returns appropriate search results even with typos
  • Lightweight design: Operates with low memory usage
  • Multi-language support: Built-in Japanese morphological analyzer Lindera
  • RESTful API: Simple and intuitive API design
  • Faceted search: Filtering by attributes
  • Geospatial search: Location-based search
  • Ranking customization: Adjustable search result ranking rules
  • Synonym functionality: Search expansion with synonyms
  • Highlight functionality: Emphasis display of search keywords
  • Vector search: Semantic search (experimental feature)
  • Multi-tenancy: User-specific access control

Pros and Cons

Pros

  • Excellent typo tolerance: Returns expected results even with input errors
  • Ultra-fast search: 4 millisecond response time for instant result display
  • Lightweight resources: Operates with minimal memory and CPU
  • Easy setup: Can be built in under 5 minutes with Docker
  • Excellent UX: Supports typeahead search and autocomplete
  • Japanese optimization: High-precision Japanese search with Lindera morphological analyzer
  • Developer-friendly: Intuitive API and rich client libraries
  • Open source: Free for commercial use
  • Active development: Continuous feature improvements and community support

Cons

  • Relatively new technology: Less proven track record compared to Elasticsearch
  • Enterprise feature limitations: Advanced clustering features are limited
  • Plugin ecosystem: Not as rich plugin ecosystem as Elasticsearch
  • Distributed processing limitations: Challenges in large-scale distributed environments
  • Complex analysis features: Not as advanced as Elasticsearch's aggregation features
  • Backup tools: Limited enterprise-level backup functionality

Key Links

Code Examples

Docker Execution

# Start MeiliSearch Docker container
docker run -d \
  --name meilisearch \
  -p 7700:7700 \
  -e MEILI_ENV=development \
  -v $(pwd)/meili_data:/meili_data \
  getmeili/meilisearch:v1.3.0

# Production startup with master key
docker run -d \
  --name meilisearch \
  -p 7700:7700 \
  -e MEILI_ENV=production \
  -e MEILI_MASTER_KEY=your-master-key-here \
  -v $(pwd)/meili_data:/meili_data \
  getmeili/meilisearch:v1.3.0

# Check startup
curl http://localhost:7700/health

Index Creation and Document Addition

# Create index
curl -X POST 'http://localhost:7700/indexes' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "uid": "movies",
    "primaryKey": "id"
  }'

# Bulk add multiple documents
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
  -H 'Content-Type: application/json' \
  --data-binary '[
    {
      "id": 1,
      "title": "Spider-Man",
      "genre": ["Action", "Adventure"],
      "director": "Sam Raimi",
      "release_year": 2002,
      "rating": 7.3,
      "overview": "The story of ordinary high school student Peter Parker transforming into Spider-Man"
    },
    {
      "id": 2,
      "title": "The Avengers",
      "genre": ["Action", "Sci-Fi"],
      "director": "Joss Whedon",
      "release_year": 2012,
      "rating": 8.0,
      "overview": "Epic story of Earths mightiest heroes saving the world"
    }
  ]'

# Add single document
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "id": 3,
    "title": "Your Name",
    "genre": ["Animation", "Romance"],
    "director": "Makoto Shinkai",
    "release_year": 2016,
    "rating": 8.2,
    "overview": "Beautiful animation depicting love transcending space and time"
  }'

Basic Search

# Simple search
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Spider"
  }'

# Typo tolerance search (searching "Spider" with "Spide")
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Spide"
  }'

# Limit and offset
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Action",
    "limit": 5,
    "offset": 0
  }'

# Return only specific attributes
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Avengers",
    "attributesToRetrieve": ["title", "director", "release_year"]
  }'

Filtering Search

# Set filterable attributes
curl -X PATCH 'http://localhost:7700/indexes/movies/settings' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "filterableAttributes": ["genre", "release_year", "rating", "director"]
  }'

# Filter by genre
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "filter": "genre = Action"
  }'

# Filter with multiple conditions
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "filter": "release_year > 2010 AND rating >= 8.0"
  }'

# OR condition filtering
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "filter": "director = \"Makoto Shinkai\" OR director = \"Sam Raimi\""
  }'

# Search in array elements
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "filter": "genre IN [Animation, Sci-Fi]"
  }'

Sort Functionality

# Set sortable attributes
curl -X PATCH 'http://localhost:7700/indexes/movies/settings' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "sortableAttributes": ["release_year", "rating", "title"]
  }'

# Sort by rating (descending)
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "sort": ["rating:desc"]
  }'

# Sort with multiple conditions
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "sort": ["release_year:desc", "rating:desc"]
  }'

# Combination of search query and sort
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Action",
    "filter": "release_year >= 2000",
    "sort": ["rating:desc"]
  }'

Highlight Functionality

# Set highlight attributes
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "Spider-Man",
    "attributesToHighlight": ["title", "overview"],
    "highlightPreTag": "<mark>",
    "highlightPostTag": "</mark>"
  }'

# Highlight all attributes
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "hero",
    "attributesToHighlight": ["*"]
  }'

Faceted Search

# Get facet distribution
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "facets": ["genre", "director"]
  }'

# Limit maximum facet values
curl -X POST 'http://localhost:7700/indexes/movies/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "facets": ["genre"],
    "maxValuesPerFacet": 10
  }'

Typo Tolerance Settings

# Check typo tolerance settings
curl -X GET 'http://localhost:7700/indexes/movies/settings/typo-tolerance'

# Disable typo tolerance
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "enabled": false
  }'

# Set minimum character count
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "minWordSizeForTypos": {
      "oneTypo": 4,
      "twoTypos": 10
    }
  }'

# Disable typo tolerance for specific words
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "disableOnWords": ["Avengers", "Spider-Man"]
  }'

# Disable typo tolerance for specific attributes
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "disableOnAttributes": ["director"]
  }'

JavaScript Client

// MeiliSearch client initialization
import { MeiliSearch } from 'meilisearch'

const client = new MeiliSearch({
  host: 'http://localhost:7700',
  apiKey: 'your-api-key' // Required in production
})

// Get index
const index = client.index('movies')

// Add documents
const documents = [
  {
    id: 1,
    title: 'Your Name',
    genre: ['Animation', 'Romance'],
    director: 'Makoto Shinkai',
    release_year: 2016
  }
]

await index.addDocuments(documents)

// Execute search
const searchResults = await index.search('Your Name', {
  attributesToHighlight: ['title'],
  filter: 'release_year > 2015',
  sort: ['release_year:desc'],
  limit: 10
})

console.log(searchResults.hits)

// Typo-tolerant search
const typoResults = await index.search('Yor Nam') // Typo of "Your Name"
console.log(typoResults.hits)

// Faceted search
const facetResults = await index.search('', {
  facets: ['genre', 'director']
})
console.log(facetResults.facetDistribution)

Python Client

# MeiliSearch client initialization
import meilisearch

client = meilisearch.Client('http://localhost:7700', 'your-api-key')
index = client.index('movies')

# Add documents
documents = [
    {
        'id': 1,
        'title': 'The Avengers',
        'genre': ['Action', 'Sci-Fi'],
        'director': 'Joss Whedon',
        'release_year': 2012,
        'rating': 8.0
    }
]

index.add_documents(documents)

# Execute search
search_results = index.search('Avengers', {
    'filter': 'rating >= 8.0',
    'attributesToHighlight': ['title', 'overview'],
    'sort': ['rating:desc']
})

print(search_results['hits'])

# Update settings
index.update_filterable_attributes(['genre', 'rating', 'release_year'])
index.update_sortable_attributes(['rating', 'release_year'])

# Typo tolerance settings
index.update_typo_tolerance({
    'minWordSizeForTypos': {
        'oneTypo': 4,
        'twoTypos': 10
    },
    'disableOnAttributes': ['director']
})

Go Client

package main

import (
    "fmt"
    "github.com/meilisearch/meilisearch-go"
)

func main() {
    // Client initialization
    client := meilisearch.NewClient(meilisearch.ClientConfig{
        Host:   "http://localhost:7700",
        APIKey: "your-api-key",
    })

    // Get index
    index := client.Index("movies")

    // Execute search
    searchRes, err := index.Search("Spider-Man", &meilisearch.SearchRequest{
        Filter:   "release_year > 2000",
        Sort:     []string{"rating:desc"},
        Limit:    10,
        AttributesToHighlight: []string{"title", "overview"},
    })
    
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Found %d results\n", len(searchRes.Hits))
    
    // Update typo tolerance settings
    _, err = index.UpdateTypoTolerance(&meilisearch.TypoTolerance{
        MinWordSizeForTypos: meilisearch.MinWordSizeForTypos{
            OneTypo:  4,
            TwoTypos: 10,
        },
        DisableOnWords: []string{"Avengers"},
    })
    
    if err != nil {
        panic(err)
    }
}

Performance Optimization

# Index optimization (recommended for regular execution)
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
  -H 'Content-Type: application/json' \
  --data-binary '[]'  # Optimize index with empty array

# Batch settings update
curl -X PATCH 'http://localhost:7700/indexes/movies/settings' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "filterableAttributes": ["genre", "release_year", "rating"],
    "sortableAttributes": ["release_year", "rating"],
    "searchableAttributes": ["title", "overview", "director"],
    "displayedAttributes": ["title", "director", "release_year", "rating"],
    "rankingRules": [
      "words",
      "typo",
      "proximity",
      "attribute",
      "sort",
      "exactness",
      "rating:desc"
    ]
  }'