読者です 読者をやめる 読者になる 読者になる

mosowave

sinamon129による(主に)技術ブログ。Ruby,Ruby on Rails,Elasticsearchやその他について書きます。

ブログデザインを更新した

BEFORE

f:id:sinamon129:20160101232218p:plain


AFTER
f:id:sinamon129:20160101232226p:plain

主な変更点とか

  • 全体的なカラーリングの変更
    • ラベンダーから、ライトピンクに
    • 色味とか雰囲気とかは好きなファッションブランドのサイトを参考に
    • ベースカラー:#FDE3EC
    • リンク色:#FF80BD
    • あと普通の黒(#000000)と白(#FFFFFF)
  • ベースのテーマを変更
    • EpicからInnocent
    • カスタムはBlankInnocentのテーマ説明内にあったものを参考にさせてもらいました
    • 色味をベースカラーにあわせてcssで調整(背景・リンク・ボタンとか)
  • アイコンとかグラフィック周りの変更
    • イラレでぽちぽち作った
    • 本業じゃないのでノリで
    • ひさびさにカーニングしてみたけど苦手すぎるから余計なことした感ある
  • その他
    • ドラックした時に色かわるようにしてみたり
    • profileちょっとかいてみたり
    • analytics入れてみたり
    • descriptionとか書いたり
    • サイドバーちょっと調整したり

ひさびさにやったけど楽しかった٩(๑′∀ ‵๑)۶•*¨*•.¸¸♪

今年自分がかいたアドベントカレンダー記事まとめ

この記事はDark - Developers at Real Kommunity Advent Calendar 2015の23日目のエントリです。

こんにちはこんにちは、@sinamon129です。
いつもDarkのslackで光と闇の戦士たちを見ていますが、
だいたい土曜日は夕方まで寝てることが多い引きこもりので、もくもく会やイベントの出現率は低めです。
(来年はちょこちょこ顔を出したい(願望))

最近ごちうさにはまりました。まだ1期を見ていますが、チノちゃんがかわいくてしかたないです。

好きなことを書いていいらしいので、今年かいたアドベントカレンダー記事をまとめたいと思います。

12/07

Elasticsearch Advent Calendar 2015
mosowave.hatenablog.com

今年6月ぐらいに初めてElasticsearchを触って、パフォーマンスチューニングから全文検索エンジン用の辞書作成まで、色々やりました。
今年の後半はElasticsearchと一緒に歩んだと言っても過言ではなくて、書きたくて仕方なかったw
辞書作成話は、案外知見がネットにまとまってなくて、cookpadさんとハッカドールさんの記事をとっても参考にしていました。

12/10

Fablic Advent Calendar 2015
in.fablic.co.jp

会社のアドベントカレンダー。実はこの記事、夏にかいたまま眠っていた記事を修正したものw
デザイナーの後輩に、画像を色々修正してもらい、結構もりもりな締め会コンテンツの記事。
締め会のコンテンツ作り、毎回楽しんでもらえる物を作るのは難しいけども、ここでもプログラムを書いたりして楽しかったりするw

12/13

Geek Women Advent Calendar 2015
mosowave.hatenablog.com

GeekなWomenのアドベントカレンダー。Women系ネタがなかったので、ブログネタにしました。
他の人の記事、女性エンジニアという観点がどこかに入れられて書かれた記事も多くて、結構共感度が高くて面白い。
女子部系の活動も結構活発になってきてて、女性のエンジニアが居ないってそんなことないんじゃない?ってぐらい。

12/21

Fablic Advent Calendar 2015
in.fablic.co.jp

7日の記事のもうちょっと広い範囲版。
この仕事自体は結構がっつり取り組んだので、ぜひ記事にしたかった話。
まだまだやることいっぱいあるので、ぜひ私と一緒に検索まわりを....(ry



そんな感じで全4本(これを含めると5本)書きました。
結構書いたなぁ(当人比)
この調子で来年もブログかいていきたい(真顔)

明日は@moschanです!

ブログを比較的継続して書けるようになった話

この記事はGeek Women Advent Calendar 2015の13日目のエントリです。

こんにちは、しなもん@sinamon129です。

せっかくのGeekなWomenのカレンダーということで、
おそらくそれに入るであろうIT企業に勤めるエンジニアである私がどういう生き物であるかをまず書いておきます。

webとファッションが好きな社会人2年目エンジニアです。
悲しいことに初対面の人は大体エンジニアと思ってくれませんw(そんな世界を変えたい)
ファッションフリマアプリFRILを運営しているFablicでエンジニアをしています(2015年4月~)

いわゆるサービス思考のエンジニアで、技術をつかってよいサービスを作ることを生きがいに生きています。
ここでいう[よい]は、「ユーザーにとってよい」と「中の人にとってよい」の両方。
もちろんコードを書くのも大好きです。綺麗なコードが書けた時にはガッツポーズをしますw

さて自己紹介はさておき、
実は私、ブログを書くのがとっても苦手で...
でもいつも、Google先生にお世話になってたくさんの方のブログにお世話になっています。
お世話になってるからこそ、自分が引っかかってわかったことをブログに書いて誰かのために残したい。

でもブログがかけない...のループ。

ですが今年12本のブログを書きました。
苦手解消につながったかなとおもうことを書きます〜。

リレーブログを始めた

前職の同期と一緒にリレーブログを始めました。
4人で毎週代わる代わる記事を書いているので、大体1ヶ月に1回回ってきます。
書けないと1000円の罰金制度がある(後日の肉代として貯蓄される)ので、いい緊張感(ただ肉がたべたい)をもって書けます。
(ちなみに年の前半は書けなさすぎて罰金が溜まって肉寿司を食べに行った...w)
記事を書かないといけないとおもうと、意識してネタを考えたり、メモっておいたりするのでとてもよいです。

会社でまとめ記事やポエムをたくさん書くようにした

弊社ではQiita::teamをつかっていて、職種問わずいろんな人が投稿しています。
自分が調べたけど他の人に役立ちそう、自由研究でこういうことやってみたなどを投稿すると、いいねがついたりします。
結構これが励みで、よくいいねがついている人や、読みやすい文章を書いている人の投稿を参考に、書き方をかえてみたりして、
遠慮なく投稿するようにしました。
投稿から、いろんな新しい動きに結びついたものもあって、文章を書くこと以外にも利点がたくさんありました。
4月から70投稿してました。結構かいたなー。

      • -

文章を書く習慣がつくと、何か書かないといけない時にスムーズにかけてとってもいいなと思いました。
昨日かいたエントリ
FRILの商品検索をnGramから形態素解析にした話 - mosowaveを結構たくさんの方に読んでいただけたみたいで、
この習慣を継続的にして、もっといろんなブログから受けた恩恵を返していけたらな〜と思いました。

FRILの商品検索をnGramから形態素解析にした話

Elasticsearch

この記事はElasticsearch Advent Calendar 2015の7日目のエントリです。

こんにちは、ファッションフリマアプリFRILを運営しているFablicでエンジニアをしている@sinamon129です。

FRILの商品検索はElasticsearchを使っていて、最近nGramベースだったものを形態素解析ベースに変更しました。
その経緯やどういう手順で行ったかを書こうと思います。
主にユーザー辞書とsynonym辞書の構築の話がメインです。

どうしてnGramベースから形態素解析ベースに変更することになったか

関係ないものがなるべくひっかからないようにしたい

nGramだとファーで検索したときに、ローファーやローリーズファームが引っかかり、本当に検索したかったものが出てこないという問題がありました。
(実際は出ているのだけども、埋もれてしまっている状態)

同じ意味の単語の検索結果はなるべく同じにしたい

バッグ・バックやカシミア・カシミヤなど、表記が揺れる語があり、それらの検索結果を同じにしたいけどnGramだと上手くできないという問題がありました。

どういう手順を踏んだか

最初は、単語ごとに適合率と再現率を出すために、既存の商品データから正解データを作り、その適合率と再現率から改善を行おうとしていました。

しかし、正解データを作る作業はほぼ人力で行うしかないのでコストが高く、また作業者の主観が入るという問題があったのと、nGramの結果を前提として引っかかって欲しくないものが引っかからないようにする方が効率が良いことに気づき、nGramでの検索hit数をみながら辞書の構築を行うことにしました。

analyzer設定

index用のanalyzerとsearch用のanalyzerを用意して、index用のanalyzerのみにsynonymの設定を追加しました。
またchar_filterにicu_nomalizerを追加したことで、文字の正規化をやってくれるようになったので、とても辞書追加が楽になりました。
(半角カタカナや全角英数字や大文字を辞書に登録する必要がなくなったので)

index_analyzer

  • kuromoji_tokenizer
  • search mode
  • char_filter
    • icu_normalizer
  • filter
    • cjk_width
    • pos_filter
    • kuromoji_baseform

search_analyzer

index_analzerにsynoymを追加

ユーザー辞書とsynonym辞書の構築

FRILの商品情報や検索ワードには、ファッションブランドや洋服に関する単語、モデルの名前などデフォルトのkuromoji辞書には載ってないような単語が多く、デフォルトの辞書では引っかからないものが増えてしまいます。

検索ログからの辞書追加

実際に検索されたワードのログを貯蓄しているので、まずはそこから辞書データを作っていきます。
ですが、生の検索ワードログには以下の問題点があります。

  • フリガナがない
  • 間違っているワードが入っている
    • ミスタイプ・勘違い等

そのため、そのまま辞書データにするのは良くないと判断し、mecabと[mecab-ipadic-neologd](https://github.com/neologd/mecab-ipadic-neologd)を用いて、形態素解析した結果、フリガナが存在する名詞のデータだけを辞書に登録しました。

本番データでindexを構築して漏れを見つける

mecabで分解できた範囲では足りない&不安なので、本番で動いているクラスタ形態素解析版のindexを載せて、チェックしていきます。

nGramモードのキーワードのhit数と、形態素解析モードのhit数とで、あまりにもかけ離れていているものをベースにチェックしてきます。
(この時、影響範囲が大きいものから潰したかったので、検索数の多いものから順にチェックしていきました)

hit数が減ってる場合は以下のような場合があります。

  1. そもそも引っかかってはダメだったものがたくさんひっかかっていた場合
  2. ワンピ(ワンピースの省略形)など、nGramから形態素解析になったことによってひっかかる数が減ってしまった場合
  3. 形態素解析で分解されたが、その分解され方がおかしい場合

1の場合はそのままで大丈夫ですが、その他の場合は、実際にワードを検索したり、inquisitorプラグインで単語を分解しながら、適宜ユーザー辞書やsynonym辞書に単語を追加していきました。

社内テストで抜け漏れを見つける

上位数千ワードぐらいが大体大丈夫だろうというところで、社内ユーザーが検索する時は先ほどつくったindexを参照するようにapiの実装を変更しました。
実際に使ってもらって、ひっかからなくなった単語や、この単語で検索した時とこの単語で検索した時は同じ結果になってほしいなどの要望をもらい、ひたすら辞書を更新していきました。

FRILでは、Ruby on Railsからsearchkichというgemを使っているので、商品検索indexは基本的に商品データの更新時にリアルタイム更新されているのですが、テスト用に作ったindexは手動更新だったため、社内ユーザーがほぼいつも通り使えるように15分に一回更新するようなバッチを書いて対応しました。

本番適応後

本番適応してからは、検索結果からの商品遷移率を見ながら辞書を修正してきました。
関係ないものが引っかかると検索結果から商品への遷移率が落ちる傾向にあるので、nGramの時の遷移率よりも下がっているものを探しながら修正を行いました。
またhit数が0になっているワード等を確認し、必要に応じて辞書の修正を行いました。

副産物

検索レポート

GoogleBigQueryに溜まっている検索ログを形態素解析モードの変更した時の影響等を観測したいので、毎日集計するようにしました。

f:id:sinamon129:20151206221603p:plain
検索ワードごとのdauや検索回数、検索結果からの商品ページへの遷移率を観測できる画面や、デイリーランキングをslackに通知したり等、社内で今何が検索されてるかを確認できる状態になりました。
f:id:sinamon129:20151206215623p:plain:w300

辞書登録システム

ユーザー辞書ファイル・synonym辞書ファイルはメンテナンスしていかないといけないものなので、自分以外も更新できるように辞書登録システムを作りました。カナが同じワードをだしたり、部分一致したワードを出すことで、類義語設定がとてもスピーディーにできるようになりました。

f:id:sinamon129:20151206213857p:plain

特に苦労したこと

データが綺麗じゃない

商品名や商品情報をユーザーさんは自由に書くので、顔文字や絵文字はもちろん、商品に関係ないことがたくさん書かれます(`・д´・ ;)

辞書追加作業が大変だった

ファッションブランドや流行りのファッションワードは、どこかにまとまっていることが少ないことが多く、ニコニコ大百科wikipediaからデータを作る、みたいなことがしづらくて苦労しました。。。

また、FRILユーザーでそこそこ洋服が好きな自分がみても、ぱっと見何かわからないワードも多く(知らないブランド・アーティスト等)これはどの表記が正しいのかなどを必死にググりました。

まとめ

nGramモードから形態素解析モードに変更するために辞書を必死に作った話でした。
日本語全文検索のための辞書構築の話はなかなか知見として公開されなくて、自分としてもどうやってやっていくか手探り状態だったので、参考になれば幸いです。

chartkickでHigh Chartsを使おうとした時に引っかかった話

rails リレーブログ

chartkickといういい感じにグラフを表示してくれるgemがあります。

JavaScriptのライブラリを読み込む時に、GooglechartsかHigh Chartsを利用できて、High Chartsを使おうとした時に、

If you prefer Highcharts, use:<%= javascript_include_tag "path/to/highcharts.js", "chartkick" %>

とREADMEに書いてあったので、

app/assets/javascripts以下にhighcharts.jsを置いて、

app/views/layouts/application.html.erbに

<%= javascript_include_tag "highcharts", "chartkick" %>

と書くと、app/assets/javascrips/applications.jsに書いてないものはprecompileされないから、そんなものねーといわれる

なので、
app/assets/javascrips/applications.jsに

//= require highcharts

を追加したら、

Highcharts already defined in the pageといわれる。どうやら2重で読み込まれてしまったらしい。

結局、

  • app/views/layouts/application.html.erbから

<%= javascript_include_tag "highcharts", "chartkick" %>

の表記を削除

  • app/assets/javascrips/applications.jsに

//= require highcharts
//= require chartkick

を記述

することでうまくいった。

Elasticsearchのslowlogの設定をした話

リレーブログ Elasticsearch

最近、日本語全文検索サーバーとしてElasticsearchを使っていて、
たまにクエリのキューが沢山たまってしまうことがあり、
原因になってるクエリを調べたくなりました。

slowlogがあるのは知っていたので見に行ったら空!設定されてなかったorz

今回はそんなslowlogを設定した話です。

Elasticsearchのバージョンは1.7.2です。

elasticsarch.ymlの下の方に

index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s
index.search.slowlog.threshold.query.trace: 500ms

index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug: 500ms
index.search.slowlog.threshold.fetch.trace: 200ms

こんな項目が!!コメントアウトされてる\(^o^)/
コメントをはずした。

traceとかdebugとかはどこで設定するんだろう?とおもって公式のドキュメントをみていたら、
公式documentlogging.ymlにloggingの出力形式等の設定ができるっぽいので見に行ってみると、ありました。

中略

index.search.slowlog: TRACE, index_search_slow_log_file
index.indexing.slowlog: TRACE, index_indexing_slow_log_file

additivity:
index.search.slowlog: false
index.indexing.slowlog: false


traceのままでよかったので、search.slowlogとindexing.slowlogをtrueにして無事slowlogが吐かれるようになりました。
ログの形式をきめれたりもするので、いい感じに設定するとはかどりそうです。

設定できた感動でログの形式はデフォルトのまま+ログ集積してゴニョりたかったけど、とりあえず各サーバーのログを集めてチェックしたい!!
となったので、
デフォルトの設定で吐かれたログをパースしてcsvに吐く超適当プログラムをrubyで書いた。

require 'csv'

CSV.open('slow_logs.csv', 'wb') do |csv|
  csv << ['date', 'kind', 'node_name', 'index_name', 'shard_number', 'took', 'took_millis', 'source']
    File.open('search_slowlog') do |file|
      file.each_line do |line|
        md = line.match(/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\]\[TRACE\]\[(.*)\] \[(.*)\] \[(.*)\]\[(.*)\] took\[(.*)\], took_millis\[(.*)\], types\[\], stats\[\], search_type\[QUERY_THEN_FETCH\], total_shards\[12\], source\[(.*)\], extra_source\[\]/)
        csv << [
          md[1], # date
          md[2], # kind
          md[3], # node_name
          md[4], # index_name
          md[5], # shard_number
          md[6], # took
          md[7], # took millis
          md[8]  # source
        ]
      end
    end
  end
end

これをノードの台数分さまるプログラムをかいて(上記のプログラムに入れた)、スプレッドシートに貼ってソートしてチェック。
ほんとうはfluendとかでいい感じにあつめてkibanaとかでいい感じに見たかった(今後の課題)

whenever で毎時45分とかをwhenever っぽく書く

rails

Rubyのコードでcrontabを管理できるwheneverというgemを使っていて、
毎時45分ってcronっぽい書き方じゃなくかくにはどうしたらいいか案外のってなかったのでかく。

github.com

毎時45分とかは、

every 1.hours, at: 45 do
rake 'rake:task'
end

みたいに書けば良かった。