データベース
MeiliSearch
概要
MeiliSearchは、Rust言語で開発された軽量で高速な全文検索エンジンです。特に優れたタイポ耐性と50ミリ秒以下の検索応答速度を誇り、RESTful APIによる簡単な操作が可能です。セルフホスト可能なオープンソースソフトウェアとして、日本語を含む多言語に最適化されており、開発者にとって扱いやすい検索ソリューションを提供します。
詳細
MeiliSearchは、フランス・パリのMeilisearch社によって開発された次世代検索エンジンです。Rust言語の安全性と高性能を活かし、従来の検索エンジンの課題を解決することを目的としています。2020年にオープンソース化され、2023年7月に安定版v1.0がリリースされました。
MeiliSearchの主な特徴:
- 超高速検索: 平均4ミリ秒の検索応答時間
- 強力なタイポ耐性: 誤字脱字があっても適切な検索結果を返す
- 軽量設計: 低メモリ使用量で動作
- 多言語サポート: 日本語形態素解析器Lindera内蔵
- RESTful API: シンプルで直感的なAPI設計
- ファセット検索: 属性による絞り込み検索
- 地理的検索: 位置情報ベースの検索
- ランキングカスタマイズ: 検索結果の順位付けルール調整
- シノニム機能: 類義語による検索拡張
- ハイライト機能: 検索キーワードの強調表示
- ベクトル検索: セマンティック検索(実験的機能)
- マルチテナンシー: ユーザー別アクセス制御
メリット・デメリット
メリット
- 優秀なタイポ耐性: 入力ミスがあっても期待する結果を返す
- 超高速検索: 4ミリ秒の応答時間で即座に結果表示
- 軽量リソース: 少ないメモリとCPUで動作
- 簡単セットアップ: Dockerで5分以内に構築可能
- 優れたUX: Typeahead検索やオートコンプリート対応
- 日本語最適化: Lindera形態素解析器による高精度日本語検索
- 開発者フレンドリー: 直感的なAPIと豊富なクライアントライブラリ
- オープンソース: 無料で商用利用可能
- アクティブな開発: 継続的な機能改善とコミュニティサポート
デメリット
- 比較的新しい技術: Elasticsearchと比べて実績が少ない
- エンタープライズ機能制限: 高度なクラスタリング機能は限定的
- プラグインエコシステム: Elasticsearchほど豊富なプラグインがない
- 分散処理制限: 大規模分散環境での運用は課題
- 複雑な分析機能: Elasticsearchの集約機能ほど高度ではない
- バックアップツール: エンタープライズレベルのバックアップ機能が限定的
主要リンク
書き方の例
Docker での実行
# MeiliSearchのDockerコンテナを起動
docker run -d \
--name meilisearch \
-p 7700:7700 \
-e MEILI_ENV=development \
-v $(pwd)/meili_data:/meili_data \
getmeili/meilisearch:v1.3.0
# マスターキーを指定した本番環境での起動
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
# 起動確認
curl http://localhost:7700/health
インデックス作成とドキュメント追加
# インデックス作成
curl -X POST 'http://localhost:7700/indexes' \
-H 'Content-Type: application/json' \
--data-binary '{
"uid": "movies",
"primaryKey": "id"
}'
# 複数ドキュメントの一括追加
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
-H 'Content-Type: application/json' \
--data-binary '[
{
"id": 1,
"title": "スパイダーマン",
"genre": ["アクション", "冒険"],
"director": "サム・ライミ",
"release_year": 2002,
"rating": 7.3,
"overview": "平凡な高校生ピーター・パーカーがスパイダーマンに変身する物語"
},
{
"id": 2,
"title": "アベンジャーズ",
"genre": ["アクション", "SF"],
"director": "ジョス・ウェドン",
"release_year": 2012,
"rating": 8.0,
"overview": "地球最強のヒーローチームが世界を救う壮大な物語"
}
]'
# 単一ドキュメントの追加
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
-H 'Content-Type: application/json' \
--data-binary '{
"id": 3,
"title": "君の名は。",
"genre": ["アニメ", "ロマンス"],
"director": "新海誠",
"release_year": 2016,
"rating": 8.2,
"overview": "時空を超えた恋愛を描く美しいアニメーション作品"
}'
基本検索
# シンプル検索
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "スパイダー"
}'
# タイポ耐性検索("スパイダー"を"スパイダ"で検索)
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "スパイダ"
}'
# 制限とオフセット
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "アクション",
"limit": 5,
"offset": 0
}'
# 特定の属性のみ返す
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "アベンジャーズ",
"attributesToRetrieve": ["title", "director", "release_year"]
}'
フィルタリング検索
# フィルタリング属性の設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings' \
-H 'Content-Type: application/json' \
--data-binary '{
"filterableAttributes": ["genre", "release_year", "rating", "director"]
}'
# ジャンルによるフィルタリング
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"filter": "genre = アクション"
}'
# 複数条件でのフィルタリング
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条件でのフィルタリング
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"filter": "director = \"新海誠\" OR director = \"サム・ライミ\""
}'
# 配列要素での検索
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"filter": "genre IN [アニメ, SF]"
}'
ソート機能
# ソート可能属性の設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings' \
-H 'Content-Type: application/json' \
--data-binary '{
"sortableAttributes": ["release_year", "rating", "title"]
}'
# 評価順(降順)でソート
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"sort": ["rating:desc"]
}'
# 複数条件でのソート
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"sort": ["release_year:desc", "rating:desc"]
}'
# 検索クエリとソートの組み合わせ
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "アクション",
"filter": "release_year >= 2000",
"sort": ["rating:desc"]
}'
ハイライト機能
# ハイライト属性の設定
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "スパイダーマン",
"attributesToHighlight": ["title", "overview"],
"highlightPreTag": "<mark>",
"highlightPostTag": "</mark>"
}'
# 全属性をハイライト
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "ヒーロー",
"attributesToHighlight": ["*"]
}'
ファセット検索
# ファセット分布の取得
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"facets": ["genre", "director"]
}'
# 最大ファセット値の制限
curl -X POST 'http://localhost:7700/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"facets": ["genre"],
"maxValuesPerFacet": 10
}'
タイポ耐性設定
# タイポ耐性設定の確認
curl -X GET 'http://localhost:7700/indexes/movies/settings/typo-tolerance'
# タイポ耐性の無効化
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
-H 'Content-Type: application/json' \
--data-binary '{
"enabled": false
}'
# 最小文字数の設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
-H 'Content-Type: application/json' \
--data-binary '{
"minWordSizeForTypos": {
"oneTypo": 4,
"twoTypos": 10
}
}'
# 特定の単語でタイポ耐性を無効化
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
-H 'Content-Type: application/json' \
--data-binary '{
"disableOnWords": ["アベンジャーズ", "スパイダーマン"]
}'
# 特定の属性でタイポ耐性を無効化
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/typo-tolerance' \
-H 'Content-Type: application/json' \
--data-binary '{
"disableOnAttributes": ["director"]
}'
ランキングルール設定
# ランキングルールの確認
curl -X GET 'http://localhost:7700/indexes/movies/settings/ranking-rules'
# カスタムランキングルールの設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/ranking-rules' \
-H 'Content-Type: application/json' \
--data-binary '[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"rating:desc"
]'
# 検索可能属性の設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/searchable-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
"title",
"overview",
"director",
"genre"
]'
シノニム設定
# シノニムの設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/synonyms' \
-H 'Content-Type: application/json' \
--data-binary '{
"映画": ["ムービー", "フィルム"],
"アクション": ["バトル", "戦闘"],
"SF": ["サイエンスフィクション", "未来"]
}'
# シノニムの確認
curl -X GET 'http://localhost:7700/indexes/movies/settings/synonyms'
ストップワード設定
# ストップワードの設定
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/stop-words' \
-H 'Content-Type: application/json' \
--data-binary '["の", "を", "に", "が", "は", "で", "と", "から"]'
# ストップワードの確認
curl -X GET 'http://localhost:7700/indexes/movies/settings/stop-words'
インデックス管理
# インデックス一覧取得
curl -X GET 'http://localhost:7700/indexes'
# インデックス情報取得
curl -X GET 'http://localhost:7700/indexes/movies'
# インデックス統計情報
curl -X GET 'http://localhost:7700/indexes/movies/stats'
# インデックス削除
curl -X DELETE 'http://localhost:7700/indexes/movies'
# 全設定の確認
curl -X GET 'http://localhost:7700/indexes/movies/settings'
# 全設定のリセット
curl -X DELETE 'http://localhost:7700/indexes/movies/settings'
JavaScriptクライアント
// MeiliSearchクライアントの初期化
import { MeiliSearch } from 'meilisearch'
const client = new MeiliSearch({
host: 'http://localhost:7700',
apiKey: 'your-api-key' // 本番環境では必須
})
// インデックス取得
const index = client.index('movies')
// ドキュメント追加
const documents = [
{
id: 1,
title: '君の名は。',
genre: ['アニメ', 'ロマンス'],
director: '新海誠',
release_year: 2016
}
]
await index.addDocuments(documents)
// 検索実行
const searchResults = await index.search('君の名は', {
attributesToHighlight: ['title'],
filter: 'release_year > 2015',
sort: ['release_year:desc'],
limit: 10
})
console.log(searchResults.hits)
// タイポ耐性のある検索
const typoResults = await index.search('きみのなま') // 「君の名は」のタイポ
console.log(typoResults.hits)
// ファセット検索
const facetResults = await index.search('', {
facets: ['genre', 'director']
})
console.log(facetResults.facetDistribution)
Python クライアント
# MeiliSearchクライアントの初期化
import meilisearch
client = meilisearch.Client('http://localhost:7700', 'your-api-key')
index = client.index('movies')
# ドキュメント追加
documents = [
{
'id': 1,
'title': 'アベンジャーズ',
'genre': ['アクション', 'SF'],
'director': 'ジョス・ウェドン',
'release_year': 2012,
'rating': 8.0
}
]
index.add_documents(documents)
# 検索実行
search_results = index.search('アベンジャーズ', {
'filter': 'rating >= 8.0',
'attributesToHighlight': ['title', 'overview'],
'sort': ['rating:desc']
})
print(search_results['hits'])
# 設定更新
index.update_filterable_attributes(['genre', 'rating', 'release_year'])
index.update_sortable_attributes(['rating', 'release_year'])
# タイポ耐性設定
index.update_typo_tolerance({
'minWordSizeForTypos': {
'oneTypo': 4,
'twoTypos': 10
},
'disableOnAttributes': ['director']
})
Go クライアント
package main
import (
"fmt"
"github.com/meilisearch/meilisearch-go"
)
func main() {
// クライアント初期化
client := meilisearch.NewClient(meilisearch.ClientConfig{
Host: "http://localhost:7700",
APIKey: "your-api-key",
})
// インデックス取得
index := client.Index("movies")
// 検索実行
searchRes, err := index.Search("スパイダーマン", &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))
// タイポ耐性設定更新
_, err = index.UpdateTypoTolerance(&meilisearch.TypoTolerance{
MinWordSizeForTypos: meilisearch.MinWordSizeForTypos{
OneTypo: 4,
TwoTypos: 10,
},
DisableOnWords: []string{"アベンジャーズ"},
})
if err != nil {
panic(err)
}
}
パフォーマンス最適化
# インデックス最適化(定期実行推奨)
curl -X POST 'http://localhost:7700/indexes/movies/documents' \
-H 'Content-Type: application/json' \
--data-binary '[]' # 空の配列でインデックスを最適化
# 設定のバッチ更新
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"
]
}'