OpenSearch と ActiveRecord の利用用途の違いまとめ
検索エンジン(OpenSearch)と ORM(ActiveRecord)は役割が違う。どちらを使うか迷ったとき用の整理メモ。
関連: インデックスという用語の混同(RDB vs OpenSearch)(用語の整理)、
searchkickのレイヤー構造とOpenSearchからActiveRecordへの境界(Rails での実装の内側)
1. ざっくり結論
|
OpenSearch |
ActiveRecord (+ RDB) |
| 正体 |
検索エンジン(Elasticsearch 系 OSS) |
ORM(背後は MySQL / PostgreSQL 等の RDB) |
| データの位置づけ |
検索用の複製(インデックス) |
正のデータ源(source of truth) |
| 得意なこと |
複雑な条件の高速な絞り込み・全文検索・スコアリング |
データの正確な永続化、トランザクション、関連(association)の取得 |
| 苦手なこと |
データの一貫性保証(複製なので古くなりうる) |
全文検索、多条件の柔軟な組み合わせ検索(SQL が複雑化・低速化) |
| 問い合わせ言語 |
Query DSL(JSON) |
SQL(ORM が生成) |
検索機能では両方を役割分担で使うのが定石。
「誰(どのレコード)がヒットするか」を検索エンジンが決め、「その実データ」を ORM が RDB から取り出す。
2. どちらを使うか
2-1. ActiveRecord(RDB)が向くケース
- CRUD(作成・参照・更新・削除)の基本操作
- トランザクションが必要な処理(決済、ステータス遷移など)
- 関連をたどる取得(
user.posts のような association)
- 「ID や外部キーで特定のレコードを引く」単純な検索
- データの正確性・一貫性が最優先の場面
2-2. OpenSearch / Elasticsearch が向くケース
- 全文検索(キーワードが本文のどこかに含まれる、形態素解析、あいまい一致)
- 多条件の組み合わせ検索(条件グループを AND/OR/NOR で自由に組む検索 UI など)
- ファセット / 集計(検索結果の件数内訳、絞り込みナビ)
- スコアリング・ランキング(関連度順、function_score による重み付け)
- 大量データに対する検索レイテンシの要求が厳しい場面
3. よくある組み合わせパターン
検索エンジンと RDB は「どちらか」ではなく、書き込みと読み込みで併用するのが一般的。
書き込み: アプリ → RDB(正データ)→ 検索エンジンへ同期(インデックス更新)
読み込み: 検索は検索エンジン → ヒット ID で RDB から実体を取得して表示
- 検索エンジンは「検索に最適化した読み取り専用ビュー」として使い、正データは常に RDB に置く
- 表示に使うデータを RDB から取り直すのは、インデックスが古い可能性があるため
(検索結果の一覧性は多少古くても許容し、詳細データは正確に、という割り切り)
注意点
- 同期遅延(Near Real-Time): 2つの要因で直後の検索に反映されないことがある。
- アプリ→検索エンジンの同期処理自体が非同期になりがち
- OpenSearch 自体が近リアルタイム(NRT)で、書き込みはリフレッシュ間隔(デフォルト1秒)を待って初めて検索対象になる
→ 同期的に保存したはずなのに直後の検索にヒットしない、という混乱はこのため
- 二重管理: スキーマ変更時は DB マイグレーションとインデックス定義(mappings)の両方の更新が必要
- 再インデックスコスト: マッピング変更は基本フル再インデックスになる
- 検索エンジンを正データにしない: 消えても RDB から再構築できる状態を保つ
4. わかりやすい例え(図書館モデル)
役割分担を「巨大な図書館」に置き換えると直感的に理解できる。
4-1. 登場人物の対応
| 登場人物 |
図書館での役割 |
何をする人か |
| ActiveRecord (RDB) |
書庫(本棚そのもの) |
本物の本が並ぶ。正のデータ源。 |
| OpenSearch |
蔵書検索カタログ(索引カード) |
「この条件の本はどの棚の何番か」を高速に返す。中身の複製ではなく“場所と目印”だけ持つ。 |
| 検索 API(GraphQL / REST) |
受付カウンター |
利用者の「こういう本ある?」を受け取り、裏で手配して返す窓口。 |
4-2. 本を探すときの流れ
利用者:「料理、和食、1990年以降、写真多め の本ある?」
▼
受付(API)が要望を聞く
▼
カタログ(OpenSearch)で該当する本の "整理番号" を一気に絞り込む
→ 「該当は 12冊、番号は A-3, B-7, C-1 …」(= WHERE 句の仕事)
▼
その番号で 書庫(ActiveRecord)から本物の本を取ってくる
→ 表紙・目次・著者情報など“実データ”はここで初めて手に入る
▼
受付が利用者に本を手渡す
4-3. なぜ2つに分けるのか(核心)
- カタログ(索引)は探すのが速い。 何万冊あっても「和食かつ1990年以降かつ写真多め」を一瞬で絞れる。ただしカタログに本物の中身は載っていない。
- 書庫(本棚)は中身が正確。 ただし条件で端から探すのは遅く大変(=複雑な SQL が遅くなる)。
- だから 「どれが該当するか」はカタログに聞き、「その本の中身」は書庫から取る、と役割を割り切る。
4-4. 注意点も同じ例えで説明できる
- 索引は古くなりうる → 新しく入った本がまだカタログ未反映のことがある(=インデックスの同期遅延)。だから最終的に手渡す本は必ず書庫の本物を取り直す。
- 本を入れたらカタログも更新が要る → 二重管理(DB マイグレーション+インデックス定義)。
- カタログが焼けても書庫があれば作り直せる → 検索エンジンを正データにしない。
4-5. 一言まとめ
OpenSearch は「目次・索引」、ActiveRecord は「本文そのもの」。
索引で当たりを付けて、本文を開いて確認する。人間が分厚い本を読むときと同じことを、システムがやっている。
4-6. 他の例え(好みで差し替え可)
- 通販サイト:OpenSearch=商品検索・絞り込み(一覧はざっくり速く)、RDB=カートに入れて決済する時の正確な在庫・価格。
- 地図アプリ:OpenSearch=「近くのカフェ」を条件で探すピン表示、RDB=店を選んだ後に出る営業時間・電話番号などの正データ。