Skip to content

Commit 5af7c90

Browse files
committed
[STORE] Updated the "Music" example application for Elasticsearch 5.x
The "Music" application had mapping and query definitions incompatible with the latest major Elasticsearch version. This patch fixes that. The main changes are: * Update the field types in mappings (`string` -> `text`, etc) * Update the mapping for the `suggest` field. Instead of using the `input`, `output` and `payload` options when indexing the field, replicate this structure as regular JSON hierarchy. * Correspondingly, update the `IndexManager` to index documents in a proper structure. * Rewrite the output of `Suggester.as_json` method to be easily consumable by the JQuery Autocomplete component. * Small fixes and tweaks for the visual style of the application. * Update the template itself for Rails 5, to download Elasticsearch 5 when it's not installed, etc
1 parent 2db5634 commit 5af7c90

File tree

12 files changed

+300
-143
lines changed

12 files changed

+300
-143
lines changed

elasticsearch-persistence/examples/music/album.rb

+23-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ class Album
1212

1313
index_name [Rails.application.engine_name, Rails.env].join('-')
1414

15+
1516
mapping _parent: { type: 'artist' } do
16-
indexes :suggest_title, type: 'completion', payloads: true
17-
indexes :suggest_track, type: 'completion', payloads: true
1817
end
1918

2019
attribute :artist
@@ -30,4 +29,26 @@ class Album
3029

3130
attribute :styles
3231
attribute :meta, Meta, mapping: { type: 'object' }
32+
33+
attribute :suggest, Hashie::Mash, mapping: {
34+
type: 'object',
35+
properties: {
36+
title: {
37+
type: 'object',
38+
properties: {
39+
input: { type: 'completion' },
40+
output: { type: 'keyword', index: false },
41+
payload: { type: 'object', enabled: false }
42+
}
43+
},
44+
track: {
45+
type: 'object',
46+
properties: {
47+
input: { type: 'completion' },
48+
output: { type: 'keyword', index: false },
49+
payload: { type: 'object', enabled: false }
50+
}
51+
}
52+
}
53+
}
3354
end

elasticsearch-persistence/examples/music/artist.rb

+25-5
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,43 @@ class Artist
44
index_name [Rails.application.engine_name, Rails.env].join('-')
55

66
analyzed_and_raw = { fields: {
7-
name: { type: 'string', analyzer: 'snowball' },
8-
raw: { type: 'string', analyzer: 'keyword' }
7+
name: { type: 'text', analyzer: 'snowball' },
8+
raw: { type: 'keyword' }
99
} }
1010

1111
attribute :name, String, mapping: analyzed_and_raw
12-
attribute :suggest_name, String, default: {}, mapping: { type: 'completion', payloads: true }
1312

1413
attribute :profile
1514
attribute :date, Date
1615

1716
attribute :members, String, default: [], mapping: analyzed_and_raw
1817
attribute :members_combined, String, default: [], mapping: { analyzer: 'snowball' }
19-
attribute :suggest_member, String, default: {}, mapping: { type: 'completion', payloads: true }
2018

2119
attribute :urls, String, default: []
2220
attribute :album_count, Integer, default: 0
2321

22+
attribute :suggest, Hashie::Mash, mapping: {
23+
type: 'object',
24+
properties: {
25+
name: {
26+
type: 'object',
27+
properties: {
28+
input: { type: 'completion' },
29+
output: { type: 'keyword', index: false },
30+
payload: { type: 'object', enabled: false }
31+
}
32+
},
33+
member: {
34+
type: 'object',
35+
properties: {
36+
input: { type: 'completion' },
37+
output: { type: 'keyword', index: false },
38+
payload: { type: 'object', enabled: false }
39+
}
40+
}
41+
}
42+
}
43+
2444
validates :name, presence: true
2545

2646
def albums
@@ -29,7 +49,7 @@ def albums
2949
has_parent: {
3050
type: 'artist',
3151
query: {
32-
filtered: {
52+
bool: {
3353
filter: {
3454
ids: { values: [ self.id ] }
3555
}

elasticsearch-persistence/examples/music/artists/index.html.erb

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<% @artists.each do |artist| %>
1616
<%= div_for artist, class: 'result clearfix' do %>
1717
<h2>
18+
<%= image_tag "http://ruby.elastic.co.s3-website-us-east-1.amazonaws.com/demo/music/bands/#{artist.id}.jpeg", height: '50px', class: 'band' %>
1819
<%= link_to artist do %>
1920
<span class="name"><%= artist.name %></span>
2021
<small><%= pluralize artist.album_count, 'album' %></small>
@@ -41,7 +42,7 @@ $.widget( "custom.suggest", $.ui.autocomplete, {
4142
var category = ul.append( "<li class='ui-autocomplete-category'>" + item.label + "</li>" );
4243

4344
$.each( item.value, function( index, item ) {
44-
var li = $('<li class="ui-autocomplete-item"><a href="<%= Rails.application.config.relative_url_root %>'+ item.payload.url +'">'+ item.text +'</a></li>').data('ui-autocomplete-item', item )
45+
var li = $('<li class="ui-autocomplete-item"><a href="<%= Rails.application.config.relative_url_root %>'+ item.url +'">'+ item.text +'</a></li>').data('ui-autocomplete-item', item )
4546
category.append(li)
4647
} )
4748
});
@@ -51,7 +52,9 @@ $.widget( "custom.suggest", $.ui.autocomplete, {
5152
$( "#q" ).suggest({
5253
source: '<%= suggest_path %>',
5354
select: function(event, ui) {
54-
document.location.href = '<%= Rails.application.config.relative_url_root %>'+ui.item.payload.url
55+
document.location.href = '<%= Rails.application.config.relative_url_root %>'+ui.item.url
5556
}
5657
});
5758
</script>
59+
60+
<script>$('img.band').error(function(){ $(this).attr('src', '/images/blank_artist.png'); });</script>
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,54 @@
1-
<header>
2-
<h1>
3-
<span class="back"><%= link_to "〈".html_safe, artists_path, title: "Back" %></span>
4-
<%= @artist.name %>
5-
<%= button_to 'Edit', edit_artist_path(@artist), method: 'get' %>
6-
</h1>
7-
</header>
1+
<div class="artist">
2+
<header>
3+
<h1>
4+
<span class="back"><%= link_to "〈".html_safe, artists_path, title: "Back" %></span>
5+
<%= image_tag "http://ruby.elastic.co.s3-website-us-east-1.amazonaws.com/demo/music/bands/#{@artist.id}.jpeg", height: '50px', class: 'band' %>
6+
<%= @artist.name %>
7+
<%= button_to 'Edit', edit_artist_path(@artist), method: 'get' %>
8+
</h1>
9+
</header>
810

9-
<p id="notice"><%= notice %></p>
11+
<p id="notice"><%= notice %></p>
1012

11-
<section class="artist-info">
12-
<span><%= @artist.members.to_sentence last_word_connector: ' and ' %></span> |
13-
<span><%= pluralize @albums.size, 'album' %></span>
14-
<p class="artist-profile"><%= @artist.profile %></p>
15-
</section>
13+
<section class="artist-info">
14+
<span><%= @artist.members.to_sentence last_word_connector: ' and ' %></span> |
15+
<span><%= pluralize @albums.size, 'album' %></span>
16+
<p class="artist-profile"><%= @artist.profile %></p>
17+
</section>
1618

17-
<section class="albums">
18-
<% @albums.each do |album| %>
19-
<%= div_for album, class: 'clearfix' do %>
20-
<h3>
21-
<span class="title"><%= album.title %></span>
22-
<div class="info">
23-
<small><%= album.meta.formats.join(', ') %></small>
24-
<small><%= album.released %></small>
19+
<section class="albums">
20+
<% @albums.each do |album| %>
21+
<%= div_for album, class: 'clearfix' do %>
22+
<h3>
23+
<span class="title"><%= album.title %></span>
24+
<div class="info">
25+
<small><%= album.meta.formats.join(', ') %></small>
26+
<small><%= album.released %></small>
27+
</div>
28+
</h3>
29+
30+
<div class="cover">
31+
<%= image_tag "http://ruby.elastic.co.s3-website-us-east-1.amazonaws.com/demo/music/covers/#{album.id}.jpeg", width: '100px', class: 'cover' %>
2532
</div>
26-
</h3>
2733

28-
<div class="cover">
29-
<%= image_tag "http://ruby-demo-assets.s3.amazonaws.com/discogs/covers/#{album.id}.jpeg", width: '100px', class: 'cover' %>
30-
</div>
34+
<div class="content">
35+
<% album.tracklist.in_groups_of(album.tracklist.size/2+1).each_with_index do |half, g| %>
36+
<ul class=<%= cycle 'first', 'second' %> start="<%= g < 1 ? 1 : album.tracklist.size/2+2 %>">
37+
<% half.compact.each_with_index do |track, i| %>
38+
<li>
39+
<i class="counter"><%= g < 1 ? i+1 : i+(g*album.tracklist.size/2+2) %></i>
40+
<%= track['title'] %>
41+
<small><%= track['duration'] %></small>
42+
</li>
43+
<% end %>
44+
</ul>
45+
<% end %>
46+
</div>
47+
<% end %>
3148

32-
<div class="content">
33-
<% album.tracklist.in_groups_of(album.tracklist.size/2+1).each_with_index do |half, g| %>
34-
<ul class=<%= cycle 'first', 'second' %> start="<%= g < 1 ? 1 : album.tracklist.size/2+2 %>">
35-
<% half.compact.each_with_index do |track, i| %>
36-
<li>
37-
<i class="counter"><%= g < 1 ? i+1 : i+(g*album.tracklist.size/2+2) %></i>
38-
<%= track['title'] %>
39-
<small><%= track['duration'] %></small>
40-
</li>
41-
<% end %>
42-
</ul>
43-
<% end %>
44-
</div>
4549
<% end %>
4650

47-
<% end %>
48-
49-
<script>$('img').error(function(){ $(this).attr('src', '/images/blank_cover.png'); });</script>
50-
<script>$(document.location.hash).effect('highlight', 1500)</script>
51-
</section>
51+
<script>$('img').error(function(){ $(this).attr('src', '/images/blank_cover.png'); });</script>
52+
<script>$(document.location.hash).effect('highlight', 1500)</script>
53+
</section>
54+
</div>

elasticsearch-persistence/examples/music/assets/application.css

+34-3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ h1 form {
100100

101101
.artist h2 {
102102
float: left;
103+
margin-left: 50px;
104+
}
105+
106+
.artist.search.result h2 {
107+
float: none;
108+
}
109+
110+
.artist h1 .back {
111+
margin-right: 65px;
112+
}
113+
114+
.artist h1 img.band {
115+
left: 120px;
116+
top: 50px;
103117
}
104118

105119
.result h2 a,
@@ -124,17 +138,17 @@ h1 form {
124138
text-decoration: underline;
125139
}
126140

127-
.result .small {
141+
.result .highlight.small {
128142
font-size: 90%;
129143
font-weight: 200;
130144
padding: 0;
131-
margin: 0.25em 0 0.25em 0;
145+
margin: 0.25em 0 0.25em 50px;
132146
}
133147

134148
.result .small .label {
135149
color: #999;
136150
font-size: 80%;
137-
min-width: 5em;
151+
/*min-width: 5em;*/
138152
display: inline-block;
139153
}
140154

@@ -156,15 +170,32 @@ h1 form {
156170
margin: 0.25em 0 0 0;
157171
}
158172

173+
.artist img.band {
174+
position: absolute;
175+
left: 85px;
176+
margin-top: 14px;
177+
transform: translate(-50%,-50%);
178+
clip-path: circle(20px at center);
179+
}
180+
159181
.album {
160182
margin: 0 0 4em 0;
161183
}
162184

185+
.album.search.result {
186+
margin: 0;
187+
}
188+
163189
.album .cover {
164190
float: left;
165191
width: 150px;
166192
}
167193

194+
.album.search.result .cover {
195+
width: 40px;
196+
margin-right: 10px;
197+
}
198+
168199
.album .cover img {
169200
border: 1px solid rgba(0,0,0,0.15);
170201
box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.05);
Loading

elasticsearch-persistence/examples/music/index_manager.rb

+31-18
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,46 @@ def self.import_from_yaml(source, options={})
2626
Artist.create artist.update(
2727
'album_count' => artist['releases'].size,
2828
'members_combined' => artist['members'].join(', '),
29-
'suggest_name' => {
30-
'input' => artist['namevariations'].unshift(artist['name']),
31-
'output' => artist['name'],
32-
'payload' => { 'url' => "/artists/#{artist['id']}" }
33-
},
34-
'suggest_member' => {
35-
'input' => artist['members'],
36-
'output' => artist['name'],
37-
'payload' => { 'url' => "/artists/#{artist['id']}" }
29+
'suggest' => {
30+
'name' => {
31+
'input' => { 'input' => artist['namevariations'].unshift(artist['name']).reject { |d| d.to_s.empty? } },
32+
'output' => artist['name'],
33+
'payload' => {
34+
'url' => "/artists/#{artist['id']}"
35+
}
36+
},
37+
'member' => {
38+
'input' => { 'input' => artist['members'] },
39+
'output' => artist['name'],
40+
'payload' => {
41+
'url' => "/artists/#{artist['id']}"
42+
}
43+
}
3844
}
3945
)
4046

4147
artist['releases'].each do |album|
4248
album.update(
43-
'suggest_title' => {
44-
'input' => album['title'],
45-
'output' => album['title'],
46-
'payload' => { 'url' => "/artists/#{artist['id']}#album_#{album['id']}" }
47-
},
48-
'suggest_track' => {
49-
'input' => album['tracklist'].map { |d| d['title'] },
50-
'output' => album['title'],
51-
'payload' => { 'url' => "/artists/#{artist['id']}#album_#{album['id']}" }
49+
'suggest' => {
50+
'title' => {
51+
'input' => { 'input' => album['title'] },
52+
'output' => album['title'],
53+
'payload' => {
54+
'url' => "/artists/#{artist['id']}#album_#{album['id']}"
55+
}
56+
},
57+
'track' => {
58+
'input' => { 'input' => album['tracklist'].map { |d| d['title'] }.reject { |d| d.to_s.empty? } },
59+
'output' => album['title'],
60+
'payload' => {
61+
'url' => "/artists/#{artist['id']}#album_#{album['id']}"
62+
}
63+
}
5264
}
5365
)
5466
album['notes'] = album['notes'].to_s.gsub(/<.+?>/, '').gsub(/ {2,}/, '')
5567
album['released'] = nil if album['released'] < 1
68+
5669
Album.create album, id: album['id'], parent: artist['id']
5770
end
5871
end

0 commit comments

Comments
 (0)