トップ 追記

アペフチ


2015年11月07日 日記移行 [長年日記] この日を編集

_ まだまだ作りかけだけど、日記の移行作業をしています。以後こちらをお願いします。


2015年10月16日 Groongaで学ぶ全文検索 2015-10-16 [長年日記] この日を編集

_ [Groonga] Groongaで学ぶ全文検索 2015-10-16

Groongaで学ぶ全文検索 2015-10-16に参加してきました。

_ 参加人数が増えなくて主催の@ktouさんが嘆いていたので、興味ある人は気軽に参加してみたらいいと思います。

_ 今回は、テーブル設計と、作成の実際の話でした。以下ほぼ箇条書きメモ。


スキーマとは -> テーブルとカラムの定義

スキーマ設計のステップは以下の通り。

1. どういう文書リストになると嬉しいかを考える

blog、記事があってコメントがあるとする。 「コメントの一覧が返って来てほしい」などと考える。

これを忘れると、

  • あれも入れたい、これも入れたい、ってなる
  • うまく検索する条件を書けなくなる

2. 何で検索したいかを考える

タイトル、著者名、本文、出版日など。 「コメントがマッチしたらそのコメントのついている記事も出てきてほしい」など

3. あとは作っていくだけ

(1)がテーブルになる
Booksテーブル、Postsテーブル、Commentsテーブル…

(2)がカラムになる
本のタイトル、ブログのタイトル、、、

複雑になるともう少しテクニックがある。
コメントから記事を探したいなら、コメント個別に分けるんじゃなくて、全部結合するとか

authorを区別したい時
EPUB Searcherは今はshort_textにしているが、区別したいなら別途IDを振ってテーブルに入れておく。著者名は付加情報。


余談 サロゲートキーの作り方

Groongaは内部でID的なのを発行している。

Groongaのテーブルの種類

  • ハッシュテーブル
  • パトリシアトライ
  • ダブルアレイトライ
  • 配列

このうち配列だけキーがない。ほかはキーがある。

配列にキーは無いがIDはある。(他のテーブルにもある)
ID=レコードを識別する番号

配列にレコードを追加すると、_idが確実に一意なIDになる。 しかし参照する方法がない。

他のテーブルで連番でキーを作りたければ、配列に適当なレコードを追加して、その_idを使うようにすればよい(APIを使わないと、追加時に_idが分からないので注意)。PostgreSQLがserial型で連番を作って管理しているようなのと同じ雰囲気のこと。


広告を検索

何が出てくるか: -> 広告がでてくる

広告とは:

  • ID
  • タイトル
  • 説明
  • 画像

がある

1. 返って来てほしいもの

  • IDのリスト広告のリスト(ユーザー目線で考えておく)

2. 何で検索したいか

  • 名前
  • 説明

3. 設計する

別途、「出してもいいIDテーブル」がある

カラム(検索したいもの):
上の(2)から取ってくる。

  • 名前
  • 説明

(他の情報は別テーブルから取ってくるとかあるので、別のレイヤー)

「出してもいいID」の物のみをGroongaに入れるほうが、条件判定がなくなるので、速くなる(Groongaには「広告テーブル」でなく「出してもいい広告テーブル」を作る)。
後から別の「出してもいいIDテーブル」を参照すると、参照して判定した結果、レコードが減るので、先頭10件取ってくるみたいなのがやりにくくなる。 全部出していいのなら、それがいらないので速い。

テーブルの種類を決める時のヒント:
「広告のデータが減ることはありますか?」
ある -> ハッシュ(探して消す、という操作が後でうまれるから)
無い -> 配列


以下、実際にコマンドを打ちながら作る時の話。

コマンドの名前の付け方に一定のルールがある。最初の所が操作対象、その後に操作が続く。

table_create
-> tableが操作対象、createが操作

table_removeなどもそういう構造。

table_create NAMEtable_create –name NAMEの両方の書き方がある。

table_createはデフォルト(テーブル名だけ指定して、他のオプションを明示的に指定しない)では使い物にならない。key_table=nullがデフォルトなので何も受け付けられない。

$ groonga -n ./advs.db < groonga-command.txt
でテーブルを作って
$ groonga ./advs.db dump
で内容を確認できる

実際に流しこんだコマンドのファイルはこれ:

table_create Advs --flags TABLE_HASH_KEY --key_type UInt32
column_create --table Advs --name desc --flags COLUMN_SCALAR --type Text

columns_createのflagsはだいたいスカラーかベクターを選んでおけばよい。 単一の値はスカラー、複数の値(タグなど)はベクター。

豆知識:_idカラムは必ずUInt32になる。どのテーブルでも。


2015年09月29日 Groonga新リリース自慢会 5.0.8 [長年日記] この日を編集

_ [Groonga] Groonga新リリース自慢会 5.0.8

銀座線で神田に向かっているはずが何故か新橋で下車し(改札も抜けた)、何とか神田駅に着いても全く反対の方向に歩き始めたり、会場に着く前から試練が盛りだくさんだったけど何とか永和システムマネジメントさんに辿り着いて、Groongaリリース自慢会に参加してきました。
https://groonga.doorkeeper.jp/events/31904

_ 僕が着いた頃に丁度、Nginxのバージョンを上げてHTTP/2対応をした、という自慢が行われていました。

_ その後Mroonga、PGroongaのリリースノートを見ながら自慢が続きます。

_ PGroongaはPostgreSQLのJSON型の検索に対応して、値が文字列の時は全文検索できたり、数値の場合は「>=」とかの比較演算子を使いながら検索できるらしい。

_ その他、内部の構造を交えて紹介してくれるので、リリースノート上はぱっとしないようなものでも面白かった。

_ なお、RubyバインディングであるRroongaは、先にシステムにlibgroongaをインストールした後に「gem install rroonga」した場合は、Groonga(libgroonga)のバージョンを上げれば、最新の機能が使えるとのこと


2015年09月24日 ブログの似た記事リスト作ってみた [長年日記] この日を編集

_ [Ruby][Groonga] ブログの似た記事リスト作ってみた

せっかくの夏休みなのに風邪ひいてしまって暇してたので、Groongaの類似文書検索機能を使って、ブログの似た記事リストを作ってみました。

_ と言ってもこの日記ではなくて、職場のブログでやってみました。

SINAPlog similar contents

_ まあまあの結果なんではないかと思います。

_ 最初はActiveGroongaでやってみようと思ってたんだけど、Railsを使わない場合のテーブルの作り方から分からなくて……結局より低レベルのRubyバインディングであるRroongaでやりました。(追記。ActiveGroongaの使い方分かってきたので、これでやるようにしました。Gist参照。)

Octopress/JekyllでGroongaによる類似文書検索機能を使うという記事がすごい参考になって、特にsimilar_searchっていうAPIがRroongaのリファレンスマニュアルに載ってないのにどこから見付けてきたんだって感じで、とても助かりました。

_ スクリプトはGistに置いたります:

https://gist.github.com/KitaitiMakoto/f493229009d168b87969

2015年09月21日 夏休み。BiB/i'd引っ越し、EPUB Searcher著者での絞り込み [長年日記] この日を編集

_ 夏休みなんで、これまで後回しにしていたこと色々やってる。

_ [AWS][GCE] BiB/i'd引っ越し

BiB/i'dっていう、EPUBをアップロードしたらHTMLタグを吐いて、それをブログとかに貼るとSlideShareのスライドみたいに埋め込まれてその場で読める、っていうサービスやってる(と言うのは不正確で、そういうウェブアプリ作っててデモサイトとして運用している)んだけど、これをAWS EC2からGCEに引っ越した。

_ EC2を使ってたのは、環境構築の経験が殆どないし、AWS触ったこともあまりないということで勉強用に、だった。t1.microで月2200円くらい。昨日、自分用のEPUB SearcherをGCEで立てて、BiB/i'dは殆どリソース食わないし、まとめちゃっていいかなということで、節約のために一緒にした。まだ旧サーバー残っているけど、他はzncしか使ってないし、一年くらいログインしてないから連休中に消す。

_ 更新が滅多に無いのでデータとか移してDNSレコード切替えるだけで、ゼロダウンタイムwで切り替わった。

_ [EPUB] EPUB Searcher著者での絞り込み

EPUB Searcherのデモサイトを公開したらちょっとテンション上がって、ちまちま触っている。デモサイトは三冊しか本が無いんだけど、自分用のEPUB Searcherはまあまあの数の本が入っているわけで、ノイズを減らしたい。というわけで、画面上部の著者名をクリックしたら、その著者で絞り込むようにしてみた。
検索 -> 絞り込み
という順番ではうまく動くんだけど、
絞り込み -> 検索
とやると絞り込みが解除される。取り敢えず個人的には使えるし、いっかなってことでここまででコミットしたけど、まあ仕様的には微妙なので直す気持ちはあるはある。るりまサーチみたいに、絞り込みを外したりもできたい。が、他にもやることあるしな……というくらいの重要度。

2015年09月20日 GCEでDroongaをインストールする時は忘れずに/etc/hostsを編集しておく [長年日記] この日を編集

_ [GCE][Droonga] GCEでDroongaをインストールする時は忘れずに/etc/hostsを編集しておく

Droongaをインストールする時は、hostnameで引けるIPアドレスが、Droongaとしてアクセスする(内部ネットクワークなどの)IPアドレスであるようにしておく必要がある。
hostname -fで報告されるホスト名、またはhostname -iで報告されるIPアドレスが、クラスタ内の他のコンピュータからアクセス可能なものであることを確認して下さい。
Droongaチュートリアル: 使ってみる/Groongaからの移行手順

_ で、GCEでは、インスタンスを作成する時にインスタンス名を付けることになっていて、それがそのままhostnameとして設定される。

しかも、デフォルトで設定されているDNSでは、そのインスタンス名でIPアドレスが引けるようになっている。
Each instance's metadata server also acts as a DNS resolver for that instance. DNS lookups are performed for instance names.
Using Networks and Firewalls

_ なので、本来は自分で/etc/hostsに各ノードの名前とIPアドレスを書かないといけない、特に、途中でノードを追加する時に、既存のノードも全部編集しないといけないのがめんどくさいなと思ってたのが、省けて喜んでいた。

_ しかし、実際droonga-enginedroonga-http-serverのサービスを立ち上げると、何故かhostnamedroonga0)ではなく、droonga0.c.PROJECT_NAME.internalという謎のアドレスで、サービスやSerfがバインドする。DNS問い合わせの時の何かなんだろうか、気付かず大分時間を無駄にしてしまった。

_ これは手で/etc/hosts

XXX.XXX.XXX.XXX droonga0
XXX.XXX.XXX.XXX droonga1
  :
  :
と書いてやると解消する。

_ やれやれ。


2015年09月18日 Groongaで学ぶ全文検索 2015-09-18に参加してきた [長年日記] この日を編集

_ [Groonga][検索エンジン] Groongaで学ぶ全文検索 2015-09-18に参加してきた

Groongaで学ぶ全文検索 2015-09-18に参加してきました。 前半はGroongaや全文検索エンジンの話を聞いて、後半は聞いて理解したことを自分でまとめてブログに書く、とそこまでをその場でやる、という勉強会です。

_ 検索インデックスの詳しい構造がいつまで経ってももやっとしているので(まあ勉強してないんで当たり前ですが)参加してきました。

_ そうしたら、「話を聞いて」の「話」を僕がすることになりました。

_ 開催者の須藤さんが「何かを考える時は入力と出力を考えるとよい」ということで、検索エンジンはクエリーを入力にマッチした文書を出力するものだ、という概要を話した後、僕に交代して、なんか思うところを説明するということに。

全文検索エンジンはインデックスを持っていて、インデックスはなぜ必要なのか、ということを説明しました。

_ で、その後須藤さんとマンツーマンでよく分からないところを聞いて、僕の場合はそちらをブログに書いて発表することになりましたので書きます。


まず、インデックスの説明をする時、僕は辞書になぞらえて、検索語の一文字目A-Zの配列があって、その配列に対応した、検索結果文書のリストがある、というふうにして説明しました(と書くと分かるけど、それってハッシュテーブルですね)。実際のGroongaではそこのところ、ハッシュテーブルと木構造とで選べるようになっているとのことでした。
そうして最初に検索した際に値として得られるのは文書IDのリストになっている、で、そのリストはID順にソートされているのがキモだとのこと。順番になっていることで、
  • 文書IDその物を保存するのではなく、「自分のIDにnを足したら次の文書IDになる」といった保存の仕方にすることで圧縮しやすくできる
  • 一つの検索後に対応したリストが、一つの領域に収まらない場合、「自分のいる領域には文書IDが1から12まで入っている」といった情報も併せて持つことで、例えば文書ID30に用があった場合は、その領域はそれ以上探さなくていいことが分かる(リストなので本来は前から順番に探すことになる)
といったメリットがあります。

_ ここで、リストじゃなくて配列配列じゃなくてリストになっているのは、挿入時のコストを抑えるため。Groongaでは、運用中、文書を追加した時に、その場でインデックスを更新するので、挿入コストを低くするのは大事なこと。

_ で、これ意外な感じだったのだけど、リスト内の文書IDのところには、結構他の情報を一緒に置いておいたりしているらしい(Groongaではオプションで入れたり外したりできる項目もある)。例えば、文書内での、検索語の出現位置。これも複数になりえるのでリストにして、更にソートしておく。ソートする理由はこれまで述べたのと同じ理由で使いでがあるから。

他にはは重み付けのための重み。重みは、検索用のインデックスとは別に「検索結果と照らして重みを出力するためのインデックス」を別に持つこともできるのだけど、Groongaでは一度の検索で取ってこられるようにするため、一緒に入れている。

_ 重みに関しておもしろい話をしてくれて、アジャスターというのがあるらしい。

ある検索語で検索をすると、検索結果が複数出てきて、それぞれ重みを持つ。これに対してタグでフィルタリングもしていたとして、そのタグでももう一度検索をする。そうするとタグ自体の重みも手に入る。タグの重みというのは重視できるので、その重みを使って、最初の検索結果の重みを調整してやることができる、という考え方であり、Groongaの機能ということだった(と思う、ここ理解が不安)。

_ 以上でした。

最後少し時間があったので、それぞれブログとして書いたことをディスプレイに映して見せ合いました。自分が教えたことのフィードバックが、その場でこういう形で得られるの、得難い体験ですわ。うまく伝わらなかったのか、こう言ったらもっと分かりやすかったなとか、意図通りに理解してもらって嬉しいとか、思ったりしました。

本日のツッコミ(全3件) [ツッコミを入れる]

_ kou [まとめ、ありがとうございます! > ここで、リストじゃなくて配列になっているのは ここ、「リスト」と「配..]

_ kou [ツッコミを入れるとツッコミは投稿されるんですが、レスポンスはInternerl Server Errorになるみたい..]

_ 北市真 [ありがとうございます。リストと配列のところは直しました。 ツッコミのエラーは何でしょうね……調べます]


2015年09月14日 EPUB Searcherのデモサイトを公開してみた [長年日記] この日を編集

_ [EPUB][Droonga] EPUB Searcherのデモサイトを公開してみた

EPUB Searcherっていう、EPUBファイルを対象にした検索システムがあって、僕も開発に参加している。それのデモサイトを公開してみた。 EPUB Searcherデモサイト
https://github.com/ranguba/epub-searcher/pull/2#issuecomment-59150154で言われているように明確なゴールのあるプロジェクトではないんだけど、ユースケースとしては、達人出版会のような本屋さんで、購入前に中身確認のための検索に使ったりすることが主だと思う。「この本の中から検索」ではなくて、「本屋全体の中から検索」ということができるのが便利なところかな。
個人的には、隠れた場所で、自分の本を入れて検索して使っている。

_ 本の追加は内部ネットワークからやるんで、不特定多数の一般ユーザーがやるものでは(今のところは)ない。
権利上問題ないことが分かってるEPUBで希望があれば、追加しますのでツイッターででもご連絡ください:@KitaitiMakoto

_ バックエンドに、分散検索エンジンであるDroongaを使っていて、

  • 専用の検索エンジンなので速い
  • コンテンツの追加がリアルタイムに検索結果に反映される:http://droonga.org/ja/overview/#section-6(但しEPUB Searcherは不特定多数がランダムに本を追加するわけじゃないからあまり有り難みがないかも)
  • サーバーを増やすことでスケールさせられる(同時アクセス数に対して)
  • スケールが簡単。サーバーを追加するだけで自動でデータベースが同期される
というのが特徴になると思う。 本当は
  • サーバーを増やすことでスケールさせられる(本の増加に対して
というのがあるといいんだけれど、これは開発中のようです(Droongaで理解する、分散処理の基本のキ:データの分散)。
実装が難しいというよりは、どういう方法が適切か決めかねているという、デザイン上の問題のようですね。

_ EPUB Searcherに話戻して、開発はゆるゆるとやっていて(と言うか殆どやってなくて)目を引く物がないとは思うけど、

  • 検索結果から本のサイトとかに飛べるようにする
  • 検索結果のHTMLとCSSをカスタマイズできるようにする
  • 検索結果の重み付けを調整できるようにする
あたりはやれるといいかなーとなんとなく思っています。デザインもよくできるといいけどね……スキルがね……。

2015年09月07日 NokogiriでHTML(XML)内の範囲を操作するgem作った [長年日記] この日を編集

_ [Ruby] NokogiriでHTML(XML)内の範囲を操作するgem作った

DOMにRangeっていう仕様があって。

その名の通りある範囲を示すんですが。

要素ノードとかテキストノードとかの「きれい」な切れ目じゃないところを端点に、範囲を表したりするので、扱いがあまり素直じゃない。

まあ、ページ中、マウスで選択した範囲を表す、あれですね。

_ これ。

https://developer.mozilla.org/ja/docs/Web/API/range
Range オブジェクトとは document の断片で、ある document 中のノードやテキストノードの一部を含むことのできるものです。

_ 実際使う時はこれが非常にためになる。

http://uhyohyo.net/javascript/8_1.html

_ で、それを、RubyのNokogiriを使って実装してみました。

これ。 https://github.com/KitaitiMakoto/nokogiri-xml-range

_ だから何だというわけではないですが。

まあ、日記なんでそんなもんですよね。

_ あ、まともにrefinement使ったの初めてだけど、いっすね、これ。


2015年08月10日 なぜAtomPubは根付かなかったのか [長年日記] この日を編集

_ 僕はウェブは割と新参者なので、渦中にはいなくて当時の雰囲気とか知らないのだけど、なぜAtomPubは根付かなかったんだろう。

_ XMLはめんどくさいし複雑だから。JSONにしておけばよかった。

というのはどうだろう。個人的には懐疑的だ。

_ 実装先行ではなくて仕様策定先行だったから。

ありそう。(本当に仕様策定先行だったのかは知らない。)現実に色々出ていた(のかも知らないけど)コンテンツ更新の仕組みを追認する形で仕様策定していたのなら、根付いたかもなあと思ってる。

_ コンテンツの更新・管理では、Gitが支配的になったから。

すごくありそうに感じる。でも、AtomPubを策定していた頃と、Gitがコンテンツ管理に使われ始めたのとは、時期的にかぶるんだろうか。

ちなみに、GitというよりはGitHubかな。JekyllとかGollumとかGitHub Pagesとか。「GitHub Pagesは現代のAtomPubである」と言えるような気がする。

Gitはもはやバージョン管理に留まらず、コンテンツ管理時のプロトコルになってしまっている。

_ オーサリング(コンテンツの見た目を確認しながらの執筆)ができないから。

これもありそう。

ではなぜAtomフォーマットではだめで、JSONではよかったのか。

これは、フォーマットよりは時代の要因が大きそうに思う。JavaScriptでリッチなオーサリング環境を作る、というのが、マシン性能的にもそれができる技術者の数的にも簡単にできるようになった時代、その時に目の前にあったのがJSONだったからではないかと思う。AtomPubはコマンドラインツールから使ったり、ブラウザーやウェブサイトとは別に何らかのAtomPub経由の更新システムを自作する前提での仕様だったのだろう、そういう時代だったのだろう、と思ったりする。

でもあれか、JavaScriptでオーサリング環境作るなら、XMLより圧倒的にJSONのほうが扱いが簡単だから、やっぱりファイルフォーマットの問題でもあったかな。ただここではファイルフォーマットその物のめんどうくささではなくて、JavaScriptとの親和性という観点でのフォーマットの問題。

_ 繰り返すけど、よく知らないままなんとなくで喋ってます、済みません。