アペフチ

渋谷.rb[:20160316]

渋谷.rb[:20160316]@ピクスタ株式会社に行って来た。道に迷って三十分遅刻して、着いたらちょうど自己紹介が終わるところだった。ある意味ちょうどいい。

今日は特に誰かが発表することはなく、各々話をしたりもくもくしたりしていた。僕もRailsまだかなあとかGoとかフレームワークとかの話に混ぜてもらいつつ、Action Cableを試すためにrails newしていた。

READMEに従ってコーディングしていたけどいつまで経ってもプロセスを起動する所に辿り着かなくておかしいなあと思って、「今(Rails 5 Beta 3)のAction Cableの入門に一番いいのって何なんですかね?」と聞いてみたらAction Cable and WebSockets: An in-Depth Tutorialという記事を教えてもらった。あと実は、READMEの下の方にプロセスの起動について書いてあったということも教えてもらった。

最後に@tyabeさんが渋谷Ruby会議02を考えてます、って案内していた。ウェブアプリケーションフレームワークをテーマにする予定だとか。

SQLアンチパターン@Sendagaya.rb #141

Sendagaya.rb #141株式会社ランチェスターに行って来た。今回は『SQLアンチパターン』に関連して相談したいことがあるという参加者がいたので、その話を前半(というか殆ど)やって、後半は『メタプログラミングRuby』を読んだ。

SQLアンチパターン

著者 : Bill Karwin
オライリージャパン
発売日 : 2013-01-26

僕はこの本を持っていないから具体的なことはあまり分からないけれど、「ポリモーフィック関連はよくない」と言っている箇所があって、ちょうど自分で作っている物でもポリモーフィック関連を使っている所があって、それで相談に来たということらしかった。本の該当箇所を説明してもらったり、@tkawaさんが電子版を持っていたのでプロジェクターで映して見せてもらったりしながら話を聞いていると、基本的には外部キー制約が使えなくなることが問題のようで、これの解決方法として交差テーブル(だっけ?)という考え方と単一テーブル継承という考え方を紹介していた。

ポリモーフィック関連はよくない、しかしではどうしたらいいだろうか、この本で提示されている解決策もあまりいいようには思われない、ということで、その人の扱っている具体的なテーブル構造なんかを教えてもらいながらみんなでああだこうだと言っていた。一般的な正解がないタイプの問題だし、ウェブのサービスだと将来要件が変わることは容易に想像できる、それも今の段階ではどう変わるか想像できないタイプの変わり方をするものだから、正解を探すのはますます難しい。@iR3さんが、長年の経験からためになるアドバイスをするなどしていた。fukajunさんもいいこと言ったり、もっと言いたいことありそうだった(前にもテーブル設計の時に言いたいこと残してそうだったのを思い出すに、RDBMS得意そう)のだけど、残念ながら体調不良でidobataによるテキストチャットでの参加だったものだから、勿体無い感じなってしまった。勿体無い。

こういうのって、誰かに相談できる、話し相手がいることその物が価値だったりするものだなあという感覚を思い出した。

メタプログラミングRuby

最後三十分くらいで『メタプログラミングRuby 第2版』を読んだ。今回は主にmethod_missingの所だ。「method_missingを定義する時は一緒にrespond_to_missing?も定義しましょう」というのがポイント。

あと、ついエイリアスメソッドチェインという言葉を使ってしまって「それ何?」って聞かれたのだけど、2016年なんだし、ほぼ説明を要するような言葉は封印していくべきだと感じた。

次回はThe Rails Doctrine(多分、日本語訳のほう)を読みながらおしゃべりする予定:
https://sendagayarb.doorkeeper.jp/events/41208

Groongaの類似文書検索

Groongaで学ぶ全文検索 2016-03-11に行って来た。

今日は、実際のユースケースに沿った話をしようということで、参加者のすごい具体的な業務の話をして、それにGroongaを入れる時にどういうふうにすればいいか、どこに注意すればいいかといった話をした。書けなさすぎるので、Mroongaを使ってウェブ日記に類似記事機能を入れるという喩え話にして書く。

この日記は、実際には静的HTMLをGitHub Pagesでホストしているが、仮に、MySQLに記事データを入れてそこからデータを引っ張って表示しているとしよう。その記事テーブルにはこんなカラムがあるだろう。

  • URL(パス)
  • タイトル
  • タグ
  • 本文

各記事の末尾に、その記事に関連する記事一覧を出したいとする。

関連記事の分類

読者に読むべき記事を提示する際、状況を二つに分けられる。

  • 読者が、自分が読みたい物を自覚している(「Groongaの記事を読みたい」)
  • 読者は自分が読みたいタイプの記事を自覚していない(「この人の文体は気持ちがいい。次に何を読もう」

前者の読者に対しては、テキストフィールドを使ったいわゆる検索機能を提供すればよい。

後者の読者には、記事の提供側(個人日記なので、僕のことだ)が何らかの基準で読むといいだろう記事を提示する。提示のアプローチは更にこう分類できる。

  • 全ユーザーに共通の記事を提示する
    • 僕がおすすめしたい記事一覧を出す
    • 日記の全記事中、人気のある記事一覧を出す(例えばPVを使って)
    • 今読んでいる記事に似た記事一覧を出す
  • 読者ごとにパーソナライズしたおすすめなどを提示する(例えばクッキーを使って。今日はこの話はしない)

類似文書検索を行う方法

このうち「今読んでいる記事に似た記事一覧を出す」というのは類似文書検索と呼ばれる一般的な検索方法を使うと可能で、Groonga(Mroonga)にはその機能が備わっているので、ここではそれを使う時の方法と注意点を述べる。

まず、検索エンジンの用意だが、Mroongaを使えば簡単だ。MroongaはMySQLに組み込んで使えるストレージエンジンで、Groongaを使った検索機能などを提供する。Mroongaで日記テーブルを作って記事の表示や検索をさせることができるが、記事追加といったデータを扱うマスターデータベースはInnoDBにし、MySQLのレプリケーション機能を使って作るレプリカをMroongaにする、ということができる。するとInnoDBの堅牢さやトランザクション機能にあやかりながらGroongaの高速検索機能を使える。データはレプリケーション機能を使って勝手に入ってくるので、自分で同期の仕組みを作る必要もない。

こうしておくと、記事を表示する時にレプリカであるMroongaから類似文書検索機能を使って似た記事一覧を出すことができる。Mroongaで類似文書検索をするには、SQLで

... MATCH(本文) AGAINST(今見ている記事の本文 IN NATURAL LANGUAGE MODE) ...

と書く。AGAINSTに記事の本文を与えることと、モードをNATURAL LANGUAGE MODEにすることがポイントだ(ここで「NATURAL LANGUAGE MODEにすると類似文書検索になる」というのはMroongaがそうしてあるということで、MySQL一般のことではない)。

それから、トークナイザーに、MeCabを使うことも重要だ。N-gramだと精度の著しく低い結果になってしまう。どうしてか。それを説明するには類似文書検索の概要を説明する必要がある。(時間があったら書く -> なかった)

Mroongaでの類似文書検索の注意点

今見ている記事の本文を使って類似文書検索をし、似ている記事の上位n件を表示すると、そこに今見ている記事その物が入ることが非常に多い(当たり前だ)。なので、条件句でそれを除くようにしておく。

「似ている」ということの精度を高めるために、本文以外の情報を使うとよい。例えば、タグが共通している記事はスコアを上げるのみを提示するなど(Groongaではできるのだが、Mroongaではスコアをどうこうというのはできないらしい)。

その他の記事提示方法

始めの分類の「読者が、自分が読みたい物を自覚している」という状況のため、検索エリアを設けたとする。検索語を入れている途中で、この日記の中で見つかりそうな単語を補完して提示すると、見栄えがいいし、検索しても無駄そうなキーワードも早めに分かってよい。

Mroongaの最新リリースで、これ(サジェスト補完機能)を実現する機能が入った。ただ、MroongaはなるべくMySQLのユーザーが自然に使えることを重視している(上のNATRUAL LANGUAGE MODEなど、既存のMySQLの物をうまくGroongaに当てはめて使っている)が、この機能はそうはいかないので、強引にGroongaのフル機能を使えるようにする。Groongaの機能を自由に使うには

... MATCH(...) AGAINST('*SS title:@"..." && point >= 100' IN BOOLEAN MODE) ...

と、AGAINSTの中を*SSで始める。これを使ってGroongaのprefix_rk_search(prefix romaji kana search)関数を使うと、上のサジェスト補完機能を実現できる。prefix_rk_searchはGroongaには以前のバージョンからあったので、「Mroongaの最新のリリースでサジェスト補完機能を実現する機能が入った」のではなく、「Groongaのフル機能を使えるようにするプラグマ(*SSのこと)が入った」というのが正しい。

このprefix_rk_search()は、ローマ字やかなを使って、漢字が入っているカラムに対して前方一致検索ができる、という機能だ(だいぶ雑な説明。詳細はドキュメントを読まれたい:http://groonga.org/ja/docs/reference/operations/prefix_rk_search.html)。

ただ、これは通常の検索と違い、検索キーワード入力が終わる前に何度もリクエストを投げるのでそこには注意が必要だ。検索専用サーバーを立てるなどの対応が必要になるかも知れない。

ユーザーの入力を楽にするまた一つの方法として、やはり最新のリリースで、曖昧検索も入った。この日記では「Groonga」というキーワードをよく使っているので、検索すると何件か記事がヒットする。検索時に「Groonag」と綴り間違いをしてしまうかも知れない(僕はこの間違いをよくする。本当によくする)。それでも「Groonga」で検索した時の結果を出してしまう機能が曖昧検索だ。これの詳細は開発者のブログ記事を参照されたし:http://blog.createfield.com/entry/2016/02/28/014432