関口宏司のLuceneブログ

OSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
Solr新機能:ツッコミ検索とは?
最近はこちらのブログ投稿がご無沙汰である。なぜならJAISTに入学し、Twitterも始めたせいもある。

Twitterは長い文章を考えなくていいから楽だ。しかし、これから述べようと思っているまとまった文章などは量的にTwitterでは無理なので、必然的にブログということになる。何を述べるかというと、最近いわゆる「もしかして検索」という機能を実装し、そこでいろいろ思い巡らせたことについてである。もしかして検索というのは、ユーザがたとえば「パートな湿布」というキーボードを打ち損じた(または変換し損ねた)キーワードで検索したときに、検索結果画面で「もしかして:パートナーシップ」などと正解と思われるキーワードを表示する機能である。そしてただ単に正解と思われるキーワードを表示するだけではなくて、キーワードにリンクを貼って、もしそれが本当に正解キーワードであったならユーザがクリックするだけで正解キーワードですぐさま再検索できるようにする、大変優れた機能である:

[プレスリリース] パートな湿布?いいえ、パートナーシップでした 。〜もしかして検索が可能なSolrサブスクリプションの新版を発表

(詳細記事)日本語「もしかして」検索について

ところで「もしかして検索」というのはGoogleで表示される文言「もしかして○○」からとって命名したものであるが、(上記2つめの記事にもあるように)Yahoo!では「○○ではありませんか?」となり、もしこちらから命名するとなると「ではありませんか?検索」となる。ただ、私の記憶が正しければ、この種の機能はGoogleが早かったような気がするので、以降ではGoogleに敬意を表して「もしかして検索」あるいは省略して「もしかして」と呼ぶことにする。

そして私がまず気がつくのは、Googleの姿勢の低さだ。あのGoogleが「もしかして」である。本当はかなりの確信を持ってリンクを提示しているはずなのだが、あくまでも低姿勢に「もしかして」である。しかし、低姿勢さはYahoo!も負けてはいない。「ではありませんか?」だ。「もしかして」よりも謙虚さを感じる。そこまで自信がないのなら提示しなければいいのに、あくまでもYahoo!はいうのだった。「ではありませんか?」と。

しかし、両社の低姿勢ぶりは私は実はうなずけるところでもある。なぜならこれは確率の問題だからだ(最近私は確率の勉強をしているのだった)。なので絶対ということはなく、「もしかして」ということになる。やろうと思えばおそらく、「何パーセントの確率で○○です」と提示できるはずである。

ロンウイットのもしかしての方法はどうかというと、確率ではなくSolrのSpellCheckComponentを使っているので、もう少し原始的だ。しかしインデックス内の正解データを使っているので、こちらも高い精度が得られている。しかし私も日頃から謙虚でありたいと考える人間であり、ロンウイットはGoogleやYahoo!の足元にも及ばない会社なので、検索結果画面に表示する文言は両社よりももっと謙虚であってしかるべきである。

たとえば、こんなのはどうだろう:

「まちがってたら申し訳ないんですが、ひょっとしたら○○ではありませんか」

相当自信がなさそうなのだった。これくらいであればもし間違っていたとしてもユーザから文句は出ないだろう。しかし、これでもまだ心配な場合は、どうすればいいか。

究極の方法は「あえて何も言わない」だ。ユーザが間違ったキーワードで検索を行う。しかし、こちらも絶対の自信がないのであえて何も言わない。つまりこうだ:

「・・・・・・」

するとユーザは検索結果一覧画面を見て、自らキーワードの打ち損じに気がつく。そして今度は落ち着いて正しいキーワードで検索し直すだろう。すると、Solrはようやく先ほどの自身がはじきだした正解キーワードと、ユーザが2回目に入れた正しいキーワードが等しいことを確認し、100%の確信を持って今度こそ検索結果画面に次のような文言を表示するのだった:

「ですよね〜。私もそう思ったんですけど、違うかもしれないので黙ってたんですけど、やっぱり打ち損じでしたね〜」

しかしこれでは意味がわからないし、CPUの無駄遣いであり、ユーザから別の文句も出そうだ。

逆はどうだろう。ロンウイットはこれでも一応ベンチャーの気概を忘れてはいない。そこであえて100%、いや、120%の確信を持って正解と思われるキーワードを提示したいと考えるのだった。つまりこうだ:

「○○だろ!」

相当な自信がうかがえる。間違っていたとしても、この勢いがあればユーザはクリックしてしまうのではないか。また私は別のことにも気がついたのだった。これはある意味、ユーザのボケ(キーワードの打ち損じ)に対するSolrのツッコミといってもよいだろう。私はこれを「ツッコミ検索」と呼ぶことにしたい。はずれても笑いがとれればそれでいい。日本のお笑いの精神がそこには垣間見えるのだった。

しかし、まじめな話、デモデータ(歴代の内閣総理大臣の所信表明演説のテキストデータを利用)を使った次の実例を見れば、相当イイセンいっていると、読者は納得してくれるに違いない:

ボケツッコミ
笑止高齢化「少子高齢化」だろ!
いんたーねっt「インターネット」だろ!
しゃ皆保険「社会保険」だろ!
滑稽銀「国会議員」だろ!
partner湿布「パートナーシップ」だろ!
滅入るマガジン リーマン「メールマガジン サラリーマン」だろ!


日本の検索にもっと笑いを。ガンバレ日本!なんかわからないがオリンピックイヤーなので言ってみた。7期目に突入したロンウイットを、相変わらずこんな感じではありますが、今後ともどうぞよろしくお願い申し上げます。



元ヤフー社員も大満足のロンウイットのSolrトレーニング・・・受講者インタビュー記事
Solr 3.6 6月 トレーニング受講者募集中

| 関口宏司 | Solr | 02:12 | comments(0) | trackbacks(0) |
FSTとGraphViz
LuceneのFSTというクラスを見ていたら、同じパッケージのUtilクラスにtoDot()というメソッドがあったので、FSTオブジェクトをGraphVizで表示してみた。そのプログラムと手順を紹介しよう。

FSTはFinite State Transducer(有限状態変換器)の略でそのままの名称のクラスがLuceneで提供されている。FSAと異なり受理した言語(バイトシーケンス)を任意の出力にマップする。Lucene/Solrでは単語辞書をさまざまなメタ情報にマップしたり、類義語辞書を持ったりするので、FSTがいろいろなところで活用できる。FSTを構築するのにすぐれたアルゴリズムがあり、こちらの論文で紹介されている方法を使ってFSTクラスが実装されている。この方法では入力単語はあらかじめソートされている必要があるが、Luceneの単語辞書はUnicode順でソートされており、また類義語辞書などソートされていないものについてはソートした上でadd()すればよい。

FSTを使った簡単なプログラムを次に示す。

public final class TestSimple {

    static final String[] DATA = {"station", "commotion", "elation", "elastic", "plastic", "stop", "ftop", "ftation", "stat"};

    public static void main(String[] args) throws IOException {
	List<String> list = new ArrayList<String>();
	for(String d : DATA){
            list.add(d);
	}
        Collections.sort(list);

	NoOutputs outputs = NoOutputs.getSingleton();
	Object nothing = outputs.getNoOutput();
	Builder<Object> b = new Builder<Object>(FST.INPUT_TYPE.BYTE1, outputs);

	final BytesRef term = new BytesRef();
	final IntsRef scratchIntsRef = new IntsRef();
	for (String d : list) {
            term.copyChars(d);
            b.add(Util.toIntsRef(term, scratchIntsRef), nothing);
	}

        FST<Object> fst = b.finish();

	Writer w = new PrintWriter("out.dot");
	Util.toDot(fst, w, true, true);
	w.close();
    }

}


このプログラムを実行すると、out.dotというファイルができるので、GraphVizのdotコマンドを次のように実行する。

$ dot -Tpng -o out.png out.dot


すると、out.pngという次のような画像ファイルが出力される。

FST output

このアルゴリズムではプレフィックスだけでなくサフィックスも文字列が共有されており、非常にメモリ効率が高いことがわかる。



あの米Clouderaディレクターも参加したロンウイットのSolrトレーニング・・・受講者インタビュー記事
決算期の今は実は受講の大チャンス!あなたの部署の余った予算を有効活用しましょう。        Solr 3.5 4月 トレーニング受講者募集中

| 関口宏司 | Luceneツール | 00:04 | comments(0) | trackbacks(0) |
ConcurrentMergeSchedulerのデッドロック問題とJava Puzzle
LuceneのConcurrentMergeSchedulerのデッドロック問題(あくまでも可能性であり、アプリケーションでへんなことをしていなければ大丈夫)が、JIRAにチケット管理されないまま修正されたので、メモしておく。

メールスレッド
修正(4.0)
修正(3.6)

ところで、このメールスレッドの中で紹介されていた「Java Puzzle」のサイトが手軽にできてよかったので簡単に紹介したい。

Wouter Coekaerts - Java Puzzle 1: Clowns

私は問題文(制限事項)をよく読まずに1秒でできたと思ったらちょっと違っていた。ヒントはVolkswagenのadd()メソッドがsynchronizedになっているところに注目する・・・という感じだろうか。

Java Puzzleは現在3つまで公開されている。



あの米Clouderaディレクターも参加したロンウイットのSolrトレーニング・・・受講者インタビュー記事
決算期の今は実は受講の大チャンス!あなたの部署の余った予算を有効活用しましょう。予算が余っていないか、上司に聞いてみましょう。        Solr 3.5 4月 トレーニング受講者募集中

| 関口宏司 | 不具合関連 | 11:23 | comments(0) | trackbacks(0) |
soleami - Apache Solr のクエリログ可視化サービス
soleami (ソレミ)というSolrのクエリログを可視化するサービスを開始した。

soleami.com

soleamiという名前は、近所の本屋の辞書コーナーを2時間ほどうろついてひねり出したものである。由来はフランス語の“ami du soleil”(太陽の友人)からとった造語である。Solrはいうまでもなくソーラーパネルやソーラーシステムのソーラー、つまり太陽から来ている(Solrのロゴも太陽を模している)。その友達のように常にそばに置いて使ってもらいたい、というところから命名した。ちなみに、"soleil"(ソレイユ)のところは日本でも「シルク・ドゥ・ソレイユ」という名前が有名だ。

なぜフランス語かというと、ロンウイットもフランス語だからだ。ロンウイットはうちの近所の丸八通りから、「丸」も「八」も縁起がいいのでとってきたものであり、「ロン(丸)」「ウイット(八)」である。じゃあなぜロンウイットはフランス語なのか、というのは聞かないで欲しい。こちらも本屋の辞書コーナーを3時間くらいうろついて命名したと思う。あれは今から6年前のことであった。3時間うろつく前には3週間くらい悩んだ記憶がある。苦しんだ様子は以下のスライドに詳しい。

プレ・ロンウイット・ネーミング・ストーリー



さてsoleamiであるが、これはSolrのクエリログを可視化するサービスである。TomcatにSolrをデプロイして運用しているサイトがほとんどだと思うが、Tomcatが出力するcatalina.outファイルをsoleamiにアップロードするだけで検索キーワードのトレンド(季節変動など)やいわゆる「0件ヒット」の発生をビジュアルにみることができる。

世の中Hadoopを使ってアクセスログ(クリックログ)を解析するのがはやっているが、一方でクエリログは置き去りにされていないだろうか。クエリログは検索システムのユーザ(サイト訪問者)のニーズのリストともいえ、サイト管理者・運営会社の立場からみれば、ぜひ分析してサイトの改善に役立てるべきデータである。

しかし大手の会社でもなければ、なかなかクエリログを分析するまで手が回らないのが実情だ。たとえばcatalina.outをExcelやawk/sedなどで処理しようとしても結構大変である。結局、catalina.outはディスク容量を圧迫するので、保存期間を過ぎると捨てられてしまう。これは非常にもったいない話ではないだろうか。

また大手の会社でもアクセスログの解析で手一杯で、クエリログまではまだまだ、というところも多い。またクエリログとなるとどこから手をつけていいかわからない、という意見もある。そういうときは、soleamiに過去(12ヶ月前までさかのぼって可視化できる)から現在のクエリログをアップロードし、おおよその見当をつけるといいだろう。 季節変動を示す検索キーワードを見つけたり、何度も「0件ヒット」を起こしているクエリを特定し、サイトの改善に役立てることができるし、自社でHadoopなどでさらに深掘りして分析する際の方向性のあたりをつけることができる。

Tomcat上でSolrをすでに運用しているサイトはcatalina.outファイルがTomcatのlogsディレクトリにできているだろう。これをsoleamiにアップロードするだけでチャートが表示できる。

soleami-chart-TOP10

soleami-list-TREND1000

TomcatもSolrでさえもまだ・・・という方は、以下の記事でTomcatとSolrをダウンロードする最初のところから解説しているので、読んでクエリログを大いに活用していただきたい。

soleami (ソレミ)の使い方〜Solrの立ち上げからログの可視化まで〜
http://www.rondhuit.com/soleami-howto.html



あの米Clouderaディレクターも参加したロンウイットのSolrトレーニング・・・受講者インタビュー記事
Solr 3.5 3月 トレーニング受講者募集中

| 関口宏司 | Solr | 01:22 | comments(0) | trackbacks(0) |
Lucene/Solr のサイトが生まれ変わりました
ASF CMSを使って生まれ変わったLucene/SolrのWebサイト:

http://lucene.apache.org/

元ヤフー社員も大満足のロンウイットのSolrトレーニング・・・受講者インタビュー記事
Solr 3.5 3月 トレーニング受講者募集中

| 関口宏司 | Lucene/Solrコミュニティ | 09:43 | comments(0) | trackbacks(0) |
(メモ)コーパス
Luceneのメーリングリストに流れていたので、メモ。

http://www.findbestopensource.com/article-detail/free-large-data-corpus
| 関口宏司 | その他(分類不能) | 20:39 | comments(0) | trackbacks(0) |
KeywordAttribute について
近くの人に質問されたのでそれをネタに簡単に解説。

KeywordAttributeはトークンが「キーワードである」ということを下流のフィルターチェインに伝達するために使われる。

上流のTokenFilterなりTokenizerが任意のトークンのKeywordAttributeのsetKeyword(true)を呼んで「キーワードである」ことの印をつけ、下流のTokenFilterが処理しようとするトークンが「キーワードかどうか」を判断して自分の処理をすべきかどうかをisKeyword()を呼んで決める。

大抵は、lucene-gosenなどのJapaneseKatakanaStemFilterなどのstemmerでisKeyword()が呼ばれ、キーワードでない場合にステミング処理をしよう、という具合に使われる。

たとえば英語のstemmerはある条件下で末尾の"s"を削除するが、これを今は亡きsolrjs(SolrのJavaScriptクライアントAPI)に適用すると、末尾の"s"がとれてsolrjとなってしまい、これは今も健在のSolrのJavaクライアントAPIというまったく別物に変換されてしまう。これではまずいので、「solrjsはキーワードである」と印をつけておけば(setKeyword(true))、これは英語のstemmerで処理されなくなる。

Solrでは、schema.xmlにてKeywordMarkerFilterFactoryを使ってprotectedに「キーワード一覧」を記したファイル名を指定することができる。

なお、通常solrjsはキーワードであると印がつけられていたりするわけではないので、(Luceneが使われている)Apacheのバグチケット管理システムJIRAなどでsolrjsを検索すると、意図しないステミング処理がされてsolrjが多数ヒットしてしまう。これを避けるには、末尾に"*"をつけて"solrjs*"とする。するとLuceneのクエリパーサーの仕様により、ワイルドカード記号がつけられた文字列はAnalyzerをスキップするので、うまく"solrjs"を検索できる。

早くもSolr 3.5に対応したロンウイットのSolrトレーニング・・・Solr 3.5 1月 トレーニング受講者募集中

| 関口宏司 | Luceneクラス解説 | 10:27 | comments(0) | trackbacks(0) |
IndexSearcher.close() の削除(4.0)
Lucene 4.0 でNo-op(空実装)であったIndexSearcherクラスのclose()メソッドが削除された。

https://issues.apache.org/jira/browse/LUCENE-3640

Lucene 3.5の時点で、IndexSearcherは自らIndexReaderインスタンスを作成して検索する方法がdeprecatedとなっており、プログラマが別途作成したIndexReaderをIndexSearcherのコンストラクタに渡す方法が推奨されていた。同時にこのときの修正でtrunk(4.0)のIndexSearcherから、IndexReaderを引数に取らないコンストラクタが完全に削除された。

https://issues.apache.org/jira/browse/LUCENE-3571

今回はこれをさらに推し進めた形となった。

早くもSolr 3.5に対応したロンウイットのSolrトレーニング・・・Solr 3.5 1月 トレーニング受講者募集中

| 関口宏司 | Luceneクラス解説 | 11:04 | comments(0) | trackbacks(0) |
FieldValueFilter (3.6)
とにかくそのフィールドに値が入っていたら(インデックスされていたら)OK、というFilterクラス、FieldValueFilterがLucene 3.6/4.0に追加された:

https://issues.apache.org/jira/browse/LUCENE-3593

使い方は、FieldValueFilterのコンストラクタに調べたい対象フィールド名を渡してFilterオブジェクトを作成し、IndexSearcherのsearch()メソッドに渡せばよい。

プログラム例
public final class TestFieldValueFilter {

  static final String INDEX = "index";
  static final String F_ID = "id";
  static final String F_VALUE = "value";
  static Analyzer analyzer = new WhitespaceAnalyzer(Version.LUCENE_36);
  static Directory dir;

  public static void main(String[] args) throws Exception {
    dir = FSDirectory.open(new File(INDEX));
    makeIndex();
    searchIndex(false);
    searchIndex(true);
  }

  static void makeIndex() throws IOException {
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, analyzer);
    IndexWriter writer = new IndexWriter(dir, config);
    writer.addDocument(doc("one", "value 1"));
    writer.addDocument(doc("zero"));
    writer.addDocument(doc("two", "value 1", "value 2"));
    writer.close();
  }
  
  static Document doc(String id, String... values){
    Document doc = new Document();
    doc.add(new Field(F_ID, id, Store.YES, Index.ANALYZED));
    if(values != null){
      for(String value : values){
        doc.add(new Field(F_VALUE, value, Store.YES, Index.ANALYZED));
      }
    }
    return doc;
  }
  
  static void searchIndex(boolean negate) throws IOException {
    System.out.println("¥n=== negate=" + negate);
    IndexReader reader = IndexReader.open(dir);
    IndexSearcher searcher = new IndexSearcher(reader);
    Filter filter = new FieldValueFilter(F_VALUE, negate);
    TopDocs docs = searcher.search(new MatchAllDocsQuery(), filter, 10);
    for(ScoreDoc scoreDoc : docs.scoreDocs){
      Document doc = searcher.doc(scoreDoc.doc);
      System.out.printf("¥tid=%s¥n", doc.get(F_ID));
    }
    searcher.close();
    reader.close();
  }
}


実行結果
=== negate=false
	id=one
	id=two

=== negate=true
	id=zero


FieldValueFilterのコンストラクタにはboolean型の2つめの引数negateを取るものがあり、上記プログラムではそれを用いている。negateがfalseの場合は値がひとつでもあるものをフィルターを通過させるが、negate=trueの場合は逆の意味となる。



早くもSolr 3.5に対応したロンウイットのSolrトレーニング・・・Solr 3.5 1月 トレーニング受講者募集中

Solr トレーニングコースパンフレットダウンロードはこちら
| 関口宏司 | Luceneクラス解説 | 10:29 | comments(0) | trackbacks(0) |
SOLR-2911
Packt社より書籍 Apache Solr 3 Enterprise Search Server が出版され、著者が現在Solrのホームページに掲載されている以前同社より出版された同じ著者の1.4の本のリンクを新しくして欲しいと、チケットSOLR-2911をオープンした。

それによると、「リンクを新しくしてくれたら、Packt社は売り上げの5%をApache Software Foundationへ寄付する」と書かれており、面白い提案だなあと感心した。
| 関口宏司 | Solr | 08:52 | comments(1) | trackbacks(0) |
+ Solrによるブログ内検索
+ PROFILE
  12345
6789101112
13141516171819
20212223242526
2728293031  
<< May 2012 >>
+ LINKS
検索エンジン製品 - 比較のポイント
商用検索エンジンを購入した企業担当者は読まないでください。ショックを受けますから・・・
>>製品比較 10のポイント
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
Apache Solr入門 ―オープンソース全文検索エンジン
Apache Solr入門 ―オープンソース全文検索エンジン (JUGEMレビュー »)
関口 宏司,三部 靖夫,武田 光平,中野 猛,大谷 純
+ RECOMMEND
Lucene in Action
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless
FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
  • 書籍「Apache Solr入門」
    ryo (02/19)
  • 書籍「Apache Solr入門」
    関口 (02/19)
  • 書籍「Apache Solr入門」
    ryo (02/18)
  • 「Railsアプリケーション開発者のためのSolr入門」 - 無料セミナーの資料公開
    tabi (02/18)
  • 「Railsアプリケーション開発者のためのSolr入門」 - 無料セミナーの資料公開
    関口 (02/12)
  • 「Railsアプリケーション開発者のためのSolr入門」 - 無料セミナーの資料公開
    tabi (02/12)
  • 書籍「Apache Solr入門」
    太郎 (02/07)
  • 書籍「Apache Solr入門」
    関口 (02/07)
  • 書籍「Apache Solr入門」
    太郎 (02/07)
  • SOLR-2911
    関口 (11/24)
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
qrcode
+ SPONSORED LINKS