Skip to content

Commit 4e59ee2

Browse files
committed
[MODEL] Added integration test for complex model with associations
1 parent 2cbc937 commit 4e59ee2

File tree

1 file changed

+304
-0
lines changed

1 file changed

+304
-0
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
require 'test_helper'
2+
3+
# ----- Models definition -------------------------------------------------------------------------
4+
5+
class Category < ActiveRecord::Base
6+
has_and_belongs_to_many :posts
7+
end
8+
9+
class Author < ActiveRecord::Base
10+
has_many :authorships
11+
12+
def full_name
13+
[first_name, last_name].compact.join(' ')
14+
end
15+
end
16+
17+
class Authorship < ActiveRecord::Base
18+
belongs_to :author
19+
belongs_to :post, touch: true
20+
end
21+
22+
class Comment < ActiveRecord::Base
23+
belongs_to :post, touch: true
24+
end
25+
26+
class Post < ActiveRecord::Base
27+
has_and_belongs_to_many :categories, after_add: [ lambda { |a,c| a.__elasticsearch__.index_document } ],
28+
after_remove: [ lambda { |a,c| a.__elasticsearch__.index_document } ]
29+
has_many :authorships
30+
has_many :authors, through: :authorships
31+
has_many :comments
32+
end
33+
34+
# ----- Search integration via Concern module -----------------------------------------------------
35+
36+
module Searchable
37+
extend ActiveSupport::Concern
38+
39+
included do
40+
include Elasticsearch::Model
41+
include Elasticsearch::Model::Callbacks
42+
43+
# Set up the mapping
44+
#
45+
mapping do
46+
indexes :title, analyzer: 'snowball'
47+
indexes :created_at, type: 'date'
48+
49+
indexes :authors do
50+
indexes :first_name
51+
indexes :last_name
52+
indexes :full_name, type: 'multi_field' do
53+
indexes :full_name
54+
indexes :raw, analyzer: 'keyword'
55+
end
56+
end
57+
58+
indexes :categories, analyzer: 'keyword'
59+
60+
indexes :comments, type: 'nested' do
61+
indexes :text
62+
indexes :author
63+
end
64+
end
65+
66+
# Customize the JSON serialization for Elasticsearch
67+
#
68+
def as_indexed_json(options={})
69+
{
70+
title: title,
71+
text: text,
72+
categories: categories.map(&:title),
73+
authors: authors.as_json(methods: [:full_name], only: [:full_name, :first_name, :last_name]),
74+
comments: comments.as_json(only: [:text, :author])
75+
}
76+
end
77+
78+
# Update document in the index after touch
79+
#
80+
after_touch() { __elasticsearch__.index_document }
81+
end
82+
end
83+
84+
# Include the search integration
85+
#
86+
Post.__send__ :include, Searchable
87+
88+
module Elasticsearch
89+
module Model
90+
class ActiveRecordAssociationsIntegrationTest < Elasticsearch::Test::IntegrationTestCase
91+
92+
context "ActiveRecord associations" do
93+
setup do
94+
95+
# ----- Schema definition ---------------------------------------------------------------
96+
97+
ActiveRecord::Schema.define(version: 1) do
98+
create_table :categories do |t|
99+
t.string :title
100+
t.timestamps
101+
end
102+
103+
create_table :categories_posts, id: false do |t|
104+
t.references :post, :category
105+
end
106+
107+
create_table :authors do |t|
108+
t.string :first_name, :last_name
109+
t.timestamps
110+
end
111+
112+
create_table :authorships do |t|
113+
t.string :first_name, :last_name
114+
t.references :post
115+
t.references :author
116+
t.timestamps
117+
end
118+
119+
create_table :comments do |t|
120+
t.string :text
121+
t.string :author
122+
t.references :post
123+
t.timestamps
124+
end and add_index(:comments, :post_id)
125+
126+
create_table :posts do |t|
127+
t.string :title
128+
t.text :text
129+
t.boolean :published
130+
t.timestamps
131+
end
132+
end
133+
134+
# ----- Reset the index -----------------------------------------------------------------
135+
136+
Post.delete_all
137+
Post.__elasticsearch__.create_index! force: true
138+
end
139+
140+
should "index and find a document" do
141+
Post.create! title: 'Test'
142+
Post.create! title: 'Testing Coding'
143+
Post.create! title: 'Coding'
144+
Post.__elasticsearch__.refresh_index!
145+
146+
response = Post.search('title:test')
147+
148+
assert_equal 2, response.results.size
149+
assert_equal 2, response.records.size
150+
151+
assert_equal 'Test', response.results.first.title
152+
assert_equal 'Test', response.records.first.title
153+
end
154+
155+
should "reindex a document after categories are changed" do
156+
# Create categories
157+
category_a = Category.where(title: "One").first_or_create!
158+
category_b = Category.where(title: "Two").first_or_create!
159+
160+
# Create post
161+
post = Post.create! title: "First Post", text: "This is the first post..."
162+
163+
# Assign categories
164+
post.categories = [category_a, category_b]
165+
166+
Post.__elasticsearch__.refresh_index!
167+
168+
query = { query: {
169+
filtered: {
170+
query: {
171+
multi_match: {
172+
fields: ['title'],
173+
query: 'first'
174+
}
175+
},
176+
filter: {
177+
terms: {
178+
categories: ['One']
179+
}
180+
}
181+
}
182+
}
183+
}
184+
185+
response = Post.search query
186+
187+
assert_equal 1, response.results.size
188+
assert_equal 1, response.records.size
189+
190+
# Remove category "One"
191+
post.categories = [category_b]
192+
193+
Post.__elasticsearch__.refresh_index!
194+
response = Post.search query
195+
196+
assert_equal 0, response.results.size
197+
assert_equal 0, response.records.size
198+
end
199+
200+
should "reindex a document after authors are changed" do
201+
# Create authors
202+
author_a = Author.where(first_name: "John", last_name: "Smith").first_or_create!
203+
author_b = Author.where(first_name: "Mary", last_name: "Smith").first_or_create!
204+
author_c = Author.where(first_name: "Kobe", last_name: "Griss").first_or_create!
205+
206+
# Create posts
207+
post_1 = Post.create! title: "First Post", text: "This is the first post..."
208+
post_2 = Post.create! title: "Second Post", text: "This is the second post..."
209+
post_3 = Post.create! title: "Third Post", text: "This is the third post..."
210+
211+
# Assign authors
212+
post_1.authors = [author_a, author_b]
213+
post_2.authors = [author_a]
214+
post_3.authors = [author_c]
215+
216+
Post.__elasticsearch__.refresh_index!
217+
218+
response = Post.search 'authors.full_name:john'
219+
220+
assert_equal 2, response.results.size
221+
assert_equal 2, response.records.size
222+
223+
post_3.authors << author_a
224+
225+
Post.__elasticsearch__.refresh_index!
226+
227+
response = Post.search 'authors.full_name:john'
228+
229+
assert_equal 3, response.results.size
230+
assert_equal 3, response.records.size
231+
end
232+
233+
should "reindex a document after comments are added" do
234+
# Create posts
235+
post_1 = Post.create! title: "First Post", text: "This is the first post..."
236+
post_2 = Post.create! title: "Second Post", text: "This is the second post..."
237+
238+
# Add comments
239+
post_1.comments.create! author: 'John', text: 'Excellent'
240+
post_1.comments.create! author: 'Abby', text: 'Good'
241+
242+
post_2.comments.create! author: 'John', text: 'Terrible'
243+
244+
Post.__elasticsearch__.refresh_index!
245+
246+
response = Post.search 'comments.author:john AND comments.text:good'
247+
assert_equal 0, response.results.size
248+
249+
# Add comment
250+
post_1.comments.create! author: 'John', text: 'Or rather just good...'
251+
252+
Post.__elasticsearch__.refresh_index!
253+
254+
response = Post.search 'comments.author:john AND comments.text:good'
255+
assert_equal 0, response.results.size
256+
257+
response = Post.search \
258+
query: {
259+
nested: {
260+
path: 'comments',
261+
query: {
262+
bool: {
263+
must: [
264+
{ match: { 'comments.author' => 'john' } },
265+
{ match: { 'comments.text' => 'good' } }
266+
]
267+
}
268+
}
269+
}
270+
}
271+
272+
assert_equal 1, response.results.size
273+
end
274+
275+
should "reindex a document after Post#touch" do
276+
# Create categories
277+
category_a = Category.where(title: "One").first_or_create!
278+
279+
# Create post
280+
post = Post.create! title: "First Post", text: "This is the first post..."
281+
282+
# Assign category
283+
post.categories << category_a
284+
285+
Post.__elasticsearch__.refresh_index!
286+
287+
assert_equal 1, Post.search('categories:One').size
288+
289+
# Update category
290+
category_a.update_attribute :title, "Updated"
291+
292+
# Trigger touch on posts in category
293+
category_a.posts.each { |p| p.touch }
294+
295+
Post.__elasticsearch__.refresh_index!
296+
297+
assert_equal 0, Post.search('categories:One').size
298+
assert_equal 1, Post.search('categories:Updated').size
299+
end
300+
end
301+
302+
end
303+
end
304+
end

0 commit comments

Comments
 (0)