Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elasticsearchでポエムを全文検索 #154

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,28 @@ gem 'gemoji'
gem 'slack-api'
gem 'browserify-rails'
gem 'react-rails'
gem 'kaminari'
# NOTE: 以下のIssueのため
# releaseはされてないのでGitHubを直接見に行く
# https://github.com/amatsuda/kaminari/issues/774
gem 'kaminari', github: 'amatsuda/kaminari'
gem 'redis', '~> 3.0'
# NOTE: 下記PRがマージされるまではエラーが出るので、個人のブランチを使用
# https://github.com/heroku/rails_stdout_logging/pull/20
gem 'rails_stdout_logging', github: 'kibitan/rails_stdout_logging', branch: 'compatible_rails5'
gem 'elasticsearch'
gem 'elasticsearch-dsl'
gem 'elasticsearch-model'
gem 'elasticsearch-rails'

group :development, :test do
gem 'annotate'
gem 'byebug'
gem 'dotenv-rails'
gem 'factory_girl_rails'
gem 'spring'
gem 'rspec'
gem 'rspec-rails', '~> 3.4.x'
gem 'rspec-collection_matchers'
gem 'rspec-rails'
gem 'elasticsearch-extensions'
gem 'pry-rails'
gem 'pry-doc'
gem 'rubocop', require: false
Expand Down
69 changes: 47 additions & 22 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ GIT
sass-rails (< 5.1)
sprockets (< 4.0)

GIT
remote: git://github.com/amatsuda/kaminari.git
revision: 5629661f84ad808411f08bcea9c5513f3bb58265
specs:
kaminari (1.0.0.alpha)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)

GIT
remote: git://github.com/kibitan/rails_stdout_logging.git
revision: 02a59481034e70e68f608b3bf1df96f75379d5a8
Expand Down Expand Up @@ -70,6 +78,7 @@ GEM
annotate (2.7.0)
activerecord (>= 3.2, < 6.0)
rake (~> 10.4)
ansi (1.5.0)
arel (7.0.0)
ast (2.2.0)
autoprefixer-rails (6.3.1)
Expand Down Expand Up @@ -119,6 +128,23 @@ GEM
dotenv-rails (2.1.0)
dotenv (= 2.1.0)
railties (>= 4.0, < 5.1)
elasticsearch (1.0.15)
elasticsearch-api (= 1.0.15)
elasticsearch-transport (= 1.0.15)
elasticsearch-api (1.0.15)
multi_json
elasticsearch-dsl (0.1.3)
elasticsearch-extensions (0.0.20)
ansi
ruby-prof
elasticsearch-model (0.1.8)
activesupport (> 3)
elasticsearch (> 0.4)
hashie
elasticsearch-rails (0.1.8)
elasticsearch-transport (1.0.15)
faraday
multi_json
erubis (2.7.0)
execjs (2.6.0)
factory_girl (4.5.0)
Expand Down Expand Up @@ -151,9 +177,6 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
less (2.6.0)
commonjs (~> 0.2.7)
libv8 (3.16.14.13)
Expand Down Expand Up @@ -246,33 +269,31 @@ GEM
redcarpet (3.3.4)
redis (3.2.2)
ref (2.0.0)
rspec (3.1.0)
rspec-core (~> 3.1.0)
rspec-expectations (~> 3.1.0)
rspec-mocks (~> 3.1.0)
rspec-collection_matchers (1.1.2)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (3.1.7)
rspec-support (~> 3.1.0)
rspec-expectations (3.1.2)
rspec-core (3.5.0.beta1)
rspec-support (= 3.5.0.beta1)
rspec-expectations (3.5.0.beta1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (= 3.5.0.beta1)
rspec-mocks (3.5.0.beta1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.1.0)
rspec-mocks (3.1.3)
rspec-support (~> 3.1.0)
rspec-rails (3.1.0)
rspec-support (= 3.5.0.beta1)
rspec-rails (3.5.0.beta1)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.1.0)
rspec-expectations (~> 3.1.0)
rspec-mocks (~> 3.1.0)
rspec-support (~> 3.1.0)
rspec-support (3.1.2)
rspec-core (= 3.5.0.beta1)
rspec-expectations (= 3.5.0.beta1)
rspec-mocks (= 3.5.0.beta1)
rspec-support (= 3.5.0.beta1)
rspec-support (3.5.0.beta1)
rubocop (0.36.0)
parser (>= 2.3.0.0, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
ruby-prof (0.15.9)
ruby-progressbar (1.7.5)
sass (3.4.21)
sass-rails (5.0.4)
Expand Down Expand Up @@ -332,14 +353,19 @@ DEPENDENCIES
byebug
compass-rails!
dotenv-rails
elasticsearch
elasticsearch-dsl
elasticsearch-extensions
elasticsearch-model
elasticsearch-rails
factory_girl_rails
font-awesome-sass
gemoji
hirb
hirb-unicode
jbuilder (~> 2.0)
jquery-rails
kaminari
kaminari!
less-rails!
mysql2 (~> 0.3.13)
omniauth
Expand All @@ -353,9 +379,8 @@ DEPENDENCIES
react-rails
redcarpet
redis (~> 3.0)
rspec
rspec-collection_matchers
rspec-rails
rspec-rails (~> 3.4.x)
rubocop
sass-rails (~> 5.0)
simple_form
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/poems_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
class PoemsController < ApplicationController
before_filter :login_required
before_action :set_poem, only: [:edit, :update, :destroy]
before_action :set_search_params, only: [:index]

def index
@poems = Poem.all.order(id: :desc).includes(:user).page(params[:page])
@poems = Poem.search(@search_params).page(params[:page]).records

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

できれば /api/poems の方も対応してほしいな・・・

end

def show
Expand Down Expand Up @@ -48,6 +49,10 @@ def set_poem
@poem = current_user.poems.find(params[:id])
end

def set_search_params
@search_params = params[:search] || {}
end

def poem_params
params.require(:poem).permit(:title, :description, :original_poem_id)
end
Expand Down
45 changes: 45 additions & 0 deletions app/models/concerns/poem/searchable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# elasticsearch用の検索モジュール
module Concerns::Poem::Searchable
extend ActiveSupport::Concern

included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本当は非同期callbackの方がいいと思うけど、面倒だしとりあえずコレで。

index_name "poem_#{Rails.env}"

settings do
mappings dynamic: 'false' do
indexes :title, analyzer: 'kuromoji'
indexes :description, analyzer: 'kuromoji'
indexes :author_name, analyzer: 'kuromoji'
indexes :created_at, type: 'date', format: 'date_time'
end
end

def self.search(params = {})
keyword = params[:keywords]

search_definition = Elasticsearch::DSL::Search.search {
query {
if keyword.present?
multi_match {
query keyword
fields %w{ title description author_name }
}
else
match_all
end
}
}

__elasticsearch__.search(search_definition)
end
end

def as_indexed_json(options = {})
attributes
.symbolize_keys
.slice(:title, :description, :created_at)
.merge(author_name: author_name)
end
end
2 changes: 2 additions & 0 deletions app/models/poem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Poem < ApplicationRecord
validates :title, presence: true, length: { maximum: 255 }
validates :description, presence: true

include Concerns::Poem::Searchable

def quote_original_poem
if original_poem
self.title = "RP: #{original_poem.title}"
Expand Down
12 changes: 8 additions & 4 deletions app/views/layouts/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
</div>
<div class="collapse navbar-collapse" id="gloabal-header">
<% if current_user.present? %>
<ul class="nav navbar-nav">
<li><%= link_to t("header.my_poem"), user_poems_path %></li>
<li><%= link_to t("header.read_poem"), read_poems_path %></li>
</ul>
<ul class="nav navbar-nav">
<li><%= link_to t("header.my_poem"), user_poems_path %></li>
<li><%= link_to t("header.read_poem"), read_poems_path %></li>
<%= simple_form_for :search, url: poems_path, method: :get, html: { class: 'navbar-form navbar-left' } do |f| %>
<%= f.input :keywords, as: :string, input_html: { value: @search_params&.fetch('keywords') }, label: false %>
<%= f.button :submit, 'ポエムを検索' ,class: "btn btn-default" %>
<% end %>
</ul>
<% end %>
<p class="navbar-text navbar-right">
<% if current_user.blank? %>
Expand Down
8 changes: 6 additions & 2 deletions app/views/poems/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<div class="page-header">
<h1>みんなのポエム一覧</h1>
<% unless @search_params %>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless + elseif + else に置き換えましょう 🐱

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

レビューありがとうございます!!!

<h1>みんなのポエム一覧</h1>
<% else %>
<h1><%= "「#{@search_params&.fetch('keywords')}」の検索結果"%> <small><%= "(#{@poems.total_count}件)" %></small></h1>
<% end %>
</div>
<p>
<%= link_to "ポエムを作成する", new_poem_path, class: "btn btn-primary btn-lg" %>
Expand All @@ -12,7 +16,7 @@
<%= link_to poem do %>
<div class="medila">
<div class="media-left">
<%= image_tag(poem.icon_url, class: "img-circle", width: 60) %>
<%= image_tag(poem.icon_url, class: "img-circle", width: 60) if poem.icon_url %>
</div>
<div class="media-body">
<div class="media-heading">
Expand Down
9 changes: 9 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dependencies:
post:
- wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-2.1.1.tar.gz
- tar -xvf elasticsearch-2.1.1.tar.gz
- elasticsearch-2.1.1/bin/plugin install analysis-kuromoji
- elasticsearch-2.1.1/bin/elasticsearch: {background: true}
test:
override:
- TEST_CLUSTER_COMMAND=/home/ubuntu/nasulog/elasticsearch-2.1.1/bin/elasticsearch bundle exec rspec spec
2 changes: 2 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker

config.elasticsearch_host = ENV['ELASTICSEARCH_URL'] || 'http://127.0.0.1:9200'
end
2 changes: 2 additions & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@
authentication: 'plain',
enable_starttls_auto: true
}

config.elasticsearch_host = ENV['ELASTICSEARCH_URL'] || 'http://127.0.0.1:9200'
end
2 changes: 2 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@

# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true

config.elasticsearch_host = ENV['ELASTICSEARCH_URL'] || 'http://127.0.0.1:9250'
end
2 changes: 2 additions & 0 deletions config/initializers/elasticsearch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Elasticsearch::Model.client =
Elasticsearch::Client.new(host: Rails.application.config.elasticsearch_host)
16 changes: 16 additions & 0 deletions spec/models/poem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,20 @@
it { is_expected.to eq @poem_previous }
end
end

describe '.search' do
let!(:nasu_poem) { create(:poem, title: '那須', description: 'がんばれよ!') }
let!(:suzuki_poem) { create(:poem, title: '鈴木さん', description: 'やったぜ!') }
let!(:iwaki_poem) { create(:poem, title: '岩木山', description: 'がんばれよ!那須さん') }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

笑ったw

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

山かwwww

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ス、スミマセン・・・直さないけど

before { sleep 1 } # elasticsearchにindexが反映されるのを少し待つ

it 'キーワードに応じた検索結果が得られること' do
aggregate_failures do
expect(Poem.search(keywords: '那須').records.to_a).to include nasu_poem
expect(Poem.search(keywords: 'がんばれ').records.to_a).to include nasu_poem, iwaki_poem
expect(Poem.search(keywords: 'やったぜ').records.to_a).to include suzuki_poem
expect(Poem.search(keywords: 'hoge').records.to_a).to eq []
end
end
end
end
11 changes: 11 additions & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'elasticsearch/extensions/test/cluster'

# Add additional requires below this line. Rails is not loaded until this point!

# Requires supporting ruby files with custom matchers and macros, etc, in
Expand Down Expand Up @@ -57,4 +59,13 @@
config.include FactoryGirl::Syntax::Methods

config.render_views

config.before(:suite) do
Elasticsearch::Extensions::Test::Cluster.
start(nodes: 1, path_logs: './log/elasticsearch_test.log')
end

config.after(:suite) do
Elasticsearch::Extensions::Test::Cluster.stop
end
end