Twitterのいいね（ふぁぼ）を検索したい

# はじめに

こんばんわ，<a href="https://adventar.org/calendars/2305">高知工科大学AdventCalendar2017</a>の22日目を担当させていただくTokikaze0604です．
これ書いてる現在12月22日22時前です，すいません．
（途中でブラウザバックが暴発して記事が消えてくたばりかけました，つらい）
今回は，個人的に欲しいなーと思っている，自分のいいね（ふぁぼ）を検索できる何かを作ることにしました．
何か良いアプリとかあったら教えてください．

できたものは一応<a href="https://github.com/tokikaze0604/fav-search">Github</a>に置きました．~~（2017/12/22現在：クローンしてもElasticsearchのgem入れるところで失敗するので，早めに直します． ）~~
201８/01/26追記：去年のうちに修正していたのですが，記事の更新を忘れていました．現在は正常に動作します．

# 使ったもの
- TwitterAPI
- Ruby
- Ruby on Rails (5)
- Elasticsearch

別にElasticsearchを使うのはmustではなかったのですが，全然最近（？）の技術を触っていない（最近Promelaしか触ってない）ので，何か使ってみたかったので試しに使ってみました．
それに加えて，いい感じに検索してくれるらしいので，そっちも試しに．

# やること
1. ツイッターからいいね（ふぁぼ）を全件取得する
2. Elasticsearchに入れる
3. Railsで適当にページ作って表示

大したことは~~できまs~~しません．

# 手順1:ツイッターからいいね（ふぁぼ）を全件取得する
特に面白くもないいいね（ふぁぼ）を取得するRubyのコード．
twitterのgem使ってます．

```
@client = Twitter::REST::Client.new do |config|
  config.consumer_key = ENV['CONSUMER_KEY']
  config.consumer_secret = ENV['CONSUMER_SECRET']
  config.access_token = ENV['ACCESS_TOKEN']
  config.access_token_secret = ENV['ACCESS_TOKEN_SECRET']
end

def get_favs(n = 200, max_id)
  @client.favorites(:count => n, :max_id => max_id)
end
```

`max_id`にツイートのID渡すと，そのID以下のツイートが`count`件返ってきます．
最後に取得したツイートのIDをそのまま渡すと１件重複するするので，下のように-1を入れるようにします．
（<a href="http://nonbiri-tereka.hatenablog.com/entry/2014/03/06/220015">参考</a>）

```
res = get_favs(max_id) # max_id以下のidのふぁぼ（ツイート）を取得
res.map do |status|
  count = count + 1
  max_id = status.id - 1
  append_fav(favs, status, count) # ふぁぼをハッシュに格納する
end
```

そんな感じで取得してみると...．
<img width="439" alt="スクリーンショット 2017-12-22 22.17.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/94032/7d97e06e-f931-5946-9099-49d4c56158b6.png">

もっとあるはずなのに，3154件で終わってしまいました．
<img width="645" alt="スクリーンショット 2017-12-22 22.14.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/94032/5376a797-6991-a865-988e-080dcc3bde5c.png">
最後に取得したいいね（ふぁぼ）が約２ヶ月前なので，２ヶ月が限界なのでしょうか．
ぐぐる力が足りなくて調べきれなかったのですが，もしかして課金APIなら全件取得できる...？

# 手順2:Elasticsearchに入れる
~~準備不足で時間が~~仕方ないので２ヶ月分で我慢してElasticsearchに移ります．

<a href="https://qiita.com/y-temp4/items/ffafe1c0ea8a29ce8707">こちら</a>の記事を参考にElasticserachを導入しました．

ただ，elasticsearch-headはElasticsearch5.XXからpluginで入れられなくなったらしいので，<a href="http://goodbyegangster.hatenablog.com/entry/2017/03/16/084924">別記事</a>を参考に導入しました.

詳しいの説明は省きますが，`localhost:9200`にアクセスして以下のような感じなら勝ちです．
<img width="360" alt="スクリーンショット 2017-12-22 22.47.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/94032/cc4320dd-5f51-6373-0923-d180ce1d3ba6.png">

次は取得したいいね（ふぁぼ）をElasticsearchにぶち込む過程ですが，
これはもうRailsでやってしまいます．

<a href="http://morizyun.github.io/blog/elasticsearch-rails-tutorial/index.html">こちらの記事</a>を参考に，scaffoldで適当に（ちょうど良いという意味だといいな）動くものを作ります．

ツイートのIDは１８桁の数値なので，`decimal`で取りました．
`decimal`使う時はシングルクォーテーションで囲まないとおかしなことになるらしいです．
<a href="https://qiita.com/noriyotcp/items/6284ae00a6362e8b218b">参考</a>

```
$ rails new fav
$ cd fav
$ rake db:create
$ bundle exec rails g scaffold favorite 'tweet_id:decimal{18,0}' text:text user_name:string created_at:date
$ bundle exec rake db:migrate
```

ここまででとりあえずRailsの方ができたので次はElasticsearchのgemを入れて，Elasticsearch用の設定とかを書いていきます．
詳しい部分は参考記事に任せることにして（ほとんどArticleをFavoriteに変えるだけ），
大きく異なる部分のみをご紹介します．

まずは，マッピング情報の設定です．
参考記事では以下のように文字列のマッピングに`string`を設定していますが，

```
# app/models/concerns/article_searcable.rb
...
# マッピング情報
    settings do
      mappings dynamic: 'false' do # 動的にマッピングを生成しない
        indexes :title, analyzer: 'kuromoji', type: 'string'
        indexes :body,  analyzer: 'kuromoji', type: 'string'
      end
    end
...
```

Elasticsearch6.XXからは`text`と設定するようになったようです．
（<a href="https://stackoverflow.com/questions/47452770/no-handler-for-type-string-declared-on-field-name">参考</a>）

それから，今回は必要ないのですが，何度もツイートを取得・保存することを考えて重複しないようにしました．
（<a href="http://yamakichi.hatenablog.com/entry/2016/08/25/003738">参考</a>）

```
# app/models/favorite.rb
class Favorite < ApplicationRecord
  include FavoriteSearchable
  validates :tweet_id, :uniqueness => {:scope => :text} # ここで重複を制限
end
```

```
# db/migrate/20171221141005_create_favorites.rb
class CreateFavorites < ActiveRecord::Migration[5.1]
  def change
    create_table :favorites do |t|
      t.decimal :tweet_id, precision: 18, scale: 0
      t.text :text
      t.string :user_name
      t.time :created_at

      t.timestamps
    end
    add_index :favorites, [:tweet_id, :text], unique: true # ここで重複を制限
  end
end
```

# 手順3:Railsで適当にページ作って表示
最後はViewをいじるのですが，~~とりあえず検索できれば満足なので~~ツイッターのリンクを貼るだけにとどめます．
~~手順にするほどじゃない~~

```
# app/views/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Favorites</h1>

<%= form_tag favorites_path, :method => :get do %>
    <%= text_field_tag :search, params[:search] %>
    <%= submit_tag 'Search', :name => nil %>
<% end %>
...
```

# 完成
そんな感じで~~色々と雑~~完成しました．
`rails s`でサーバを立てて，'localhost:3000/favorites'にアクセスすると以下のように表示されます．

<img width="1308" alt="スクリーンショット 2017-12-22 23.16.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/94032/1ba4aaf3-9be4-b190-ffaf-87603ad630a8.png">

検索するとこんな感じです．
<img width="1283" alt="スクリーンショット 2017-12-22 23.18.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/94032/2315829b-343f-b883-00ac-e15f4ff6dab2.png">

何も考えずに本文とユーザ名の両方にマッピングしたので，ユーザ名に検索ワードが入っている人が出てきて見辛い...．
（そもそも見た目がひどい）

# おわりに
そんなこんなで色々と期待通りの結果は得られませんでしたが，久しぶりにコードを見たり書いたりしたので楽しかったです．
全然Elasticsearchを活かせていないのは確実なので，もうちょっとブラッシュアップしていきたいと思います．
（とりあえずGithubからクローンしても動くようにします...）

稚拙な技術と文章ですが，ここまで読んでくださりありがとうございました．
（次の機会があればもっと余裕を持って投稿します，ごめんなさい）