MongoDBなら検索エンジンが簡単に作れる
形態素解析でインデックスを作って検索する
Mongoでの全文検索 - Docs-Japanese - 10gen Confluenceを参考に、すぐ実装できた
試しに、青空文庫から走れメロス引っ張ってやってみた。
ライブラリ
MongoDB API Docs for python
> easy_install pymongo
SREngine: Sein blog PythonからMeCabを使う。
ごにょごにょやっていれる
やってみる
#!/usr/bin/env python # encoding:utf-8 import pymongo import MeCab import urllib import re mecab = MeCab.Tagger("-Ochasen") con = pymongo.Connection() col = con.test.row def get_source(url): return [ unicode(re.sub("<.*?>","",line),"sjis") for line in urllib.urlopen(url).readlines()] def create_index(): lines = get_source("http://www.aozora.gr.jp/cards/000035/files/1567_14913.html" ) for n, line in enumerate(lines): words = [] # 単語に分割 変な文字列渡すとSWIGでエラーが出るっぽい try: node = mecab.parseToNode( line.encode("utf-8") ) words = get_words( node ) except : pass col.insert( {"at":n, "text":line,"words":words }) col.ensure_index("words") def search(word): for i in col.find({"words":word}): print i["at"],i["text"] def get_words(node): if not node.next: return [node.surface] return [node.surface] + get_words(node.next) if __name__ == '__main__': create_index() # 一回だけ実行 search("セリヌンティウス")
> python text-search.py 23 メロスは激怒した。必ず、かの邪智暴虐(じゃちぼうぎゃく)の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。けれども邪悪に対しては、人一倍に敏感であった。きょう未明メロスは村を出発し、野を越え山越え、十里はなれた此(こ)のシラクスの市にやって来た。メロスには父も、母も無い。女房も無い。十六の、内気な妹と二人暮しだ。この妹は、村の或る律気な一牧人を、近々、花婿(はなむこ)として迎える事になっていた。結婚式も間近かなのである。メロスは、それゆえ、花嫁の衣裳やら祝宴の御馳走やらを買いに、はるばる市にやって来たのだ。先ず、その品々を買い集め、それから都の大路をぶらぶら歩いた。メロスには竹馬の友があった。セリヌンティウスである。今は此のシラクスの市で、石工をしている。その友を、これから訪ねてみるつもりなのだ。久しく逢わなかったのだから、訪ねて行くのが楽しみである。歩いているうちにメロスは、まちの様子を怪しく思った。ひっそりしている。もう既に日も落ちて、まちの暗いのは当りまえだが、けれども、なんだか、夜のせいばかりでは無く、市全体が、やけに寂しい。のんきなメロスも、だんだん不安になって来た。路で逢った若い衆をつかまえて、何かあったのか、二年まえに此の市に来たときは、夜でも皆が歌をうたって、まちは賑やかであった筈(はず)だが、と質問した。若い衆は、首を振って答えなかった。しばらく歩いて老爺(ろうや)に逢い、こんどはもっと、語勢を強くして質問した。老爺は答えなかった。メロスは両手で老爺のからだをゆすぶって質問を重ねた。老爺は、あたりをはばかる低声で、わずか答えた。 42 「そうです。帰って来るのです。」メロスは必死で言い張った。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の帰りを待っているのだ。そんなに私を信じられないならば、よろしい、この市にセリヌンティウスという石工がいます。私の無二の友人だ。あれを、人質としてここに置いて行こう。私が逃げてしまって、三日目の日暮まで、ここに帰って来なかったら、あの友人を絞め殺して下さい。たのむ、そうして下さい。」 48 竹馬の友、セリヌンティウスは、深夜、王城に召された。暴君ディオニスの面前で、佳(よ)き友と佳き友は、二年ぶりで相逢うた。メロスは、友に一切の事情を語った。セリヌンティウスは無言で首肯(うなず)き、メロスをひしと抱きしめた。友と友の間は、それでよかった。セリヌンティウスは、縄打たれた。メロスは、すぐに出発した。初夏、満天の星である。 75 「フィロストラトスでございます。貴方のお友達セリヌンティウス様の弟子でございます。」その若い石工も、メロスの後について走りながら叫んだ。「もう、駄目でございます。むだでございます。走るのは、やめて下さい。もう、あの方(かた)をお助けになることは出来ません。」 83 「待て。その人を殺してはならぬ。メロスが帰って来た。約束のとおり、いま、帰って来た。」と大声で刑場の群衆にむかって叫んだつもりであったが、喉(のど)がつぶれて嗄(しわが)れた声が幽(かす)かに出たばかり、群衆は、ひとりとして彼の到着に気がつかない。すでに磔の柱が高々と立てられ、縄を打たれたセリヌンティウスは、徐々に釣り上げられてゆく。メロスはそれを目撃して最後の勇、先刻、濁流を泳いだように群衆を掻きわけ、掻きわけ、 84 「私だ、刑吏! 殺されるのは、私だ。メロスだ。彼を人質にした私は、ここにいる!」と、かすれた声で精一ぱいに叫びながら、ついに磔台に昇り、釣り上げられてゆく友の両足に、齧(かじ)りついた。群衆は、どよめいた。あっぱれ。ゆるせ、と口々にわめいた。セリヌンティウスの縄は、ほどかれたのである。 85 「セリヌンティウス。」メロスは眼に涙を浮べて言った。「私を殴れ。ちから一ぱいに頬を殴れ。私は、途中で一度、悪い夢を見た。君が若(も)し私を殴ってくれなかったら、私は君と抱擁する資格さえ無いのだ。殴れ。」 86 セリヌンティウスは、すべてを察した様子で首肯(うなず)き、刑場一ぱいに鳴り響くほど音高くメロスの右頬を殴った。殴ってから優しく微笑(ほほえ)み、 88 メロスは腕に唸(うな)りをつけてセリヌンティウスの頬を殴った。
全文検索エンジンとの比較
このようにMongoDBは、検索機能を簡単にするおもしろい機能を持っていますが、上記の通り、これは専用の全文検索エンジンではありません。
専用のエンジンは以下のようなこともできます。
組み込みのstemming機能
クエリにマッチする語句のランキング (MongoDBでもできるかもしれませんが、ユーザがそういうコードを書く必要があります)
一括でのインデックスの構築 (bulk index building)一括でのインデックスの構築は、インデックスの作成を速くしますが、リアルタイムではないという欠点もあります。MongoDBは、リアルタイムでの検索に特に向いています。伝統的なツールは、このような使い方はあまり得意ではありません。
適材適所でやろう。MongoDB環境があるとき、気軽に実装できる、という程度。