Help us understand the problem. What is going on with this article?

Rebuild.fmのShownoteを検索するサービスを作りました

More than 3 years have passed since last update.

僕は何を隠そうRebuild.fmのヘヴィリスナーです。
Rebuildは毎回新しい技術ネタが豊富なのですが、話題が出てから数ヶ月してそれが流行りだしてから「これRebuildで聞いたやつだ!」となり改めて当時のエピソードを聞き直してみたい、となることが多々あります。

最近だとReduxについての話題がそうでした。
Episode.114で初めて話題に登った時にはなんとなく聴き流していたのですが(これに関してはMCのmiyagawa氏も「それ流行るの?」的なリアクションだったけども)、
各所で名前を見るようになり、Web+DBPressの記事を読んでからそういえばと思い出して改めてEpisode.114を聴き直したことがつい最近ありました。

というわけで、Rebuildで話題になったネタについて話しているエピソードを楽に検索したいと思いつきました。

http://rebuildsearch.jacoyutorius.com/

RSSで各エピソードやShownoteの情報が取れるので、それをDBに突っ込んでしまえば検索できるだろうと。
勉強がてらElasticsearchやReactを使っていまが、まだ1使いこなせているとは言えないですね。。

開発時はローカルでRailsアプリを構築し、ElasticsearchはVagrant上に構築しています。

Elasticsearch

VagrantへのElasticsearchセットアップにはitamaeでレシピを書きました。
Elasticserch本体と、幾つかのプラグインも一緒に入れています。
レシピやVagrantFileもろもろはRailsアプリケーションとは別に以下のリポジトリにまとめています。

https://github.com/jacoyutorius/itamae-centos7-nginx-ruby-serverspec

cookbook/elasticsearch/default.rb

package "java-1.8.0-openjdk.x86_64" do
  action :install
end

# https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html
template "/etc/yum.repos.d/elasticsearch.repo" do
  user "root"
  group "root"
  source "./templates/etc/yum.repos.d/elasticsearch.repo.erb"
  not_if "test -e /etc/yum.repos.d/elasticsearch.repo"
end

package "elasticsearch" do
  action :install
end

# kuromoji
# https://www.elastic.co/guide/en/elasticsearch/plugins/2.0/analysis-kuromoji.html
execute "install analysis-kuromoji" do
    user "root"
    cwd "/usr/share/elasticsearch"
    command "bin/plugin install analysis-kuromoji"
  not_if "test -e /usr/share/elasticsearch/plugins/analysis-kuromoji"
end

template "/etc/elasticsearch/elasticsearch.yml" do
  user "root"
  group "root"
  source "./templates/etc/elasticsearch.yml.erb"
end

# user,groupをelasticsearchにしないとサービス起動でエラーになる
execute "change owner to elasticsearch" do
    user "root"
    cwd "/etc/"
    command "chown -R elasticsearch elasticsearch;chgrp -R elasticsearch elasticsearch"
end

Rails + Elasticsearch

Railsにelasticsearch-rails gemを組み込むのは以下の記事を参考にしています。

Elasticsearchを使ったRailsサンプルアプリの作成

ほとんど上記のサンプルコードのままです。
ElasticsearchはVagrant上で構築したのでVMのホスト名をモデル内で設定しています。

config/environments/development.rb

Rails.application.configure do
  # elasticsearch server host
  config.elasticsearch_server_host = "192.168.33.10:9200"
end

app/models/shownote.rb

# == Schema Information
#
# Table name: shownotes
#
#  id         :integer          not null, primary key
#  episode_id :integer
#  title      :string
#  link        :string
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Shownote < ActiveRecord::Base
  belongs_to :episode, foreign_key: :episode_id, class_name: :Episode

  include Elasticsearch::Model

  self.__elasticsearch__.client = Elasticsearch::Client.new host: RebuildFulltextSearch::Application.config.elasticsearch_server_host, log: true

  INDEX_FIELDS = %w(title link).freeze
  index_name "rebuild_shownote_#{Rails.env}"

  settings do
    mappings dynamic: "false" do
      indexes :title, analyzer: "kuromoji", type: "string"
      indexes :link,  analyzer: "kuromoji", type: "string"
    end
  end

  def as_indexed_json(options = {})
    self.as_json.select{|k, _| INDEX_FIELDS.include?(k) }
  end

  def self.create_index!
    client = __elasticsearch__.client
    client.indices.delete index: self.index_name rescue nil
    client.indices.create(
      index: self.index_name,
      body: {
        settings: self.settings.to_hash,
        mappings: self.mappings.to_hash
      }
    )
  end
end

app/controllers/shownotes_controller

# GET /shownotes
# GET /shownotes.json
def index
  @shownotes = if params[:search]
    Shownote.search(query:{match: {_all: params[:search] }}).records
  else
    Shownote.includes(:episode).all
  end

  if request.format.to_s == "application/json"
    @shownotes = @shownotes.map{|r|
      {
        id: r.id,
        episode_id: r.episode_id,
        title: r.title,
        link: r.link,
        episode_no: r.episode.episode_no,
        episode_title: r.episode.title ,
        episode_subtitle: r.episode.subtitle,
        episode_link: r.episode.link,
        episode_pubdate: r.episode.pubdate
      }
    }
  end

end

React

フロント側はReact+Reduxで実装しています。
あまり動きの無いアプリケーションなのでReduxを使っている意味はあまりないですね。。
コードはWeb+DBPress Vol.92の特集に載っていたものを参考にしています。

http://gihyo.jp/magazine/wdpress/archive/2016/vol92

(余談ですが、Vol.92にはReduxの記事以外にもRails+React構成でのTODOアプリ作成サンプルなどが載っており大変参考になりました。)

サーバーサイドへのリクエストはsuperagentを使い、レスポンスをJSONフォーマットで受け取っています。

frontend/src/app.jsx

  onKyewordChange(e){
    this.setState({keyword: e.target.value});

    var url = "/shownotes.json?search=" + this.state.keyword
    request.get(url)
      .accept("application/json")
      .end((err, res) => {
        if(err || !res.ok){
          console.error(this.props.url, status, err.toString())
        }
        else{
          this.setState({data: res.body})
        }
      })
  }

jsxのコンパイルはwebpackを使っていますが、これらをRailsの構成ディレクトリにどう組み込めば良いのか、悩みましたが以下の記事を参考にfrontendディレクトリを作成してその中にpackage.jsonほか一式を入れてしまいました。
jsxを触るときはfrontendディレクトリに移動して"webpack --watch"しています。

WebPackを使ってRailsからJavaScriptを楽に良い感じに分離する

$ tree -L 3
.
├── app
│   ├── assets
│   │   ├── images
│   │   ├── javascripts
│   │   └── stylesheets
│   ├── controllers
│   │   ├── application_controller.rb
│   │   ├── concerns
│   │   ├── episodes_controller.rb
│   │   ├── main_controller.rb
│   │   └── shownotes_controller.rb
│   ├── helpers
│   ├── mailers
│   ├── models
│   │   ├── concerns
│   │   ├── episode.rb
│   │   └── shownote.rb
│   └── views
│       ├── episodes
│       ├── layouts
│       ├── main
│       └── shownotes
├── bin
├── config
├── config.ru
├── db
├── frontend
│   ├── package.json
│   ├── src
│   │   ├── app.jsx
│   │   └── index.html
│   └── webpack.config.js
├── lib
│   ├── assets
│   └── tasks
│       ├── elasticsearch.rake
│       └── rebuild.rake

画面デザイン

bulmaのコンポーネントを使って構成しています。

http://bulma.io/

なんだかんだで一番時間がかかったのがこのデザイン部分だった気がします。
配色は当初bulmaのデフォルトのままだったのが、ちまちまあれでもないこれでもないといじっていました。

コンポーネントの配置は変えておらず、背景や色などをチマチマいじっていたのですが、なかなか好みの感じになりません。。
以下は修正途中のものです。見づらいですね。

現在のものは結構いい感じになったと思いますがどうでしょうか。
テーマカラーと背景画像がバッチリ着地した感があったので決定としました。
本当はこれぐらいのものを一発で作り上げたいものです。

jacoyutorius
Software/Server developer at AIRS. Ruby / AWS / Vue
http://jacoyutorius.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした