mini-smart-box 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/elasticsearch-rails-8.0.1/CHANGELOG.md +44 -0
- data/elasticsearch-rails-8.0.1/Gemfile +38 -0
- data/elasticsearch-rails-8.0.1/LICENSE.txt +202 -0
- data/elasticsearch-rails-8.0.1/README.md +149 -0
- data/elasticsearch-rails-8.0.1/Rakefile +67 -0
- data/elasticsearch-rails-8.0.1/elasticsearch-rails.gemspec +67 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/instrumentation/controller_runtime.rb +58 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/instrumentation/log_subscriber.rb +67 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/instrumentation/publishers.rb +53 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/instrumentation/railtie.rb +44 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/instrumentation.rb +53 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/lograge.rb +57 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/tasks/import.rb +128 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails/version.rb +22 -0
- data/elasticsearch-rails-8.0.1/lib/elasticsearch/rails.rb +24 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/01-basic.rb +364 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/02-pretty.rb +348 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/03-expert.rb +358 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/04-dsl.rb +146 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/05-settings-files.rb +88 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/articles.yml.gz +0 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/articles_settings.json +1 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/index.html.dsl.erb +178 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/index.html.erb +178 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/indexer.rb +44 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/search.css +76 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/search_controller_test.dsl.rb +148 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/search_controller_test.rb +148 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/searchable.dsl.rb +234 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/searchable.rb +224 -0
- data/elasticsearch-rails-8.0.1/lib/rails/templates/seeds.rb +75 -0
- data/elasticsearch-rails-8.0.1/spec/instrumentation/log_subscriber_spec.rb +57 -0
- data/elasticsearch-rails-8.0.1/spec/instrumentation_spec.rb +103 -0
- data/elasticsearch-rails-8.0.1/spec/lograge_spec.rb +52 -0
- data/elasticsearch-rails-8.0.1/spec/spec_helper.rb +65 -0
- data/mini-smart-box.gemspec +11 -0
- metadata +76 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
|
3
|
+
# license agreements. See the NOTICE file distributed with
|
|
4
|
+
# this work for additional information regarding copyright
|
|
5
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
|
6
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
|
7
|
+
# not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
|
13
|
+
# software distributed under the License is distributed on an
|
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
# KIND, either express or implied. See the License for the
|
|
16
|
+
# specific language governing permissions and limitations
|
|
17
|
+
# under the License.
|
|
18
|
+
%>
|
|
19
|
+
<div class="col-md-12">
|
|
20
|
+
<h1 class="text-right"><%= link_to 'Search New York Times articles', root_path %></h1>
|
|
21
|
+
|
|
22
|
+
<%= form_tag search_path, method: 'get', role: 'search' do %>
|
|
23
|
+
<div class="input-group">
|
|
24
|
+
<%= text_field_tag :q, params[:q], class: 'form-control', placeholder: 'Search...' %>
|
|
25
|
+
|
|
26
|
+
<span class="input-group-btn">
|
|
27
|
+
<button type="submit" class="btn btn-default">
|
|
28
|
+
<span class="glyphicon glyphicon-search"></span>
|
|
29
|
+
</button>
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div id="form-options" class="clearfix">
|
|
34
|
+
<div class="btn-group pull-left">
|
|
35
|
+
<label class="checkbox-inline">
|
|
36
|
+
<%= check_box_tag 'comments', 'y', params[:comments] == 'y', onclick: "$(this).closest('form').submit()" %>
|
|
37
|
+
Search in comments?
|
|
38
|
+
</label>
|
|
39
|
+
<% params.slice(:a, :c, :s).each do |name, value| %>
|
|
40
|
+
<%= hidden_field_tag name, value %>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="btn-group pull-right">
|
|
45
|
+
<p style="float: left; margin: 0.1em 0 0 0"><small>Displaying <%= (params[:page] || 1).to_i.ordinalize %> page with <%= @articles.size %> articles
|
|
46
|
+
of <strong>total <%= @articles.total %></strong></small></p>
|
|
47
|
+
|
|
48
|
+
<button class="btn btn-default btn-xs dropdown-toggle" type="button" data-toggle="dropdown" style="margin-left: 0.5em">
|
|
49
|
+
<% sort = case
|
|
50
|
+
when params[:s] then params[:s]
|
|
51
|
+
when params[:q].blank? then 'published_on'
|
|
52
|
+
else 'relevancy'
|
|
53
|
+
end
|
|
54
|
+
%>
|
|
55
|
+
sorted by <%= sort.humanize.downcase %> <span class="caret"></span>
|
|
56
|
+
</button>
|
|
57
|
+
<ul class="dropdown-menu" role="menu">
|
|
58
|
+
<li><%= link_to "Sort by published on", search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(s: 'published_on')), class: 'btn-xs' %></li>
|
|
59
|
+
<li><%= link_to "Sort by relevancy", search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(s: nil)), class: 'btn-xs' unless params[:q].blank? %></li>
|
|
60
|
+
</ul>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
|
|
65
|
+
<hr>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<% if @articles.size < 1 && @articles.response.suggestions.present? %>
|
|
69
|
+
<div class="col-md-12">
|
|
70
|
+
<p class="alert alert-warning">
|
|
71
|
+
No documents have been found.
|
|
72
|
+
<% if @articles.response.suggestions.terms.present? %>
|
|
73
|
+
Maybe you mean
|
|
74
|
+
<%= @articles.response.suggestions.terms.map do |term|
|
|
75
|
+
link_to term, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge q: term)
|
|
76
|
+
end.to_sentence(last_word_connector: ' or ').html_safe %>?
|
|
77
|
+
<% end %>
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
<% end %>
|
|
81
|
+
|
|
82
|
+
<div id="facets" class="col-md-3">
|
|
83
|
+
<% unless @articles.size < 1 %>
|
|
84
|
+
|
|
85
|
+
<div class="categories panel panel-default">
|
|
86
|
+
<p class="panel-heading"><%= link_to 'All Sections →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(c: nil))%></p>
|
|
87
|
+
|
|
88
|
+
<div class="list-group">
|
|
89
|
+
<% @articles.response.response['aggregations']['categories']['categories']['buckets'].each do |c| %>
|
|
90
|
+
<%=
|
|
91
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(c: c['key'])),
|
|
92
|
+
class: "list-group-item#{' active' if params[:c] == c['key']}" do
|
|
93
|
+
c['key'].titleize.html_safe + content_tag(:small, c['doc_count'], class: 'badge').html_safe
|
|
94
|
+
end
|
|
95
|
+
%>
|
|
96
|
+
<% end %>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="authors panel panel-default">
|
|
101
|
+
<p class="panel-heading"><%= link_to 'All Authors →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(a: nil))%></p>
|
|
102
|
+
|
|
103
|
+
<div class="list-group">
|
|
104
|
+
<% @articles.response.response['aggregations']['authors']['authors']['buckets'].each do |a| %>
|
|
105
|
+
<%=
|
|
106
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(a: a['key'])),
|
|
107
|
+
class: "list-group-item#{' active' if params[:a] == a['key']}" do
|
|
108
|
+
a['key'].titleize.html_safe + content_tag(:small, a['doc_count'], class: 'badge').html_safe
|
|
109
|
+
end
|
|
110
|
+
%>
|
|
111
|
+
<% end %>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="authors panel panel-default">
|
|
116
|
+
<p class="panel-heading"><%= link_to 'Any Date →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(w: nil))%></p>
|
|
117
|
+
|
|
118
|
+
<div class="list-group">
|
|
119
|
+
<% @articles.response.response['aggregations']['published']['published']['buckets'].each do |w| %>
|
|
120
|
+
<%=
|
|
121
|
+
__start = Time.at(w['key']/1000)
|
|
122
|
+
__end = __start.end_of_week
|
|
123
|
+
__date = __start.to_date.to_s(:iso)
|
|
124
|
+
|
|
125
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(w: __date)),
|
|
126
|
+
class: "list-group-item#{' active' if params[:w] == __date}" do
|
|
127
|
+
"#{__start.to_date.to_s(:short)} — #{__end.to_date.to_s(:short)}".html_safe + \
|
|
128
|
+
content_tag(:small, w['doc_count'], class: 'badge').html_safe
|
|
129
|
+
end
|
|
130
|
+
%>
|
|
131
|
+
<% end %>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<% end %>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="col-md-9">
|
|
138
|
+
<div id="results">
|
|
139
|
+
<% @articles.each do |article| %>
|
|
140
|
+
<div class="result">
|
|
141
|
+
<h3 class="title">
|
|
142
|
+
<%= (article.try(:highlight).try(:title) ? article.highlight.title.join.html_safe : article.title) %>
|
|
143
|
+
<small class="category"> | <%= article.categories.to_sentence %></small>
|
|
144
|
+
</h3>
|
|
145
|
+
|
|
146
|
+
<p class="body">
|
|
147
|
+
<% if article.try(:highlight).try(:abstract) %>
|
|
148
|
+
<%= article.highlight.abstract.join.html_safe %>
|
|
149
|
+
<% else %>
|
|
150
|
+
<%= article.try(:highlight).try(:content) ? article.highlight.content.join('…').html_safe : article.abstract %>
|
|
151
|
+
<% end %>
|
|
152
|
+
</p>
|
|
153
|
+
|
|
154
|
+
<% if comments = article.try(:highlight) && article.highlight['comments.body'] %>
|
|
155
|
+
<p class="comments">
|
|
156
|
+
Comments: <%= comments.join('…').html_safe %>
|
|
157
|
+
</p>
|
|
158
|
+
<% end %>
|
|
159
|
+
|
|
160
|
+
<p class="text-muted">
|
|
161
|
+
<small>Authors: <%= article.authors.map(&:full_name).to_sentence %></small> |
|
|
162
|
+
<small>Published: <%= article.published_on %></small> |
|
|
163
|
+
<small>Score: <%= article._score %></small>
|
|
164
|
+
</p>
|
|
165
|
+
</div>
|
|
166
|
+
<% end %>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<ul class="pager">
|
|
170
|
+
<li class="previous"><%= link_to_previous_page @articles, 'Previous Page', params: params.slice(:q, :c, :a, :comments) %></li>
|
|
171
|
+
<li class="next"><%= link_to_next_page @articles, 'Next Page', params: params.slice(:q, :c, :a, :comments) %></li>
|
|
172
|
+
</ul>
|
|
173
|
+
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="footer <%= @articles.size < 1 ? 'col-md-12' : 'col-md-9 col-md-offset-3' %>">
|
|
177
|
+
<p><small>Content provided by <a href="http://nytimes.com"><em>The New York Times</em></a>.</small></p>
|
|
178
|
+
</div>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
|
3
|
+
# license agreements. See the NOTICE file distributed with
|
|
4
|
+
# this work for additional information regarding copyright
|
|
5
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
|
6
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
|
7
|
+
# not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
|
13
|
+
# software distributed under the License is distributed on an
|
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
# KIND, either express or implied. See the License for the
|
|
16
|
+
# specific language governing permissions and limitations
|
|
17
|
+
# under the License.
|
|
18
|
+
%>
|
|
19
|
+
<div class="col-md-12">
|
|
20
|
+
<h1 class="text-right"><%= link_to 'Search New York Times articles', root_path %></h1>
|
|
21
|
+
|
|
22
|
+
<%= form_tag search_path, method: 'get', role: 'search' do %>
|
|
23
|
+
<div class="input-group">
|
|
24
|
+
<%= text_field_tag :q, params[:q], class: 'form-control', placeholder: 'Search...' %>
|
|
25
|
+
|
|
26
|
+
<span class="input-group-btn">
|
|
27
|
+
<button type="submit" class="btn btn-default">
|
|
28
|
+
<span class="glyphicon glyphicon-search"></span>
|
|
29
|
+
</button>
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div id="form-options" class="clearfix">
|
|
34
|
+
<div class="btn-group pull-left">
|
|
35
|
+
<label class="checkbox-inline">
|
|
36
|
+
<%= check_box_tag 'comments', 'y', params[:comments] == 'y', onclick: "$(this).closest('form').submit()" %>
|
|
37
|
+
Search in comments?
|
|
38
|
+
</label>
|
|
39
|
+
<% params.slice(:a, :c, :s).each do |name, value| %>
|
|
40
|
+
<%= hidden_field_tag name, value %>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="btn-group pull-right">
|
|
45
|
+
<p style="float: left; margin: 0.1em 0 0 0"><small>Displaying <%= (params[:page] || 1).to_i.ordinalize %> page with <%= @articles.size %> articles
|
|
46
|
+
of <strong>total <%= @articles.total %></strong></small></p>
|
|
47
|
+
|
|
48
|
+
<button class="btn btn-default btn-xs dropdown-toggle" type="button" data-toggle="dropdown" style="margin-left: 0.5em">
|
|
49
|
+
<% sort = case
|
|
50
|
+
when params[:s] then params[:s]
|
|
51
|
+
when params[:q].blank? then 'published_on'
|
|
52
|
+
else 'relevancy'
|
|
53
|
+
end
|
|
54
|
+
%>
|
|
55
|
+
sorted by <%= sort.humanize.downcase %> <span class="caret"></span>
|
|
56
|
+
</button>
|
|
57
|
+
<ul class="dropdown-menu" role="menu">
|
|
58
|
+
<li><%= link_to "Sort by published on", search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(s: 'published_on')), class: 'btn-xs' %></li>
|
|
59
|
+
<li><%= link_to "Sort by relevancy", search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(s: nil)), class: 'btn-xs' unless params[:q].blank? %></li>
|
|
60
|
+
</ul>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
|
|
65
|
+
<hr>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<% if @articles.size < 1 && @articles.response.suggestions.present? %>
|
|
69
|
+
<div class="col-md-12">
|
|
70
|
+
<p class="alert alert-warning">
|
|
71
|
+
No documents have been found.
|
|
72
|
+
<% if @articles.response.suggestions['suggest_title'].present? || @articles.response.suggestions['suggest_body'].present? %>
|
|
73
|
+
Maybe you mean
|
|
74
|
+
<%= @articles.response.suggestions.map { |k,v| v.first['options'] }.flatten.map {|v| v['text']}.uniq.map do |term|
|
|
75
|
+
link_to term, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge q: term)
|
|
76
|
+
end.to_sentence(last_word_connector: ' or ').html_safe %>?
|
|
77
|
+
<% end %>
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
<% end %>
|
|
81
|
+
|
|
82
|
+
<div id="facets" class="col-md-3">
|
|
83
|
+
<% unless @articles.size < 1 %>
|
|
84
|
+
|
|
85
|
+
<div class="categories panel panel-default">
|
|
86
|
+
<p class="panel-heading"><%= link_to 'All Sections →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(c: nil))%></p>
|
|
87
|
+
|
|
88
|
+
<div class="list-group">
|
|
89
|
+
<% @articles.response.response['aggregations']['categories']['categories']['buckets'].each do |c| %>
|
|
90
|
+
<%=
|
|
91
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(c: c['key'])),
|
|
92
|
+
class: "list-group-item#{' active' if params[:c] == c['key']}" do
|
|
93
|
+
c['key'].titleize.html_safe + content_tag(:small, c['doc_count'], class: 'badge').html_safe
|
|
94
|
+
end
|
|
95
|
+
%>
|
|
96
|
+
<% end %>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="authors panel panel-default">
|
|
101
|
+
<p class="panel-heading"><%= link_to 'All Authors →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(a: nil))%></p>
|
|
102
|
+
|
|
103
|
+
<div class="list-group">
|
|
104
|
+
<% @articles.response.response['aggregations']['authors']['authors']['buckets'].each do |a| %>
|
|
105
|
+
<%=
|
|
106
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(a: a['key'])),
|
|
107
|
+
class: "list-group-item#{' active' if params[:a] == a['key']}" do
|
|
108
|
+
a['key'].titleize.html_safe + content_tag(:small, a['doc_count'], class: 'badge').html_safe
|
|
109
|
+
end
|
|
110
|
+
%>
|
|
111
|
+
<% end %>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="authors panel panel-default">
|
|
116
|
+
<p class="panel-heading"><%= link_to 'Any Date →'.html_safe, search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(w: nil))%></p>
|
|
117
|
+
|
|
118
|
+
<div class="list-group">
|
|
119
|
+
<% @articles.response.response['aggregations']['published']['published']['buckets'].each do |w| %>
|
|
120
|
+
<%=
|
|
121
|
+
__start = Time.at(w['key']/1000)
|
|
122
|
+
__end = __start.end_of_week
|
|
123
|
+
__date = __start.to_date.to_s(:iso)
|
|
124
|
+
|
|
125
|
+
link_to search_path(params.permit(:q, :a, :c, :s, :w, :comments).merge(w: __date)),
|
|
126
|
+
class: "list-group-item#{' active' if params[:w] == __date}" do
|
|
127
|
+
"#{__start.to_date.to_s(:short)} — #{__end.to_date.to_s(:short)}".html_safe + \
|
|
128
|
+
content_tag(:small, w['doc_count'], class: 'badge').html_safe
|
|
129
|
+
end
|
|
130
|
+
%>
|
|
131
|
+
<% end %>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<% end %>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="col-md-9">
|
|
138
|
+
<div id="results">
|
|
139
|
+
<% @articles.each do |article| %>
|
|
140
|
+
<div class="result">
|
|
141
|
+
<h3 class="title">
|
|
142
|
+
<%= (article.try(:highlight).try(:title) ? article.highlight.title.join.html_safe : article.title) %>
|
|
143
|
+
<small class="category"> | <%= article.categories.to_sentence %></small>
|
|
144
|
+
</h3>
|
|
145
|
+
|
|
146
|
+
<p class="body">
|
|
147
|
+
<% if article.try(:highlight).try(:abstract) %>
|
|
148
|
+
<%= article.highlight.abstract.join.html_safe %>
|
|
149
|
+
<% else %>
|
|
150
|
+
<%= article.try(:highlight).try(:content) ? article.highlight.content.join('…').html_safe : article.abstract %>
|
|
151
|
+
<% end %>
|
|
152
|
+
</p>
|
|
153
|
+
|
|
154
|
+
<% if comments = article.try(:highlight) && article.highlight['comments.body'] %>
|
|
155
|
+
<p class="comments">
|
|
156
|
+
Comments: <%= comments.join('…').html_safe %>
|
|
157
|
+
</p>
|
|
158
|
+
<% end %>
|
|
159
|
+
|
|
160
|
+
<p class="text-muted">
|
|
161
|
+
<small>Authors: <%= article.authors.map(&:full_name).to_sentence %></small> |
|
|
162
|
+
<small>Published: <%= article.published_on %></small> |
|
|
163
|
+
<small>Score: <%= article._score %></small>
|
|
164
|
+
</p>
|
|
165
|
+
</div>
|
|
166
|
+
<% end %>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<ul class="pager">
|
|
170
|
+
<li class="previous"><%= link_to_previous_page @articles, 'Previous Page', params: params.slice(:q, :c, :a, :comments) %></li>
|
|
171
|
+
<li class="next"><%= link_to_next_page @articles, 'Next Page', params: params.slice(:q, :c, :a, :comments) %></li>
|
|
172
|
+
</ul>
|
|
173
|
+
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="footer <%= @articles.size < 1 ? 'col-md-12' : 'col-md-9 col-md-offset-3' %>">
|
|
177
|
+
<p><small>Content provided by <a href="http://nytimes.com"><em>The New York Times</em></a>.</small></p>
|
|
178
|
+
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
|
3
|
+
# this work for additional information regarding copyright
|
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
|
6
|
+
# not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
# Indexer class for <http://sidekiq.org>
|
|
19
|
+
#
|
|
20
|
+
# Run me with:
|
|
21
|
+
#
|
|
22
|
+
# $ bundle exec sidekiq --queue elasticsearch --verbose
|
|
23
|
+
#
|
|
24
|
+
class Indexer
|
|
25
|
+
include Sidekiq::Worker
|
|
26
|
+
sidekiq_options queue: 'elasticsearch', retry: false, backtrace: true
|
|
27
|
+
|
|
28
|
+
Logger = Sidekiq.logger.level == Logger::DEBUG ? Sidekiq.logger : nil
|
|
29
|
+
Client = Elasticsearch::Client.new host: (ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200'), logger: Logger
|
|
30
|
+
|
|
31
|
+
def perform(operation, klass, record_id, options={})
|
|
32
|
+
logger.debug [operation, "#{klass}##{record_id} #{options.inspect}"]
|
|
33
|
+
|
|
34
|
+
case operation.to_s
|
|
35
|
+
when /index|update/
|
|
36
|
+
record = klass.constantize.find(record_id)
|
|
37
|
+
record.__elasticsearch__.client = Client
|
|
38
|
+
record.__elasticsearch__.__send__ "#{operation}_document"
|
|
39
|
+
when /delete/
|
|
40
|
+
Client.delete index: klass.constantize.index_name, id: record_id
|
|
41
|
+
else raise ArgumentError, "Unknown operation '#{operation}'"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
h1 {
|
|
2
|
+
font-size: 28px !important;
|
|
3
|
+
color: #a3a3a3 !important;
|
|
4
|
+
text-transform: uppercase;
|
|
5
|
+
letter-spacing: -2px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.label-highlight {
|
|
9
|
+
background: #f6fbfc !important;
|
|
10
|
+
box-shadow: 0px 1px 0px rgba(0,0,0,0.15);
|
|
11
|
+
padding: 0.2em 0.4em 0.2em 0.4em !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
h3 .label-highlight {
|
|
15
|
+
background: transparent !important;
|
|
16
|
+
padding: 0.1em 0.4em 0px 0.4em !important;
|
|
17
|
+
border-bottom: 1px solid #999;
|
|
18
|
+
box-shadow: 0px 2px 1px rgba(0,0,0,0.15);
|
|
19
|
+
border-radius: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.comments .label-highlight {
|
|
23
|
+
background: #fcfdf0 !important;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
small.badge {
|
|
27
|
+
font-size: 80% !important;
|
|
28
|
+
font-weight: normal !important;
|
|
29
|
+
display: inline-block;
|
|
30
|
+
float: right;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
form #form-options {
|
|
34
|
+
color: #666;
|
|
35
|
+
font-size: 95%;
|
|
36
|
+
margin-top: 1.5em;
|
|
37
|
+
padding: 0 0.25em;
|
|
38
|
+
}
|
|
39
|
+
form #form-options input {
|
|
40
|
+
margin-top: 0.25em;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#facets .panel-heading {
|
|
44
|
+
margin-bottom: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.result {
|
|
48
|
+
border-bottom: 1px solid #ccc;
|
|
49
|
+
margin: 2em 0 0 0;
|
|
50
|
+
padding: 0 0 1em 0;
|
|
51
|
+
}
|
|
52
|
+
.result:first-child {
|
|
53
|
+
margin-top: 0.25em;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.result h3.title {
|
|
57
|
+
font-size: 180% !important;
|
|
58
|
+
font-family: 'Rokkitt', sans-serif;
|
|
59
|
+
margin-top: 0;
|
|
60
|
+
}
|
|
61
|
+
.result h3.title small {
|
|
62
|
+
font-size: 75% !important;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.result .body {
|
|
66
|
+
font-family: Georgia, serif;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.result .category {
|
|
70
|
+
font-family: 'Rokkitt', sans-serif;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.result .comments {
|
|
74
|
+
color: #666666;
|
|
75
|
+
font-size: 80%;
|
|
76
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
|
3
|
+
# this work for additional information regarding copyright
|
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
|
6
|
+
# not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
require 'test_helper'
|
|
19
|
+
require 'sidekiq/api'
|
|
20
|
+
|
|
21
|
+
class SearchControllerTest < ActionController::TestCase
|
|
22
|
+
setup do
|
|
23
|
+
travel_to Time.new(2015, 03, 16, 10, 00, 00, 0)
|
|
24
|
+
|
|
25
|
+
Article.delete_all
|
|
26
|
+
|
|
27
|
+
articles = [
|
|
28
|
+
{ title: 'Article One', abstract: 'One', content: 'One', published_on: 1.day.ago, category_title: 'One', author_first_name: 'John', author_last_name: 'Smith' },
|
|
29
|
+
{ title: 'Article One Another', abstract: '', content: '', published_on: 2.days.ago, category_title: 'One', author_first_name: 'John', author_last_name: 'Smith' },
|
|
30
|
+
{ title: 'Article One Two', abstract: '', content: '', published_on: 10.days.ago, category_title: 'Two', author_first_name: 'Mary', author_last_name: 'Smith' },
|
|
31
|
+
{ title: 'Article Two', abstract: '', content: '', published_on: 12.days.ago, category_title: 'Two', author_first_name: 'Mary', author_last_name: 'Smith' },
|
|
32
|
+
{ title: 'Article Three', abstract: '', content: '', published_on: 12.days.ago, category_title: 'Three', author_first_name: 'Alice', author_last_name: 'Smith' }
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
articles.each do |a|
|
|
36
|
+
article = Article.create! \
|
|
37
|
+
title: a[:title],
|
|
38
|
+
abstract: a[:abstract],
|
|
39
|
+
content: a[:content],
|
|
40
|
+
published_on: a[:published_on]
|
|
41
|
+
|
|
42
|
+
article.categories << Category.find_or_create_by!(title: a[:category_title])
|
|
43
|
+
|
|
44
|
+
article.authors << Author.find_or_create_by!(first_name: a[:author_first_name], last_name: a[:author_last_name])
|
|
45
|
+
|
|
46
|
+
article.save!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Article.find_by_title('Article Three').comments.create body: 'One'
|
|
50
|
+
|
|
51
|
+
Sidekiq::Queue.new("elasticsearch").clear
|
|
52
|
+
|
|
53
|
+
Article.__elasticsearch__.import force: true
|
|
54
|
+
Article.__elasticsearch__.refresh_index!
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test "should return search results" do
|
|
58
|
+
get :index, params: { q: 'one' }
|
|
59
|
+
assert_response :success
|
|
60
|
+
assert_equal 3, assigns(:articles).size
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test "should return search results in comments" do
|
|
64
|
+
get :index, params: { q: 'one', comments: 'y' }
|
|
65
|
+
assert_response :success
|
|
66
|
+
assert_equal 4, assigns(:articles).size
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
test "should return highlighted snippets" do
|
|
70
|
+
get :index, params: { q: 'one' }
|
|
71
|
+
assert_response :success
|
|
72
|
+
assert_match %r{<em class="label label-highlight">One</em>}, assigns(:articles).first.highlight.title.first
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "should return suggestions" do
|
|
76
|
+
get :index, params: { q: 'one' }
|
|
77
|
+
assert_response :success
|
|
78
|
+
|
|
79
|
+
suggestions = assigns(:articles).response.suggestions
|
|
80
|
+
|
|
81
|
+
assert_equal 'one', suggestions['suggest_title'][0]['text']
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
test "should return aggregations" do
|
|
85
|
+
get :index, params: { q: 'one' }
|
|
86
|
+
assert_response :success
|
|
87
|
+
|
|
88
|
+
aggregations = assigns(:articles).response.response['aggregations']
|
|
89
|
+
|
|
90
|
+
assert_equal 2, aggregations['categories']['categories']['buckets'].size
|
|
91
|
+
assert_equal 2, aggregations['authors']['authors']['buckets'].size
|
|
92
|
+
assert_equal 2, aggregations['published']['published']['buckets'].size
|
|
93
|
+
|
|
94
|
+
assert_equal 'John Smith', aggregations['authors']['authors']['buckets'][0]['key']
|
|
95
|
+
assert_equal 'One', aggregations['categories']['categories']['buckets'][0]['key']
|
|
96
|
+
assert_equal '2015-03-02T00:00:00.000Z', aggregations['published']['published']['buckets'][0]['key_as_string']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
test "should sort on the published date" do
|
|
100
|
+
get :index, params: { q: 'one', s: 'published_on' }
|
|
101
|
+
assert_response :success
|
|
102
|
+
|
|
103
|
+
assert_equal 3, assigns(:articles).size
|
|
104
|
+
assert_equal '2015-03-15', assigns(:articles)[0].published_on
|
|
105
|
+
assert_equal '2015-03-14', assigns(:articles)[1].published_on
|
|
106
|
+
assert_equal '2015-03-06', assigns(:articles)[2].published_on
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
test "should sort on the published date when no query is provided" do
|
|
110
|
+
get :index, params: { q: '' }
|
|
111
|
+
assert_response :success
|
|
112
|
+
|
|
113
|
+
assert_equal 5, assigns(:articles).size
|
|
114
|
+
assert_equal '2015-03-15', assigns(:articles)[0].published_on
|
|
115
|
+
assert_equal '2015-03-14', assigns(:articles)[1].published_on
|
|
116
|
+
assert_equal '2015-03-06', assigns(:articles)[2].published_on
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
test "should filter search results and the author and published date facets when user selects a category" do
|
|
120
|
+
get :index, params: { q: 'one', c: 'One' }
|
|
121
|
+
assert_response :success
|
|
122
|
+
|
|
123
|
+
assert_equal 2, assigns(:articles).size
|
|
124
|
+
|
|
125
|
+
aggregations = assigns(:articles).response.response['aggregations']
|
|
126
|
+
|
|
127
|
+
assert_equal 1, aggregations['authors']['authors']['buckets'].size
|
|
128
|
+
assert_equal 1, aggregations['published']['published']['buckets'].size
|
|
129
|
+
|
|
130
|
+
# Do NOT filter the category facet
|
|
131
|
+
assert_equal 2, aggregations['categories']['categories']['buckets'].size
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
test "should filter search results and the category and published date facets when user selects a category" do
|
|
135
|
+
get :index, params: { q: 'one', a: 'Mary Smith' }
|
|
136
|
+
assert_response :success
|
|
137
|
+
|
|
138
|
+
assert_equal 1, assigns(:articles).size
|
|
139
|
+
|
|
140
|
+
aggregations = assigns(:articles).response.response['aggregations']
|
|
141
|
+
|
|
142
|
+
assert_equal 1, aggregations['categories']['categories']['buckets'].size
|
|
143
|
+
assert_equal 1, aggregations['published']['published']['buckets'].size
|
|
144
|
+
|
|
145
|
+
# Do NOT filter the authors facet
|
|
146
|
+
assert_equal 2, aggregations['authors']['authors']['buckets'].size
|
|
147
|
+
end
|
|
148
|
+
end
|