CTOの村上です。弊社では一覧検索でMongoDB Atlasを使用しています。
DRIVE SFAではエンティティに対してカスタム項目を設定でき、標準の項目でもカスタム項目でも部分一致検索が出来るという仕様上、途中からパフォーマンス観点の対策として採用しました。
そこで勉強会で技術周知のために共有しました。LLMも使って資料を作りましたのでご了承ください。
https://speakerdeck.com/chie8842/mongodb-atlas-search-nogoshao-jie
🧭 概要
検索エンジンにおけるテキスト検索は、「情報検索(Information Retrieval)」の原理に基づき、インデックス構築 → 検索 → スコアリング の流れで動作します。
日本語の場合、単語境界が曖昧なため、特有の工夫が必要になります。
🧩 1. インデックス(Index)の構築
検索エンジンは全データを都度スキャンする代わりに、テキストを検索しやすい形に変換して保存します。これを「インデックス」と呼びます。
手順
- トークン化(Tokenization)
- 文を単語単位に分ける。
- 例:「東京都渋谷区にある会社です」→
["東京", "都", "渋谷", "区", "会社"]
- 正規化
- 大文字小文字や全角半角、記号を統一。
- ステミング / レンマ化
- 英語では語幹統一(例:「running」「ran」→「run」)。
- 転置インデックス
- 単語 → 出現文書IDの対応表。
| 単語 | 出現文書ID |
|---|---|
| 渋谷 | 1, 4, 9 |
| 会社 | 1, 3, 7 |
| 東京 | 1, 2, 9 |
🔍 2. クエリ解析と検索
⚖️ 3. スコアリングとランキング
TF-IDF
- 単語の出現頻度 × 逆文書頻度で重み付け。
BM25
- TF-IDFを改良し、文書長や単語飽和を考慮。
ベクトル検索
- 文書をベクトル化し、意味の近さ(コサイン類似度など)で検索。
🧠 4. 日本語検索特有の課題
英語はスペース区切りだが、日本語は「これはペンです」のように単語境界が明示されない。
→ トークン化 のために形態素解析や n-gram が使われる。
🈶 5. 形態素解析(morphological analysis)
概要
文を単語単位に分解し、品詞情報を付与。
流れ
例:「東京都渋谷区にある会社です」→「東京/都/渋谷/区/に/ある/会社/です」
メリット
- 意味的に自然な単位で検索可能
- 誤ヒットが少ない
- 活用形も吸収可能
デメリット
- 処理が重い
- 辞書更新が必要
- 新語に弱い
🔢 6. n-gram 手法
概要
テキストを連続した n 文字単位で分割。
例:「渋谷会社」→ ["渋谷", "谷会", "会社"](2-gram)
メリット
- 辞書不要・新語に強い
- 実装が簡単
- 部分一致に強い
デメリット
- 誤ヒットが増える
- インデックスサイズが大きい
- 意味理解ができない
⚖️ 7. 比較表
🔀 8. ハイブリッド運用例
MongoDB Atlas Search / Elasticsearch
{ "mappings": { "fields": { "normalizedName": [ { "type": "string", "analyzer": "kuromoji" }, { "type": "string", "analyzer": "ngram" } ] } } }
- Kuromoji:意味ベースの精密検索
- n-gram:部分一致・補完検索
両者を should 句で組み合わせることで、精度と柔軟性を両立。
🏁 9. まとめ
元々の実装の問題点
- RDBとMongoDBに検索条件がまたいでいるため、それぞれで検索をしてIDを取得し、和集合を取る必要がある
- カスタム項目のテキスト検索が遅い
- Atlas Searchを使うことで高速化はしたが、精度が低い
今入れている改善
- MongoDBを一覧検索専用にして、検索に必要な項目は公式項目や関連エンティティも含めてドキュメント化する
- 単純にテキスト検索するのではなく、n-gramやkeyword検索などを項目の性質に応じて重み付けを変えてスコアリングする
難しい点
- 文書の検索ではないので、曖昧性は不要で厳格性を求められる
- ただしテキスト検索は100%の精度はありえない
- スコアリングしている関係上、スコア順で出すことを想定しているが、項目でのソートが仕様
Q: n-gramもpg_bigmもDRIVE SFAで使っているが、そちらは部分一致と同義で使えている認識。Atlas Searchの場合はなぜ精度の問題が出てくるのか
A: 以下に回答を後日まとめました
pg_bigmとの比較
| 観点 | pg_bigm | MongoDB Atlas Search (nGram Analyzer) |
|---|---|---|
| インデックス単位 | 2文字ごとの固定バイグラム | 可変長 n(例: min=2, max=3) |
| 検索時の挙動 | 文字列類似度(Jaccardベース)を計算 | n-gram の一部一致でもスコアに寄与(ブールではない) |
| マッチ条件 | 全バイグラムの集合比較 | 単語レベル+トークンごと部分マッチ |
| トークン境界 | なし(全文連続文字) | トークナイザが単語・記号を区切る |
| マッチ評価 | 類似度 ≥ 閾値(例: 0.3)でヒット | BM25/Tf-Idfスコア計算。類似度閾値なし |
| 検索結果 | 類似度が高い(部分一致の中でも厳選) | 類似スコアが小さくても「ヒット」として返る |