Skip to content

Commit 5ab4eb7

Browse files
18 search with sphinx
* add questions index * user can search in questions * user can search in questions, users, answers, comments and everywhere * add SearchesHelper
1 parent 7fb7c59 commit 5ab4eb7

21 files changed

+291
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@
2929
config/database.yml
3030

3131
/dump.rdb
32+
33+
config/*.sphinx.conf
34+
db/sphinx

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ gem 'devise'
4343
gem 'doorkeeper', '4.2.6'
4444
gem 'gon'
4545
gem 'jquery-rails'
46+
gem 'mysql2'
4647
gem 'oj'
4748
gem 'oj_mimic_json'
4849
gem 'omniauth'
@@ -53,6 +54,7 @@ gem 'sidekiq'
5354
gem 'sinatra', require: nil
5455
gem 'skim'
5556
gem 'slim-rails'
57+
gem 'thinking-sphinx'
5658
gem 'whenever'
5759

5860
group :development, :test do
@@ -82,6 +84,7 @@ group :test do
8284
gem 'chromedriver-helper'
8385
gem 'launchy'
8486
gem 'json_spec'
87+
gem 'database_cleaner'
8588
end
8689

8790
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem

Gemfile.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ GEM
8888
concurrent-ruby (1.0.5)
8989
connection_pool (2.2.2)
9090
crass (1.0.4)
91+
database_cleaner (1.7.0)
9192
devise (4.4.3)
9293
bcrypt (~> 3.0)
9394
orm_adapter (~> 0.1)
@@ -116,10 +117,13 @@ GEM
116117
hashie (3.5.7)
117118
i18n (1.0.1)
118119
concurrent-ruby (~> 1.0)
120+
innertube (1.1.0)
119121
io-like (0.3.0)
120122
jbuilder (2.7.0)
121123
activesupport (>= 4.2.0)
122124
multi_json (>= 1.2)
125+
joiner (0.4.2)
126+
activerecord (>= 5.2.beta1)
123127
jquery-rails (4.3.3)
124128
rails-dom-testing (>= 1, < 3)
125129
railties (>= 4.2.0)
@@ -143,6 +147,7 @@ GEM
143147
marcel (0.3.2)
144148
mimemagic (~> 0.3.2)
145149
method_source (0.9.0)
150+
middleware (0.1.0)
146151
mime-types (3.1)
147152
mime-types-data (~> 3.2015)
148153
mime-types-data (3.2016.0521)
@@ -155,6 +160,7 @@ GEM
155160
multi_xml (0.6.0)
156161
multipart-post (2.0.0)
157162
mustermann (1.0.3)
163+
mysql2 (0.5.2)
158164
nio4r (2.3.0)
159165
nokogiri (1.8.2)
160166
mini_portile2 (~> 2.3.0)
@@ -228,6 +234,7 @@ GEM
228234
responders (2.4.0)
229235
actionpack (>= 4.2.0, < 5.3)
230236
railties (>= 4.2.0, < 5.3)
237+
riddle (2.3.1)
231238
rspec (3.7.0)
232239
rspec-core (~> 3.7.0)
233240
rspec-expectations (~> 3.7.0)
@@ -297,6 +304,13 @@ GEM
297304
activesupport (>= 4.0)
298305
sprockets (>= 3.0.0)
299306
temple (0.8.0)
307+
thinking-sphinx (4.0.0)
308+
activerecord (>= 3.1.0)
309+
builder (>= 2.1.2)
310+
innertube (>= 1.0.2)
311+
joiner (>= 0.2.0)
312+
middleware (>= 0.1.0)
313+
riddle (~> 2.3)
300314
thor (0.20.0)
301315
thread_safe (0.3.6)
302316
tilt (2.0.8)
@@ -335,6 +349,7 @@ DEPENDENCIES
335349
chromedriver-helper
336350
cocoon
337351
coffee-rails (~> 4.2)
352+
database_cleaner
338353
devise
339354
doorkeeper (= 4.2.6)
340355
factory_bot_rails
@@ -344,6 +359,7 @@ DEPENDENCIES
344359
json_spec
345360
launchy
346361
listen (>= 3.0.5, < 3.2)
362+
mysql2
347363
oj
348364
oj_mimic_json
349365
omniauth
@@ -364,6 +380,7 @@ DEPENDENCIES
364380
slim-rails
365381
spring
366382
spring-watcher-listen (~> 2.0.0)
383+
thinking-sphinx
367384
turbolinks (~> 5)
368385
tzinfo-data
369386
uglifier (>= 1.3.0)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class SearchesController < ApplicationController
2+
respond_to :js, only: :result
3+
4+
def result
5+
authorize! :do, :search
6+
7+
return render :show if params[:q].empty?
8+
9+
model = params[:model].constantize
10+
respond_with(@result = model.search(params[:q], { per_page: 20, order: 'created_at DESC' }))
11+
end
12+
13+
def show
14+
authorize! :do, :search
15+
end
16+
end

app/helpers/searches_helper.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module SearchesHelper
2+
def result_handler(result)
3+
type = result.class.name
4+
case type
5+
when 'User'
6+
tag.div(result.email)
7+
when 'Question'
8+
tag.div(link_to result.title, question_path(result))
9+
else
10+
tag.div(type + ': ' + result.body.truncate(20))
11+
end
12+
end
13+
end

app/indices/answers_index.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ThinkingSphinx::Index.define :answer, with: :active_record do
2+
indexes body
3+
4+
has created_at
5+
end

app/indices/comments_index.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ThinkingSphinx::Index.define :comment, with: :active_record do
2+
indexes body
3+
4+
has created_at
5+
end

app/indices/questions_index.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ThinkingSphinx::Index.define :question, with: :active_record do
2+
# fields
3+
indexes title
4+
indexes body
5+
6+
has created_at
7+
end

app/indices/users_index.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ThinkingSphinx::Index.define :user, with: :active_record do
2+
indexes email
3+
4+
has created_at
5+
end

app/models/ability.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class Ability
33

44
def initialize(user)
55
can :read, :all
6+
can :do, :search
67

78
return unless user.present?
89
can :me, User

app/views/searches/_result.html.slim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
p
2+
= 'Results: '
3+
= @result.size
4+
5+
- @result.each do |el|
6+
= result_handler el
7+

app/views/searches/result.js.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
$('.results').html('<%= j render 'result' %>');

app/views/searches/show.html.slim

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
p
2+
= form_tag '/search/result', method: :get, remote: true do
3+
p
4+
= label_tag :q, "Search for:"
5+
br
6+
= text_field_tag :q
7+
p= select_tag(:model, options_for_select([['Everywhere', ThinkingSphinx],['Question', Question],['Answer', Answer],['Comment', Comment], ['User', User]]))
8+
p= submit_tag "Search"
9+
10+
.results

app/views/shared/_header.html.slim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ nav
99
= ' '
1010
=> link_to 'Register', new_user_registration_path
1111

12-
= link_to 'Questions', root_path
12+
=> link_to 'Questions', root_path
13+
=> link_to 'Search', search_path

config/routes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131

3232
resources :attachments, only: :destroy
3333

34+
resource :search, only: :show do
35+
get :result, on: :member
36+
end
37+
3438
namespace :api do
3539
namespace :v1 do
3640
resources :profiles, only: :index do

config/thinking_sphinx.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test:
2+
mysql41: 9307

spec/acceptance/acceptance_helper.rb

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,119 @@
88

99
config.include AcceptanceHelpers, type: :feature
1010
config.include OmniauthMacros
11+
config.include SphinxHelpers, type: :feature
1112

12-
config.after(:all) do
13-
if Rails.env.test?
14-
FileUtils.rm_rf(Dir["#{Rails.root}/public/uploads/test/attachment/"])
13+
14+
config.use_transactional_fixtures = false
15+
16+
config.before(:suite) do
17+
if config.use_transactional_fixtures?
18+
raise(<<-MSG)
19+
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
20+
(or set it to false) to prevent uncommitted transactions being used in
21+
JavaScript-dependent specs.
22+
During testing, the app-under-test that the browser driver connects to
23+
uses a different database connection to the database connection used by
24+
the spec. The app's database connection would not be able to access
25+
uncommitted transaction data setup over the spec's database connection.
26+
MSG
27+
end
28+
DatabaseCleaner.clean_with(:truncation)
29+
end
30+
31+
config.before(:each) do
32+
DatabaseCleaner.strategy = :transaction
33+
end
34+
35+
config.before(:each, type: :feature) do
36+
# :rack_test driver's Rack app under test shares database connection
37+
# with the specs, so continue to use transaction strategy for speed.
38+
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
39+
40+
unless driver_shares_db_connection_with_specs
41+
# Driver is probably for an external browser with an app
42+
# under test that does *not* share a database connection with the
43+
# specs, so use truncation strategy.
44+
DatabaseCleaner.strategy = :truncation
1545
end
1646
end
1747

48+
config.before(:suite) do
49+
# Ensure sphinx directories exist for the test environment
50+
ThinkingSphinx::Test.init
51+
# Configure and start Sphinx, and automatically
52+
# stop Sphinx at the end of the test suite.
53+
ThinkingSphinx::Test.start_with_autostop
54+
end
55+
56+
config.before(:each, js: true) do
57+
DatabaseCleaner.strategy = :truncation
58+
end
59+
60+
config.before(:each) do
61+
DatabaseCleaner.start
62+
end
63+
64+
config.append_after(:each) do
65+
DatabaseCleaner.clean
66+
end
67+
1868
OmniAuth.config.test_mode = true
1969
end
70+
71+
# config.include AcceptanceHelpers, type: :feature
72+
# config.include OmniauthMacros
73+
# config.include SphinxHelpers, type: :feature
74+
75+
# config.before(:suite) do
76+
# # Ensure sphinx directories exist for the test environment
77+
# ThinkingSphinx::Test.init
78+
# # Configure and start Sphinx, and automatically
79+
# # stop Sphinx at the end of the test suite.
80+
# ThinkingSphinx::Test.start_with_autostop
81+
# end
82+
83+
# # config.before(:each) do
84+
# # # Index data when running an acceptance spec.
85+
# # index if example.metadata[:js]
86+
# # end
87+
88+
# config.after(:all) do
89+
# if Rails.env.test?
90+
# FileUtils.rm_rf(Dir["#{Rails.root}/public/uploads/test/attachment/"])
91+
# end
92+
# end
93+
94+
# config.use_transactional_fixtures = false
95+
96+
# # config.before(:suite) do
97+
# # DatabaseCleaner.clean_with :truncation
98+
# # end
99+
100+
# config.before(:each) do
101+
# DatabaseCleaner.strategy = :transaction
102+
# end
103+
104+
# config.before(:each, :sphinx => true) do
105+
# # For tests tagged with Sphinx, use deletion (or truncation)
106+
# DatabaseCleaner.strategy = :deletion
107+
# end
108+
109+
# # config.before(:each, js: true) do
110+
# # DatabaseCleaner.strategy = :truncation
111+
# # end
112+
113+
# config.before(:each) do
114+
# DatabaseCleaner.start
115+
# end
116+
117+
# # config.after(:each) do
118+
# # DatabaseCleaner.clean
119+
# # end
120+
121+
# config.append_after(:each) do
122+
# DatabaseCleaner.clean
123+
# end
124+
125+
# OmniAuth.config.test_mode = true
126+
# end

spec/acceptance/search_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require_relative 'acceptance_helper'
2+
3+
feature 'Search', %q(
4+
As an user
5+
I want to be able to search resources
6+
) do
7+
given!(:user) { create :user, email: '[email protected]' }
8+
given!(:question) { create :question, title: 'search-question' }
9+
given!(:answer) { create :answer, body: 'search-answer' }
10+
11+
scenario 'user searches', js: true do
12+
ThinkingSphinx::Test.run do
13+
visit search_path
14+
fill_in 'q', with: 'search-question'
15+
select 'Question', from: 'model'
16+
click_button 'Search'
17+
18+
expect(page).to have_link question.title
19+
expect(page).to_not have_content answer.body
20+
21+
fill_in 'q', with: 'search-answer'
22+
select 'Answer', from: 'model'
23+
click_button 'Search'
24+
25+
expect(page).to_not have_link question.title
26+
expect(page).to have_content answer.body
27+
28+
fill_in 'q', with: 'search-user'
29+
select 'User', from: 'model'
30+
click_button 'Search'
31+
32+
expect(page).to_not have_link question.title
33+
expect(page).to have_content user.email
34+
35+
fill_in 'q', with: 'search'
36+
select 'Everywhere', from: 'model'
37+
click_button 'Search'
38+
39+
expect(page).to have_link question.title
40+
expect(page).to have_content user.email
41+
expect(page).to have_content answer.body
42+
end
43+
end
44+
end

0 commit comments

Comments
 (0)