まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:fulltext

IBM Bluemix からも提供されている NoSQL 型 DBaaS の1つが Cloudant です:
2016071301


ベースとなる製品はオープンソースの Apache CouchDB です。私自身も以前は疑問に思っていたのですが、たまに聞かれる質問の1つに「Cloudant と Apache CouchDB は何が違うのか?」があります。

Cloudant は(ソフトウェア単体版も存在していますが) DBaaS としてクラウドで提供されているとか、 IBM の管理下で利用できる(サーバーインフラを自分が管理しなくてよい)とか、運用面での違いは比較的わかりやすいのですが、機能面での決定的な違いについてあまり目にする機会がありませんでした。

その違いの1つが今回紹介する「全文検索」機能です。一般的に NoSQL DB はスケーラビリティにすぐれた大容量データの保存に向くストレージシステムですが、SQL のようなクエリー言語が使えないこともあり、柔軟な検索はあまり得意ではありません。全文検索に関しては SQL DB であれば、
> select * from xxx where name like '%テスト%'

のような指定で「name にテストという文字を含むもの」が検索できます。パフォーマンス対策とか色々考慮すべき点はありますが、処理そのものは単純だし簡単・便利に実現できてしまいます。

一方で、一般的な NoSQL DB には全文検索を実現するためのこのような便利な機能がありません。Apache CouchDB でもインデックスを作成することで「完全一致検索」は実現できますが、「全文検索」はそうはいきません。Apache Lucene や ElasticSearch などの全文検索エンジンを使って NoSQL DB とは別に全文検索機能を実装し、併用して運用していくことになります。この方法はデータの一元管理が難しく、また DB と検索エンジンの整合性も併せて管理する必要が生じるため、比較的運用負荷の高い実現方法になってしまいます。

実はこの点で Cloudant は Apache CouchDB とは違います。Cloudant の検索機能には Apache Lucene が初めから含まれており、特に Lucene の存在を意識することなく全文検索インデックスが使えるようになっています。その使い方を紹介します。


まずは Cloudant のインスタンスを用意します。IBM Bluemix アカウントをお持ちであれば、サービスとして Cloudant を1インスタンス用意いただくのが一番簡単だと思います。IBM Bluemix アカウントがない場合は http://cloudant.com/ に直接サインアップしていただいても構いません。以下は前者の前提で紹介します。

作成した Cloudant の環境変数を参照して、Cloudant サービスを利用するための接続情報を確認します:
2016071301


サービス接続情報の JSON を確認し、credentials.url の値をメモしておきます(後で使います)。ここでは credentials.url の値が以下のようになっていると仮定して続きを紹介します:
{
  "credentials": {
    "username": "USERNAME",
    "password": "PASSWORD",
    "host": "USERNAME.cloudant.com",
    "port": 443,
    "url": "https://USERNAME:PASSWORD@USERNAME.cloudant.com"
  }
}


まずは Cloudant のダッシュボード画面にアクセスしてみます。まだ何も作業していなければ特にデータベースも作られてなく、データベース一覧は空の状態のはずです。ここでは今回の作業用に1つデータベースを追加することにします。画面右上の "Create Database" をクリックします:
2016071302


新規に作成するデータベースの名称を入力するよう求められるので適当に(以下の例では "mydb" と)入力して "Create" をクリックします:
2016071303


mydb データベースが新規に作成されました。が、(当たり前ですが)まだこのデータベースには何のドキュメントも登録されていません:
2016071304


検索作業用にいくつかのドキュメントを追加してみましょう。+印から "New Doc" を選択します:
2016071305


以下の様なドキュメント編集画面が表示されるはずです。ここに登録するドキュメントの内容を JSON 形式で入力していきます:
2016071306


今回は以下の様に "name" と "desc" という2つのフィールドを持つドキュメントを生成することにします("_id" は初めから入っている値をそのまま使います):
{
 "_id": "XXXXXXXX....XXX",
 "name": "Natural Language Classifier",
 "desc": "自然言語テキストを学習してカテゴリを分類する"
}

この状態でドキュメントを保存します。"Create Document" と書かれたボタンをクリックします:
2016071307


ドキュメントが保存されました。mydb データベースのドキュメント一覧からも参照できますが、指定した "name" や "desc" の値はこの画面からは確認できません。ドキュメントの内容を確認するには該当ドキュメントの右上の鉛筆マークをクリックします:
2016071308


"name" や "desc" 、そして追加された "_rev" の値なども確認することができます。正しく格納されているようです。一度 "<" 印をクリックして元の画面に戻ります:
2016071309


同様にしていくつかのドキュメントを追加していきましょう。上記の作業を繰り返して、とりあえず以下の3つのドキュメントが登録された状態にしておきます:
#name の値desc の値
1Natural Language Classifier自然言語テキストを学習してカテゴリを分類する
2Personality Insights入力されたテキストから性格を分析する
3Tradeoff Analytics優先順位を意識したトレードオフ判断を視覚化する


ではこのデータベースに全文検索の索引を追加して全文検索ができるようにします。mydb のメニュー画面から Design Documents 横の+印をクリックし、サブメニューから "New Search Index" を選択します:
2016071301


索引に関する情報を入力する画面に切り替わります。ここではデザイン ID ("_design/" と書かれた右)に "mydbdoc" と、インデックス名("Index name" と書かれた下)に "mydbsearch" とそれぞれ入力します:
2016071302


続けて下にスクロールし、Search Index function の中身を以下のように書き換えます:
function(doc){
 if ('name' in doc) {
  var search_this = [doc.name, doc.desc];
  index('default', search_this.join(' '));
  index('name', doc.name, {store: 'yes'});
  index('desc', doc.desc, {store: 'yes'});
 }
}
  ↑ "name" というフィールドを持つドキュメントに対して、
   "name" と "desc" の2フィールドを対象に検索インデックスを作成するようにしています。


最後に Type には "Japanese" を選択して、一番下の "Create Document and Build Index" をクリックします。これで検索索引が作成され、かつ索引付けが実行されて検索が可能になります:
2016071303


元の画面に戻ると "Build Indexes" 欄が追加されており、その中に作成したインデックス名称(mydbsearch)が表示されているはずです。これを選択すると検索のプレビューを確認することができるので、本当にこれだけで全文検索が可能になったのか試してみましょう:
2016071304


画面右の検索ボックスに「テキスト」と入力してみます(上記3ドキュメントと同じデータを登録していれば、3つのうちの2つのドキュメントが「テキスト」という文字列を含んでいるので、正しく検索できれば結果は2ドキュメントになることが予想されます):
2016071305


全文検索が実行され、画面のように2ドキュメントが検索結果にリストされました。正しく全文検索が実行されたことが確認できました:
2016071306


ちなみに、この検索処理を API で実行する場合は以下の URL に対して GET リクエストを実行することになります:
https://USERNAME:PASSWORD@USERNAME.cloudant.com/mydb/_design/mydbdoc/_search/mydbsearch?q=テキスト

指定する URL は以下の形になっています:
(上述の環境変数で取得した青字部分)/(DB名)/_design/(デザインID)/_search/(インデックス名)?q=(検索文字列)

試しにこの文字列をブラウザのアドレスバーに入力して実行すると、同じ2件の検索結果が得られるはずです:
2016071307


というわけで、Cloudant サービスには標準で Lucene ベースの全文検索エンジンが搭載されており、Apache CouchDB 同様に簡単に使うことができる上、NoSQL DB に苦手な全文検索が簡単に実現・実装することができる、という特徴を持っていることが確認できました。

(参考)
https://cloudant.com/product/cloudant-features/cloudant-search/

ケースバイケースではあるのですが、まだ自分の中で明確な答が出ていない問題です。

ウェブサイトを作る時に、そのサイト内のページや情報を検索するための「サイト内検索」、これを用意するべきかどうか、用意するとしたらどうやって用意するか、という問題です。
2014050301
↑こういうやつね


普通は「え?そりゃ無いよりあった方がいいでしょ?」と思いますよね。ではサイト内検索機能を用意する前提でもう少し細かく考えてみます。


サイト内検索を自前で用意する場合、開発コスト/運用コストによって選択肢はいくつかあります。まず手っ取り早いのは「DB内を全文検索する」ような検索実装です。端的に言えば SQL で select * from XXX where title like '%キーワード%' or category like '%キーワード%' or body like '%キーワード%' みたいなのを実行する、というやり方です。これだとウェブ/DBサーバーの負荷はその分上がりますが、サーバー構成の変化をあまり意識する必要がなく、懐にやさしい実装と言えます。

ただし、このやり方はいわゆる「ゆらぎ」や「同義語」に対応した検索ができないので、指定したキーワードにピッタリマッチした場合だけヒットします(近いキーワードではヒットしません)。そのため利用者視点では期待するような検索結果にならず、必ずしもユーザビリティはよくありません。また RDB の SQL を使うこの方法は複雑な条件での検索に対応できる一方で、検索速度については(KVS 型などと比較して)遅くなってしまいます。速度低下を避けるためにディスク高速化やメモリキャッシュを使うなどハードウェアで対処することもできますが、これはサーバーコストにまともに跳ね返ってしまうので、経済的というメリットが薄くなってしまいます。またクラウドサーバーでの運用を考えている場合、そもそもクラウドベンダーがハードウェア高速化を提供しているかどうかも事前に調べておく必要があります。そこまでして実現しても、利用者が検索にグーグル並の使い勝手を期待しているような場合、ちょっと残念な検索機能という印象を持たれてしまうかもしれません。


検索専用に別サーバーを用意してもいい、という費用感であれば、検索サーバーを立ち上げて検索機能に関しては検索サーバーに一任する、という方法も考えられます。このケースでは検索サーバーをオープンソース製品などを使って自前で用意してもいいし、商用の検索サービスを提供している業者と契約する方法も考えられます。検索サーバーの細かな機能や得意・不得意機能は千差万別ですが、対象レコード数や検索クエリー頻度、検索速度で検索サーバーの規模を概算して、それに見合ったサーバーを用意することになります。検索専用サーバーであればほとんどの場合で「ゆらぎ」や「同義語」にも対応できますが、その辞書メンテナンスを含めた管理を自分達で行うのか、そこも業者に任せるのか、どこまで任せるのか、そしてそれはいくらかかるのか、というコスト感と合わせて判断することになります。

この方法の問題点として、検索サービス専用業者との契約はその他のサーバー運用費と比較して結構高コストになる、という印象を持っています。また検索エンジンは途中から「やっぱりいらない」とは(アプリ設計から見直すことになるため)しにくいのです。したがって、一度導入すると定常的に(安くない)運用コストがかかる要素と言えます。


そして、個人的にはここが一番気になるのですが、「そもそもサイト利用者はサイト内検索をどれだけ必要としているのか?」という問題があります。上記のようにユーザビリティの高いサイト内検索を(コストもかけた上で)用意したとして、ユーザーのサイト内検索にはどれだけの需要があるのでしょうか?

これは提供するサービスの内容にもよるとは思います。例えば有名人のブログやニュースサイトなど読み物コンテンツ中心だったり、利用者が特定の何かの情報を探しに来ているのであれば、利用者はトップページから参照して、過去の情報も含めてサイト内を検索する、という行動も珍しくないと思っています。そのようなケースに該当する場合、サイト内検索は必要と考えるべきでしょう。 

でも現実問題として、多くのケースで利用者はトップページから情報を探すわけではなく、グーグルなどの検索エンジンから見つけたコンテンツに直接(トップページを経由せずに)訪問してきます。そうして訪問してきた利用者を同じサイト内の他コンテンツにも誘導したい気持ちは分かるのですが、そこでどれだけの利用者がサイト内を検索するのでしょうか? 例えば人気コンテンツや関連コンテンツ、リコメンドといったリンクを用意しておいて、そこから誘導させる手段は(自分の行動を顧みても)ある程度の効果があると思うのですが、グーグル検索結果からふと訪れたサイトで、わざわざ再度サイト内検索をしてまで別の情報を探すか?と言われると、ほとんどしていないように思うのです。


ここで改めて冒頭の疑問にたち戻ります。サイト内検索機能は比較的高いコストを要して用意することになるのですが、本当にサイト内検索はコストに見合うだけ使われるのでしょうか?それともコストをかけて用意するだけの必要性はないと考えるべきでしょうか? これが自分の中で答の出ていない問題なのでした。

そしてもう1つ。ユーザーはサイト内を検索することはなくても、グーグルなどのウェブ検索をすることは一般的と言えます。であれば SEM/SEO 対策を効果的に行ってユーザーの検索結果に自分達のコンテンツをある程度うまく表示させることができていれば、グーグルをサイト検索代わりにすることができるようになります。検索時に「検索キーワードに続けてサービス名も入れてもらう」とかであれば比較的すぐに検索結果を上位に持ってくることもできると思うし、グーグル検索であればかなり複雑なゆらぎにも対応した検索を提供してくれるし、その実装については悩む必要もありません。

極端な言い方かもしれませんが、検索エンジンにコストをかけるのであれば、その分を SEO 対策やマーケティングに費やしてもいいのでは? と考えてしまうのでした。


ただ例外もあります。キーワード以外で、例えば位置情報や画像やらユーザー名やらで検索するような場合はグーグル検索に任せられないので、こういうのは自前で用意しないといけないですよね。


というわけでまとめると、サイト内検索機能というのは、、
- 実装・運用にコストがかかる割には
- あまり使われない可能性があって、
- しかも SEM/SEO 対策次第でグーグル検索の方が高い精度でサイト内検索の代わりになるかもしれない

といった傾向が考えられるのです。 
そんな中でもコストをかけて柔軟なサイト内検索を実装/実現すべきなのか? という問題を考えていたのでした。簡易的な全文検索にするか、それ以上を求めるならグーグルの SEO 対策を強化する、でもいいのかな、と。

あらためて色んなサイトを見直してみると、サイト内検索を実装していない所も珍しくないですよね。このブログにもありません。最近は「意外となくてもいいんじゃないかなあ」と思うようになってきたのでした。

#っていうか、検索の SaaS って高すぎて個人サービスレベルじゃ手が出せない・・・ (x_x )
 

このページのトップヘ