@@ -11,14 +11,24 @@ class Question < ActiveRecord::Base
11
11
12
12
has_many :answers , dependent : :destroy
13
13
14
- index_name 'questions_and_answers'
14
+ JOIN_TYPE = 'question' . freeze
15
+ JOIN_METADATA = { join_field : JOIN_TYPE } . freeze
16
+
17
+ index_name 'questions_and_answers' . freeze
18
+ document_type 'doc' . freeze
15
19
16
20
mapping do
17
21
indexes :title
18
22
indexes :text
19
23
indexes :author
20
24
end
21
25
26
+ def as_indexed_json ( options = { } )
27
+ # This line is necessary for differences between ActiveModel::Serializers::JSON#as_json versions
28
+ json = as_json ( options ) [ JOIN_TYPE ] || as_json ( options )
29
+ json . merge ( JOIN_METADATA )
30
+ end
31
+
22
32
after_commit lambda { __elasticsearch__ . index_document } , on : :create
23
33
after_commit lambda { __elasticsearch__ . update_document } , on : :update
24
34
after_commit lambda { __elasticsearch__ . delete_document } , on : :destroy
@@ -29,32 +39,55 @@ class Answer < ActiveRecord::Base
29
39
30
40
belongs_to :question
31
41
32
- index_name 'questions_and_answers'
42
+ JOIN_TYPE = 'answer' . freeze
43
+
44
+ index_name 'questions_and_answers' . freeze
45
+ document_type 'doc' . freeze
46
+
47
+ before_create :randomize_id
48
+
49
+ def randomize_id
50
+ begin
51
+ self . id = SecureRandom . random_number ( 1_000_000 )
52
+ end while Answer . where ( id : self . id ) . exists?
53
+ end
33
54
34
- mapping _parent : { type : 'question' } , _routing : { required : true } do
55
+ mapping do
35
56
indexes :text
36
57
indexes :author
37
58
end
38
59
39
- after_commit lambda { __elasticsearch__ . index_document ( parent : question_id ) } , on : :create
40
- after_commit lambda { __elasticsearch__ . update_document ( parent : question_id ) } , on : :update
41
- after_commit lambda { __elasticsearch__ . delete_document ( parent : question_id ) } , on : :destroy
60
+ def as_indexed_json ( options = { } )
61
+ # This line is necessary for differences between ActiveModel::Serializers::JSON#as_json versions
62
+ json = as_json ( options ) [ JOIN_TYPE ] || as_json ( options )
63
+ json . merge ( join_field : { name : JOIN_TYPE , parent : question_id } )
64
+ end
65
+
66
+ after_commit lambda { __elasticsearch__ . index_document ( routing : ( question_id || 1 ) ) } , on : :create
67
+ after_commit lambda { __elasticsearch__ . update_document ( routing : ( question_id || 1 ) ) } , on : :update
68
+ after_commit lambda { __elasticsearch__ . delete_document ( routing : ( question_id || 1 ) ) } , on : :destroy
42
69
end
43
70
44
71
module ParentChildSearchable
45
- INDEX_NAME = 'questions_and_answers'
72
+ INDEX_NAME = 'questions_and_answers' . freeze
73
+ JOIN = 'join' . freeze
46
74
47
75
def create_index! ( options = { } )
48
76
client = Question . __elasticsearch__ . client
49
77
client . indices . delete index : INDEX_NAME rescue nil if options [ :force ]
50
78
51
79
settings = Question . settings . to_hash . merge Answer . settings . to_hash
52
- mappings = Question . mappings . to_hash . merge Answer . mappings . to_hash
80
+ mapping_properties = { join_field : { type : JOIN ,
81
+ relations : { Question ::JOIN_TYPE => Answer ::JOIN_TYPE } } }
82
+
83
+ merged_properties = mapping_properties . merge ( Question . mappings . to_hash [ :doc ] [ :properties ] ) . merge (
84
+ Answer . mappings . to_hash [ :doc ] [ :properties ] )
85
+ mappings = { doc : { properties : merged_properties } }
53
86
54
87
client . indices . create index : INDEX_NAME ,
55
88
body : {
56
- settings : settings . to_hash ,
57
- mappings : mappings . to_hash }
89
+ settings : settings . to_hash ,
90
+ mappings : mappings }
58
91
end
59
92
60
93
extend self
@@ -100,34 +133,34 @@ class ActiveRecordAssociationsParentChildIntegrationTest < Elasticsearch::Test::
100
133
101
134
should "find questions by matching answers" do
102
135
response = Question . search (
103
- { query : {
104
- has_child : {
105
- type : 'answer' ,
106
- query : {
107
- match : {
108
- author : 'john'
109
- }
110
- }
111
- }
112
- }
113
- } )
136
+ { query : {
137
+ has_child : {
138
+ type : 'answer' ,
139
+ query : {
140
+ match : {
141
+ author : 'john'
142
+ }
143
+ }
144
+ }
145
+ }
146
+ } )
114
147
115
148
assert_equal 'Second Question' , response . records . first . title
116
149
end
117
150
118
151
should "find answers for matching questions" do
119
152
response = Answer . search (
120
- { query : {
121
- has_parent : {
122
- parent_type : 'question' ,
123
- query : {
124
- match : {
125
- author : 'john'
126
- }
127
- }
128
- }
129
- }
130
- } )
153
+ { query : {
154
+ has_parent : {
155
+ parent_type : 'question' ,
156
+ query : {
157
+ match : {
158
+ author : 'john'
159
+ }
160
+ }
161
+ }
162
+ }
163
+ } )
131
164
132
165
assert_same_elements [ 'Adam' , 'Ryan' ] , response . records . map ( &:author )
133
166
end
@@ -136,12 +169,20 @@ class ActiveRecordAssociationsParentChildIntegrationTest < Elasticsearch::Test::
136
169
Question . where ( title : 'First Question' ) . each ( &:destroy )
137
170
Question . __elasticsearch__ . refresh_index!
138
171
139
- response = Answer . search query : { match_all : { } }
172
+ response = Answer . search (
173
+ { query : {
174
+ has_parent : {
175
+ parent_type : 'question' ,
176
+ query : {
177
+ match_all : { }
178
+ }
179
+ }
180
+ }
181
+ } )
140
182
141
183
assert_equal 1 , response . results . total
142
184
end
143
185
end
144
-
145
186
end
146
187
end
147
188
end
0 commit comments