我建立了一个能够搜索Rebuild.fm的Shownote的服务

我其实是Rebuild.fm的忠实听众。
Rebuild每一期都有丰富的新技术话题,然后几个月后这些话题开始流行,很多时候都会想起那个时候,在Rebuild听到的那个话题,然后重新回顾一下当时的那一期节目。

最近,关于Redux的话题真的很多。第114集的时候我稍微听过一点(Miyagawa先生对此也有一些”这个会流行吗?”的反应),但直到我在各个地方看到它的名字,读了Web+DBPress的文章之后,才想起来再次听了一遍第114集,这是最近的事情。

所以,我突然想要轻松地搜索与Rebuild中热门话题相关的讨论场景。

由于可以从RSS获取每个剧集和Shownote的信息,所以如果将其存入数据库,就可以进行搜索了。
为了学习,我正在使用Elasticsearch和React,但我还不能很好地使用它们。

在开发过程中,我们在本地构建了一个Rails应用程序,并在Vagrant上部署了Elasticsearch。

弹性搜索

我在Vagrant上通过itamae编写了Elasticsearch安装配置的配方。
这个配方包括了Elasticsearch本身和一些插件。
配方和VagrantFile等都与Rails应用程序分别存放在以下存储库中。

中文的选项:食谱/ Elasticsearch/ 默认.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的结合

我正在参考以下文章将elasticsearch-rails gem集成到Rails中。

使用Elasticsearch创建一个使用Rails的示例应用程序

大部分是使用上述示例代码的原样。
由于Elasticsearch是在Vagrant上构建的,所以在模型内设置了虚拟机的主机名。

开发环境配置文件: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

应用程序/控制器/显示笔记控制器

# 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+Redux实现的。
这个应用程序的动作很少,所以用Redux没有太多意义。
代码是参考Web+DBPress Vol.92特辑中的内容。

顺便说一下,第92期中除了有关Redux的文章外,还收录了使用Rails+React构建的TODO应用程序示例,非常有参考价值。

使用superagent进行向服务器端发送请求,并接收以JSON格式返回的响应。

前端/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})
        }
      })
  }

使用webpack来编译jsx文件时,我曾为如何将其集成到Rails的配置目录中而烦恼,但参考了下面的文章后,我创建了一个frontend目录,并将其中的package.json和其他一些文件放入其中。
当我要编写jsx时,我会切换到frontend目录下执行”webpack –watch”命令。

使用Webpack可以方便地将JavaScript从Rails中分离并达到良好的效果。

$ 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的組件進行構建。

不知怎地,我觉得花费最多时间的部分是这个设计。最开始的调色方式还是保持了bulma的默认配色,但我反复无数次地调整了这个不好那个不好的。

组件的布局没有改变,只是在调整背景和颜色等细节,但是始终无法达到我喜欢的感觉。
以下是修正过程中的内容。很难辨识吧。

我认为现在的东西已经变得很不错了,你觉得怎么样呢?
因为主题颜色和背景图片都完美地配合在一起,所以我们决定采用。
真希望一次就能做出这样的东西。

bannerAds