はじめまして、
今回は私が作っているソースコード検索エンジンのMilkodeにて、

ソースコードを検索するということ
プログラマの仕事はプログラムを書くことですが、
- ある関数の名前を変えたいので使っている箇所をすべて見つけたい
- ライブラリの仕様が変わった。使っているコードはどこだったか?
- コードリーディング中。今読んでいる関数の実体はどこにあるのか?
ベーマガを買ってゲームのプログラムを写経していた頃から時代は進み、
既存の検索ツールの問題点
私もソースコードを検索するためのツールとして色々なものを使ってきました。その内に
Wikipediaの
それぞれのタイプの不満を挙げてみます。
- grep型の不満
- インデックス型の不満
- 複数のキーワードにマッチする行を見つけることができない
- ファイル単位で見つけるものが多い
- 非公開のコードも検索対象に含めたい
- Webアプリのみ提供しているタイプのものはオープンソースしか検索対象にできない
- grep型と同じくらい、
簡単にインストールできるようにしてほしい
- 複数のキーワードにマッチする行を見つけることができない
これらの問題を上手く解決することができればそれなりに需要があるのではないかと考え、
さてどうやって作ろう?
ソースコード検索エンジンを作ろうと思い立ったものの、
- 主要なOSで使える
- (比較的)
簡単にインストールできる - 日本語の情報が豊富
groongaとの出会い
いくつかの検索エンジンを調べていくなかで、
他にも候補はあったのですが、
- Sennaという有名な検索エンジンの後継
(らしい) - 他の検索エンジンと比べると後発のため、
老舗のものにありがちな問題が解消されていそう - 日本人の開発者を中心に作られているので日本語の情報が豊富
- MLのログを読むと質問やバグ報告に対するレスポンスも速い
- 若いプロジェクトなのでパッチも積極的に受け入れてくれそう
正直検索エンジンに対する知識が皆無だったため、
その後、
もう少し真剣にリファレンスマニュアルなどを読み始め、
- 単体で全文検索+ストレージとして動作する
- Rubyだけで書ける
- ライブラリとして動作する
順に説明していきます。
1. 単体で全文検索+ストレージとして動作する
groongaの場合は、
- ファイルの内容をデータベースに格納
- 格納されたデータを全文検索
別途MySQLなどのストレージエンジンのインストールを要求するタイプもあったのですが、
2. Rubyだけで書ける
groonga本体はC言語で書かれていますが、
require 'rubygems'
require 'groonga'
# データベースの作成
Groonga::Context.default_options = {:encoding => :utf8}
Groonga::Database.create(:path => "bookmark.db")
# テーブルの定義
Groonga::Schema.define do |schema|
Groonga::Schema.create_table("Items", :type => :hash) do |table|
table.text("title")
end
Groonga::Schema.create_table("Terms",
:type => :patricia_trie,
:key_normalize => true,
:default_tokenizer => "TokenBigram") do |table|
table.index("Items.title")
end
end
# レコードの追加
items = Groonga["Items"]
items.add("http://gihyo.jp" , title:"技術評論社")
items.add("http://ja.wikipedia.org/wiki/Ruby", title:"Ruby")
items.add("http://www.ruby-lang.org/ja" , title:"オブジェクトスクリプト言語Ruby")
# 全文検索
result = items.select {|record| record.title =~ "Ruby"}
# 結果を集計
p result.map{|record| record.title} #=> ["Ruby", "オブジェクトスクリプト言語Ruby"]
Milkodeではrroongaを使っており、
3. ライブラリとして動作する
rroongaはRubyGemsライブラリとして提供されているため、
$ gem install rroonga
bundlerであればGemfileに次の記述を一行追加するだけです。とっても簡単ですね!
gem 'rroonga'
すべての機能はRubyのクラスやメソッドとして提供されているため、
これらのメリットと、
Milkodeでの使い方
Milkodeでは、
- ソースコードのテーブル
(documents) - パッケージのテーブル
(packages) (※5)
特に重要なのはdocumentsテーブルです。検索対象となるすべてのファイルが1つのテーブルに格納されています。
- 1ファイル == 1レコード として登録している
- パッケージごとにテーブルを分けたりはしない
- 全文検索のクエリとしてpackageカラムを指定した方が高速なため
- 拡張子はソースコードにとって大切な情報なので専用カラムを作成
- .cと.cppを区別することができる
documentsテーブルのスキーマ
それではdocumentsテーブルのスキーマ情報をみてましょう。
カラム名 | 型 | 説明 | 実例 | 全文検索 |
---|---|---|---|---|
path | string | ファイルの絶対パス | /path/ | ○ |
package | string | パッケージ名 | proj | ○ |
restpath | string | パッケージ名以降のパス | src/ | ○ |
content | text | ファイルの内容 | aaa\nbbb\nccc | ○ |
timestamp | time | ファイルのタイムスタンプ | 2012-11-21 23:26:00 +0900 | - |
suffix | text | 拡張子 | txt | ○ |
定義するためのRubyコード
実際にスキーマを定義しているのは/milkode/
module Milkode
class DocumentTable
def self.define_schema
Groonga::Schema.define do |schema|
schema.create_table("documents", :type => :hash) do |table|
table.string("path")
table.string("package")
table.string("restpath")
table.text("content")
table.time("timestamp")
table.text("suffix")
end
schema.create_table("terms",
:type => :patricia_trie,
:key_normalize => true,
:default_tokenizer => "TokenBigramSplitSymbolAlphaDigit") do |table|
table.index("documents.path")
table.index("documents.package")
table.index("documents.restpath")
table.index("documents.content")
table.index("documents.suffix")
end
end
end
さらに詳しい使い方を知りたい人は、
とりあえず完成 「行指向のソースコード検索エンジンMilkode」
こうしてrroongaのおかげでMilkodeができました。ブラウザで検索すると次のような画面になります。

他の検索ツールと比較した時の特徴として、
- インデックス型のため、
数万オーダーのファイルが登録されていても1秒以内に検索できる [6] - しかし、
grep型のように複数のキーワードを含む行を見つけることができる - ブラウザからもコマンドラインからも検索することができる
- 個人用のデスクトップアプリケーションとしても使えるし、
デプロイして共有のWebアプリにすることもできる
Milkodeのインストールはgemから行います。
$ gem install milkode
それにより、
コマンド | 概要 |
---|---|
milk | gitライクなコマンドを使って検索したいディレクトリを登録 |
milk web | Webアプリを立ち上げてブラウザから検索 |
gmilk | grepのようにコマンドラインから検索 |
mcd | 登録したディレクトリ間を素早く移動 |
さらに詳しい使い方はhttp://
派生物として、
まとめ
rroongaを使って良かった点はやはり検索の高速性です。勉強会などで人に見せる機会があるとほとんどの人が検索結果が返ってくるまでのスピードに驚いてくれます。grep型のものと比べると驚く程速いです。また、
困った点として、
全文検索というとWebアプリのものというイメージがありますが、
次回予告
次回はgroongaをライブラリとして使っているプロダクトの紹介です。今回でてきたrroongaについて詳しく紹介します。rroongaが大事にしていることはいったいなんでしょう。お楽しみに!