sunspot 2.3.0 → 2.6.0
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 +4 -4
- data/Appraisals +4 -4
- data/lib/sunspot/adapters.rb +15 -1
- data/lib/sunspot/data_extractor.rb +36 -6
- data/lib/sunspot/dsl/fields.rb +16 -0
- data/lib/sunspot/dsl/fulltext.rb +1 -1
- data/lib/sunspot/dsl/group.rb +10 -0
- data/lib/sunspot/dsl/scope.rb +17 -17
- data/lib/sunspot/dsl/standard_query.rb +29 -1
- data/lib/sunspot/dsl.rb +2 -2
- data/lib/sunspot/field.rb +15 -4
- data/lib/sunspot/indexer.rb +37 -8
- data/lib/sunspot/query/abstract_fulltext.rb +7 -3
- data/lib/sunspot/query/abstract_json_field_facet.rb +3 -0
- data/lib/sunspot/query/composite_fulltext.rb +21 -2
- data/lib/sunspot/query/date_field_json_facet.rb +2 -16
- data/lib/sunspot/query/dismax.rb +10 -4
- data/lib/sunspot/query/function_query.rb +25 -1
- data/lib/sunspot/query/group.rb +4 -5
- data/lib/sunspot/query/join.rb +3 -5
- data/lib/sunspot/query/range_json_facet.rb +5 -2
- data/lib/sunspot/query/restriction.rb +18 -13
- data/lib/sunspot/query/standard_query.rb +12 -0
- data/lib/sunspot/search/abstract_search.rb +1 -1
- data/lib/sunspot/search/field_json_facet.rb +14 -3
- data/lib/sunspot/search/hit.rb +6 -1
- data/lib/sunspot/session.rb +9 -1
- data/lib/sunspot/setup.rb +69 -0
- data/lib/sunspot/util.rb +4 -11
- data/lib/sunspot/version.rb +1 -1
- data/lib/sunspot.rb +9 -1
- data/spec/api/adapters_spec.rb +13 -0
- data/spec/api/data_extractor_spec.rb +39 -0
- data/spec/api/indexer/removal_spec.rb +87 -0
- data/spec/api/query/connective_boost_examples.rb +85 -0
- data/spec/api/query/fulltext_examples.rb +6 -12
- data/spec/api/query/join_spec.rb +2 -2
- data/spec/api/query/standard_spec.rb +10 -0
- data/spec/api/search/hits_spec.rb +14 -0
- data/spec/api/setup_spec.rb +99 -0
- data/spec/api/sunspot_spec.rb +3 -0
- data/spec/helpers/indexer_helper.rb +22 -0
- data/spec/integration/atomic_updates_spec.rb +169 -5
- data/spec/integration/faceting_spec.rb +68 -34
- data/spec/integration/field_grouping_spec.rb +19 -0
- data/spec/integration/field_lists_spec.rb +16 -0
- data/spec/integration/geospatial_spec.rb +15 -0
- data/spec/integration/join_spec.rb +64 -0
- data/spec/integration/scoped_search_spec.rb +78 -0
- data/spec/mocks/adapters.rb +33 -0
- data/spec/mocks/connection.rb +6 -0
- data/spec/mocks/photo.rb +19 -5
- data/spec/mocks/post.rb +35 -1
- data/sunspot.gemspec +0 -2
- metadata +14 -8
- data/gemfiles/.gitkeep +0 -0
@@ -12,6 +12,20 @@ describe 'hits', :type => :search do
|
|
12
12
|
end).to eq([['Post', post_1.id.to_s], ['Post', post_2.id.to_s]])
|
13
13
|
end
|
14
14
|
|
15
|
+
it "should return ID prefix when used with compositeId shard router" do
|
16
|
+
Sunspot.index!(ModelWithPrefixId.new)
|
17
|
+
|
18
|
+
expect(Sunspot.search(ModelWithPrefixId).
|
19
|
+
hits.map { |h| h.id_prefix }.uniq).to eq ["USERDATA!"]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should parse nested ID prefixes" do
|
23
|
+
Sunspot.index!(ModelWithNestedPrefixId.new)
|
24
|
+
|
25
|
+
expect(Sunspot.search(ModelWithNestedPrefixId).
|
26
|
+
hits.map { |h| h.id_prefix }.uniq).to eq ["USER!USERDATA!"]
|
27
|
+
end
|
28
|
+
|
15
29
|
it 'returns search total as attribute of hits' do
|
16
30
|
stub_results(Post.new, 4)
|
17
31
|
expect(session.search(Post) do
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Sunspot::Setup do
|
4
|
+
context '#id_prefix_for_class' do
|
5
|
+
subject { Sunspot::Setup.for(clazz).id_prefix_for_class }
|
6
|
+
|
7
|
+
context 'when `id_prefix` is defined on model' do
|
8
|
+
context 'as Proc' do
|
9
|
+
let(:clazz) { PostWithProcPrefixId }
|
10
|
+
|
11
|
+
it 'returns nil' do
|
12
|
+
is_expected.to be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'as Symbol' do
|
17
|
+
let(:clazz) { PostWithSymbolPrefixId }
|
18
|
+
|
19
|
+
it 'returns nil' do
|
20
|
+
is_expected.to be_nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'as String' do
|
25
|
+
let(:clazz) { PostWithStringPrefixId }
|
26
|
+
|
27
|
+
it 'returns `id_prefix` value' do
|
28
|
+
is_expected.to eq('USERDATA!')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when `id_prefix` is not defined on model' do
|
34
|
+
let(:clazz) { PostWithoutPrefixId }
|
35
|
+
|
36
|
+
it 'returns nil' do
|
37
|
+
is_expected.to be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context '#id_prefix_defined?' do
|
43
|
+
subject { Sunspot::Setup.for(clazz).id_prefix_defined? }
|
44
|
+
|
45
|
+
context 'when `id_prefix` is defined on model' do
|
46
|
+
let(:clazz) { PostWithProcPrefixId }
|
47
|
+
|
48
|
+
it 'returns true' do
|
49
|
+
is_expected.to be_truthy
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when `id_prefix` is not defined on model' do
|
54
|
+
let(:clazz) { PostWithoutPrefixId }
|
55
|
+
|
56
|
+
it 'returns false' do
|
57
|
+
is_expected.to be_falsey
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context '#id_prefix_requires_instance?' do
|
63
|
+
subject { Sunspot::Setup.for(clazz).id_prefix_requires_instance? }
|
64
|
+
|
65
|
+
context 'when `id_prefix` is defined on model' do
|
66
|
+
context 'as Proc' do
|
67
|
+
let(:clazz) { PostWithProcPrefixId }
|
68
|
+
|
69
|
+
it 'returns true' do
|
70
|
+
is_expected.to be_truthy
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'as Symbol' do
|
75
|
+
let(:clazz) { PostWithSymbolPrefixId }
|
76
|
+
|
77
|
+
it 'returns true' do
|
78
|
+
is_expected.to be_truthy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'as String' do
|
83
|
+
let(:clazz) { PostWithStringPrefixId }
|
84
|
+
|
85
|
+
it 'returns false' do
|
86
|
+
is_expected.to be_falsey
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when `id_prefix` is not defined on model' do
|
92
|
+
let(:clazz) { PostWithoutPrefixId }
|
93
|
+
|
94
|
+
it 'returns false' do
|
95
|
+
is_expected.to be_falsey
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/spec/api/sunspot_spec.rb
CHANGED
@@ -14,4 +14,26 @@ module IndexerHelper
|
|
14
14
|
def values_in_last_document_for(field_name)
|
15
15
|
@connection.adds.last.last.fields_by_name(field_name).map { |field| field.value }
|
16
16
|
end
|
17
|
+
|
18
|
+
def index_post(post)
|
19
|
+
Sunspot.index!(post)
|
20
|
+
hit = find_post(post)
|
21
|
+
expect(hit).not_to be_nil
|
22
|
+
hit
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_post(post)
|
26
|
+
Sunspot.search(clazz).hits.find { |h| h.primary_key.to_i == post.id && h.id_prefix == id_prefix_value(post, id_prefix) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def id_prefix_value(post, id_prefix)
|
30
|
+
return unless id_prefix
|
31
|
+
return id_prefix if id_prefix.is_a?(String)
|
32
|
+
|
33
|
+
id_prefix.call(post)
|
34
|
+
end
|
35
|
+
|
36
|
+
def post_solr_id
|
37
|
+
"#{id_prefix_value(post, id_prefix)}#{clazz} #{post.id}"
|
38
|
+
end
|
17
39
|
end
|
@@ -1,6 +1,79 @@
|
|
1
1
|
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
2
|
|
3
|
-
|
3
|
+
shared_examples 'atomic update with instance as key' do
|
4
|
+
it 'updates record' do
|
5
|
+
post = clazz.new(title: 'A Title', featured: true)
|
6
|
+
Sunspot.index!(post)
|
7
|
+
|
8
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
9
|
+
|
10
|
+
Sunspot.atomic_update!(clazz, post => { title: 'A New Title' })
|
11
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A New Title', featured: true)
|
12
|
+
|
13
|
+
Sunspot.atomic_update!(clazz, post => { featured: false })
|
14
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A New Title', featured: false)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not print warning' do
|
18
|
+
post = clazz.new(title: 'A Title', featured: true)
|
19
|
+
Sunspot.index!(post)
|
20
|
+
|
21
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
22
|
+
|
23
|
+
expect do
|
24
|
+
Sunspot.atomic_update!(clazz, post => { title: 'A New Title' })
|
25
|
+
end.to_not output(Sunspot::AtomicUpdateRequireInstanceForCompositeIdMessage.call(clazz)).to_stderr
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'does not create duplicate document' do
|
29
|
+
post = clazz.new(title: 'A Title', featured: true)
|
30
|
+
Sunspot.index!(post)
|
31
|
+
|
32
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
33
|
+
|
34
|
+
Sunspot.atomic_update!(clazz, post => { title: 'A New Title' })
|
35
|
+
hit = find_indexed_post_with_prefix_id(post, nil)
|
36
|
+
expect(hit).to be_nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
shared_examples 'atomic update with id as key' do
|
41
|
+
it 'does not update record' do
|
42
|
+
post = clazz.new(title: 'A Title', featured: true)
|
43
|
+
Sunspot.index!(post)
|
44
|
+
|
45
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
46
|
+
|
47
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
48
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A Title', featured: true)
|
49
|
+
|
50
|
+
Sunspot.atomic_update!(clazz, post.id => { featured: false })
|
51
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A Title', featured: true)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'prints warning' do
|
55
|
+
post = clazz.new(title: 'A Title', featured: true)
|
56
|
+
Sunspot.index!(post)
|
57
|
+
|
58
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
59
|
+
|
60
|
+
expect do
|
61
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
62
|
+
end.to output(Sunspot::AtomicUpdateRequireInstanceForCompositeIdMessage.call(clazz) + "\n").to_stderr
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'creates duplicate document that have only fields provided for update' do
|
66
|
+
post = clazz.new(title: 'A Title', featured: true)
|
67
|
+
Sunspot.index!(post)
|
68
|
+
|
69
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
70
|
+
|
71
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
72
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, nil), title: 'A New Title', featured: nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'Atomic Update feature' do
|
4
77
|
before :all do
|
5
78
|
Sunspot.remove_all
|
6
79
|
end
|
@@ -18,10 +91,27 @@ describe 'atomic updates' do
|
|
18
91
|
hit
|
19
92
|
end
|
20
93
|
|
21
|
-
|
94
|
+
def find_and_validate_indexed_post_with_prefix_id(post, id_prefix)
|
95
|
+
hit = find_indexed_post_with_prefix_id(post, id_prefix_value(post, id_prefix))
|
96
|
+
expect(hit).not_to be_nil
|
97
|
+
hit
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_indexed_post_with_prefix_id(post, id_prefix)
|
101
|
+
Sunspot.search(post.class).hits.find { |h| h.primary_key.to_i == post.id && h.id_prefix == id_prefix }
|
102
|
+
end
|
103
|
+
|
104
|
+
def id_prefix_value(post, id_prefix)
|
105
|
+
return unless id_prefix
|
106
|
+
return id_prefix if id_prefix.is_a?(String)
|
107
|
+
|
108
|
+
id_prefix.call(post)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'updates single record fields one by one' do
|
22
112
|
post = Post.new(title: 'A Title', featured: true)
|
23
113
|
Sunspot.index!(post)
|
24
|
-
|
114
|
+
|
25
115
|
validate_hit(find_indexed_post(post.id), title: post.title, featured: post.featured)
|
26
116
|
|
27
117
|
Sunspot.atomic_update!(Post, post.id => {title: 'A New Title'})
|
@@ -31,7 +121,7 @@ describe 'atomic updates' do
|
|
31
121
|
validate_hit(find_indexed_post(post.id), title: 'A New Title', featured: false)
|
32
122
|
end
|
33
123
|
|
34
|
-
it '
|
124
|
+
it 'updates fields for multiple records' do
|
35
125
|
post1 = Post.new(title: 'A First Title', featured: true)
|
36
126
|
post2 = Post.new(title: 'A Second Title', featured: false)
|
37
127
|
Sunspot.index!(post1, post2)
|
@@ -44,7 +134,7 @@ describe 'atomic updates' do
|
|
44
134
|
validate_hit(find_indexed_post(post2.id), title: 'A Second Title', featured: true)
|
45
135
|
end
|
46
136
|
|
47
|
-
it '
|
137
|
+
it 'clears field value properly' do
|
48
138
|
post = Post.new(title: 'A Title', tags: %w(tag1 tag2), featured: true)
|
49
139
|
Sunspot.index!(post)
|
50
140
|
validate_hit(find_indexed_post(post.id), title: post.title, tag_list: post.tags, featured: true)
|
@@ -55,4 +145,78 @@ describe 'atomic updates' do
|
|
55
145
|
Sunspot.atomic_update!(Post, post.id => {featured: nil})
|
56
146
|
validate_hit(find_indexed_post(post.id), title: post.title, tag_list: nil, featured: nil)
|
57
147
|
end
|
148
|
+
|
149
|
+
context 'when `id_prefix` is defined on model' do
|
150
|
+
context 'as Proc' do
|
151
|
+
let(:clazz) { PostWithProcPrefixId }
|
152
|
+
let(:id_prefix) { lambda { |post| "USERDATA-#{post.id}!" } }
|
153
|
+
|
154
|
+
context 'and instance passed as key' do
|
155
|
+
include_examples 'atomic update with instance as key'
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'and id passed as key' do
|
159
|
+
include_examples 'atomic update with id as key'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'as Symbol' do
|
164
|
+
let(:clazz) { PostWithSymbolPrefixId }
|
165
|
+
let(:id_prefix) { lambda { |post| "#{post.title}!" } }
|
166
|
+
|
167
|
+
context 'and instance passed as key' do
|
168
|
+
include_examples 'atomic update with instance as key'
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'and id passed as key' do
|
172
|
+
include_examples 'atomic update with id as key'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'as String' do
|
177
|
+
let(:clazz) { PostWithStringPrefixId }
|
178
|
+
let(:id_prefix) { 'USERDATA!' }
|
179
|
+
|
180
|
+
context 'and instance passed as key' do
|
181
|
+
include_examples 'atomic update with instance as key'
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'and id passed as key' do
|
185
|
+
it 'updates record' do
|
186
|
+
post = clazz.new(title: 'A Title', featured: true)
|
187
|
+
Sunspot.index!(post)
|
188
|
+
|
189
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
190
|
+
|
191
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
192
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A New Title', featured: true)
|
193
|
+
|
194
|
+
Sunspot.atomic_update!(clazz, post.id => { featured: false })
|
195
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: 'A New Title', featured: false)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'does not print warning' do
|
199
|
+
post = clazz.new(title: 'A Title', featured: true)
|
200
|
+
Sunspot.index!(post)
|
201
|
+
|
202
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
203
|
+
|
204
|
+
expect do
|
205
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
206
|
+
end.to_not output(Sunspot::AtomicUpdateRequireInstanceForCompositeIdMessage.call(clazz) + "\n").to_stderr
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'does not create duplicate document' do
|
210
|
+
post = clazz.new(title: 'A Title', featured: true)
|
211
|
+
Sunspot.index!(post)
|
212
|
+
|
213
|
+
validate_hit(find_and_validate_indexed_post_with_prefix_id(post, id_prefix), title: post.title, featured: post.featured)
|
214
|
+
|
215
|
+
Sunspot.atomic_update!(clazz, post.id => { title: 'A New Title' })
|
216
|
+
hit = find_indexed_post_with_prefix_id(post, nil)
|
217
|
+
expect(hit).to be_nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
58
222
|
end
|
@@ -84,7 +84,7 @@ describe 'search faceting' do
|
|
84
84
|
end
|
85
85
|
expect(search.facet(:title).rows.map { |row| row.value }).to include('zero')
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
it 'should return facet rows from an offset' do
|
89
89
|
search = Sunspot.search(Post) do
|
90
90
|
facet :title, :offset => 3
|
@@ -214,6 +214,15 @@ describe 'search faceting' do
|
|
214
214
|
expect(search.facet(:title).rows.map { |row| row.value }).to eq(%w(four three two one))
|
215
215
|
end
|
216
216
|
|
217
|
+
it 'should include allBuckets and missing' do
|
218
|
+
search = Sunspot.search(Post) do
|
219
|
+
with :blog_id, 1
|
220
|
+
json_facet :title, all_buckets: true, missing: true
|
221
|
+
end
|
222
|
+
expect(search.facet(:title).other_count('allBuckets')).to eq(10)
|
223
|
+
expect(search.facet(:title).other_count('missing')).to eq(1)
|
224
|
+
end
|
225
|
+
|
217
226
|
it 'should limit facet values by prefix' do
|
218
227
|
search = Sunspot.search(Post) do
|
219
228
|
with :blog_id, 1
|
@@ -221,7 +230,64 @@ describe 'search faceting' do
|
|
221
230
|
end
|
222
231
|
expect(search.facet(:title).rows.map { |row| row.value }.sort).to eq(%w(three two))
|
223
232
|
end
|
233
|
+
end
|
224
234
|
|
235
|
+
context 'date or time json facet' do
|
236
|
+
before :all do
|
237
|
+
Sunspot.remove_all
|
238
|
+
posts = [
|
239
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 8, 20)),
|
240
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 7, 20)),
|
241
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 6, 20)),
|
242
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 6, 15)),
|
243
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 5, 20)),
|
244
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 4, 20)),
|
245
|
+
Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 3, 20))
|
246
|
+
]
|
247
|
+
posts.each { |p| Sunspot.index(p) }
|
248
|
+
Sunspot.commit
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'facets properly with the range specified as time_range' do
|
252
|
+
time_range = [Time.new(2020, 4, 1), Time.new(2020, 7, 31)]
|
253
|
+
search = Sunspot.search(Post) do
|
254
|
+
with :blog_id, 1
|
255
|
+
json_facet :published_at, time_range: time_range, gap: 1, gap_unit: 'MONTHS'
|
256
|
+
end
|
257
|
+
expected_rows = [
|
258
|
+
{ count: 1, value: Time.new(2020, 4, 1).utc.iso8601 },
|
259
|
+
{ count: 1, value: Time.new(2020, 5, 1).utc.iso8601 },
|
260
|
+
{ count: 2, value: Time.new(2020, 6, 1).utc.iso8601 },
|
261
|
+
{ count: 1, value: Time.new(2020, 7, 1).utc.iso8601 }
|
262
|
+
]
|
263
|
+
expect(search.facet(:published_at).rows.map { |row| { count: row.count, value: row.value } }).to eq(expected_rows)
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should use custom gap parameters if provided' do
|
267
|
+
time_range = [Time.new(2020, 4, 1), Time.new(2020, 7, 31)]
|
268
|
+
search = Sunspot.search(Post) do
|
269
|
+
with :blog_id, 1
|
270
|
+
json_facet :published_at, range: time_range, gap: 1, gap_unit: 'MONTHS'
|
271
|
+
end
|
272
|
+
expected_rows = [
|
273
|
+
{ count: 1, value: Time.new(2020, 4, 1).utc.iso8601 },
|
274
|
+
{ count: 1, value: Time.new(2020, 5, 1).utc.iso8601 },
|
275
|
+
{ count: 2, value: Time.new(2020, 6, 1).utc.iso8601 },
|
276
|
+
{ count: 1, value: Time.new(2020, 7, 1).utc.iso8601 }
|
277
|
+
]
|
278
|
+
expect(search.facet(:published_at).rows.map { |row| { count: row.count, value: row.value } }).to eq(expected_rows)
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'should support computing other statistics' do
|
282
|
+
time_range = [Time.new(2020, 5, 1), Time.new(2020, 7, 1)]
|
283
|
+
search = Sunspot.search(Post) do
|
284
|
+
with :blog_id, 1
|
285
|
+
json_facet :published_at, range: time_range, gap: 1, gap_unit: 'MONTHS', other: 'all'
|
286
|
+
end
|
287
|
+
expect(search.facet(:published_at).other_count('before')).to eq(2)
|
288
|
+
expect(search.facet(:published_at).other_count('after')).to eq(2)
|
289
|
+
expect(search.facet(:published_at).other_count('between')).to eq(3)
|
290
|
+
end
|
225
291
|
end
|
226
292
|
|
227
293
|
context 'nested json facet' do
|
@@ -237,7 +303,7 @@ describe 'search faceting' do
|
|
237
303
|
end
|
238
304
|
|
239
305
|
0.upto(9) { |i| Sunspot.index(Post.new(:title => 'zero', :author_name => "another#{i}", :blog_id => 1)) }
|
240
|
-
|
306
|
+
|
241
307
|
Sunspot.commit
|
242
308
|
end
|
243
309
|
|
@@ -459,38 +525,6 @@ describe 'search faceting' do
|
|
459
525
|
expect(search.facet(:published_at).rows.last.value).to eq((time + 60*60*24)..(time + 60*60*24*2))
|
460
526
|
expect(search.facet(:published_at).rows.last.count).to eq(1)
|
461
527
|
end
|
462
|
-
|
463
|
-
it 'json facet should return time ranges' do
|
464
|
-
days_diff = 15
|
465
|
-
time_from = Time.utc(2009, 7, 8)
|
466
|
-
time_to = Time.utc(2009, 7, 8 + days_diff)
|
467
|
-
search = Sunspot.search(Post) do
|
468
|
-
json_facet(
|
469
|
-
:published_at,
|
470
|
-
:time_range => time_from..time_to
|
471
|
-
)
|
472
|
-
end
|
473
|
-
|
474
|
-
expect(search.facet(:published_at).rows.size).to eq(days_diff)
|
475
|
-
expect(search.facet(:published_at).rows[0].count).to eq(2)
|
476
|
-
expect(search.facet(:published_at).rows[1].count).to eq(1)
|
477
|
-
end
|
478
|
-
|
479
|
-
it 'json facet should return time ranges with custom gap' do
|
480
|
-
days_diff = 10
|
481
|
-
time_from = Time.utc(2009, 7, 8)
|
482
|
-
time_to = Time.utc(2009, 7, 8 + days_diff)
|
483
|
-
search = Sunspot.search(Post) do
|
484
|
-
json_facet(
|
485
|
-
:published_at,
|
486
|
-
:time_range => time_from..time_to,
|
487
|
-
gap: 60*60*24*2
|
488
|
-
)
|
489
|
-
end
|
490
|
-
expect(search.facet(:published_at).rows.size).to eq(days_diff / 2)
|
491
|
-
expect(search.facet(:published_at).rows[0].count).to eq(3)
|
492
|
-
end
|
493
|
-
|
494
528
|
end
|
495
529
|
|
496
530
|
context 'class facets' do
|
@@ -94,6 +94,25 @@ describe "field grouping" do
|
|
94
94
|
expect(search.group(:title).groups.length).to eql(1)
|
95
95
|
expect(search.group(:title).groups.first.results).to eq([ @posts.last ])
|
96
96
|
end
|
97
|
+
|
98
|
+
context "returns a not paginated collection" do
|
99
|
+
subject do
|
100
|
+
search = Sunspot.search(Post) do
|
101
|
+
group :title do
|
102
|
+
ngroups false
|
103
|
+
end
|
104
|
+
paginate :per_page => 1, :page => 2
|
105
|
+
|
106
|
+
end
|
107
|
+
search.group(:title).groups
|
108
|
+
end
|
109
|
+
|
110
|
+
it { expect(subject.per_page).to eql(1) }
|
111
|
+
it { expect(subject.total_pages).to eql(0) }
|
112
|
+
it { expect(subject.current_page).to eql(2) }
|
113
|
+
it { expect(subject.first_page?).to be(false) }
|
114
|
+
it { expect(subject.last_page?).to be(true) }
|
115
|
+
end
|
97
116
|
|
98
117
|
context "returns a paginated collection" do
|
99
118
|
subject do
|
@@ -31,6 +31,22 @@ describe 'fields lists' do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
it 'does not raise Sunspot::UnrecognizedFieldError when listing existing text fields' do
|
35
|
+
expect do
|
36
|
+
Sunspot.search(Post) {
|
37
|
+
field_list(:body)
|
38
|
+
}
|
39
|
+
end.to_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does raise Sunspot::UnrecognizedFieldError when listing a non-existent text fields' do
|
43
|
+
expect do
|
44
|
+
Sunspot.search(Post) {
|
45
|
+
field_list(:bogus_body)
|
46
|
+
}
|
47
|
+
end.to raise_error(Sunspot::UnrecognizedFieldError)
|
48
|
+
end
|
49
|
+
|
34
50
|
it 'does not load any stored fields' do
|
35
51
|
hit = Sunspot.search(Post) { without_stored_fields }.hits.first
|
36
52
|
|
@@ -26,15 +26,30 @@ describe "geospatial search" do
|
|
26
26
|
expect(results).not_to include(@post)
|
27
27
|
end
|
28
28
|
|
29
|
+
it "filters out posts in the radius" do
|
30
|
+
results = Sunspot.search(Post) {
|
31
|
+
without(:coordinates_new).in_radius(32, -68, 1)
|
32
|
+
}.results
|
33
|
+
|
34
|
+
expect(results).not_to include(@post)
|
35
|
+
end
|
36
|
+
|
29
37
|
it "allows conjunction queries with radius" do
|
38
|
+
post = Post.new(:title => "Howdy",
|
39
|
+
:coordinates => Sunspot::Util::Coordinates.new(35, -68))
|
40
|
+
|
41
|
+
Sunspot.index!(post)
|
42
|
+
|
30
43
|
results = Sunspot.search(Post) {
|
31
44
|
any_of do
|
32
45
|
with(:coordinates_new).in_radius(32, -68, 1)
|
33
46
|
with(:coordinates_new).in_radius(35, 68, 1)
|
47
|
+
without(:coordinates_new).in_radius(35, -68, 1)
|
34
48
|
end
|
35
49
|
}.results
|
36
50
|
|
37
51
|
expect(results).to include(@post)
|
52
|
+
expect(results).not_to include(post)
|
38
53
|
end
|
39
54
|
|
40
55
|
it "allows conjunction queries with bounding box" do
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe "searching by joined fields" do
|
4
|
+
before :each do
|
5
|
+
Sunspot.remove_all!
|
6
|
+
|
7
|
+
@container1 = PhotoContainer.new(:id => 1)
|
8
|
+
@container2 = PhotoContainer.new(:id => 2).tap { |c| allow(c).to receive(:id).and_return(2) }
|
9
|
+
@container3 = PhotoContainer.new(:id => 3).tap { |c| allow(c).to receive(:id).and_return(3) }
|
10
|
+
|
11
|
+
@picture = Picture.new(:photo_container_id => @container1.id, :description => "one", :published => true)
|
12
|
+
@photo1 = Photo.new(:photo_container_id => @container1.id, :description => "two", :published => true)
|
13
|
+
@photo2 = Photo.new(:photo_container_id => @container2.id, :description => "three", :published => false)
|
14
|
+
|
15
|
+
Sunspot.index!(@container1, @container2, @photo1, @photo2, @picture)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "matches by joined fields" do
|
19
|
+
{
|
20
|
+
"one" => [],
|
21
|
+
"two" => [@container1],
|
22
|
+
"three" => [@container2]
|
23
|
+
}.each do |key, res|
|
24
|
+
results = Sunspot.search(PhotoContainer) {
|
25
|
+
fulltext(key, :fields => [:photo_description])
|
26
|
+
}.results
|
27
|
+
|
28
|
+
expect(results).to eq res
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't match by joined fields with the same name from other collections" do
|
33
|
+
{
|
34
|
+
"one" => [@container1],
|
35
|
+
"two" => [],
|
36
|
+
"three" => []
|
37
|
+
}.each do |key, res|
|
38
|
+
results = Sunspot.search(PhotoContainer) {
|
39
|
+
fulltext(key, :fields => [:picture_description])
|
40
|
+
}.results
|
41
|
+
|
42
|
+
expect(results).to eq res
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "matches by joined fields when using filter queries" do
|
47
|
+
{
|
48
|
+
:photo_published => [
|
49
|
+
[true, [@container1]],
|
50
|
+
[false, [@container2]]
|
51
|
+
],
|
52
|
+
:picture_published => [
|
53
|
+
[true, [@container1]],
|
54
|
+
[false, []]
|
55
|
+
]
|
56
|
+
}.each do |key, data|
|
57
|
+
data.each do |(value, res)|
|
58
|
+
results = Sunspot.search(PhotoContainer) { with(key, value) }.results
|
59
|
+
|
60
|
+
expect(results).to eq res
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|