gojee-sunspot 2.0.3 → 2.0.4
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.
- data/.gitignore +12 -0
- data/Gemfile +5 -0
- data/History.txt +252 -0
- data/LICENSE +18 -0
- data/Rakefile +13 -0
- data/TODO +13 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/batcher.rb +62 -0
- data/lib/sunspot/class_set.rb +23 -0
- data/lib/sunspot/composite_setup.rb +202 -0
- data/lib/sunspot/configuration.rb +53 -0
- data/lib/sunspot/data_extractor.rb +50 -0
- data/lib/sunspot/dsl/adjustable.rb +47 -0
- data/lib/sunspot/dsl/field_group.rb +57 -0
- data/lib/sunspot/dsl/field_query.rb +327 -0
- data/lib/sunspot/dsl/fields.rb +103 -0
- data/lib/sunspot/dsl/fulltext.rb +243 -0
- data/lib/sunspot/dsl/function.rb +27 -0
- data/lib/sunspot/dsl/functional.rb +44 -0
- data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
- data/lib/sunspot/dsl/paginatable.rb +32 -0
- data/lib/sunspot/dsl/query_facet.rb +36 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
- data/lib/sunspot/dsl/scope.rb +217 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/dsl/standard_query.rb +123 -0
- data/lib/sunspot/dsl.rb +5 -0
- data/lib/sunspot/field.rb +193 -0
- data/lib/sunspot/field_factory.rb +129 -0
- data/lib/sunspot/indexer.rb +136 -0
- data/lib/sunspot/query/abstract_field_facet.rb +52 -0
- data/lib/sunspot/query/bbox.rb +15 -0
- data/lib/sunspot/query/boost_query.rb +24 -0
- data/lib/sunspot/query/common_query.rb +96 -0
- data/lib/sunspot/query/composite_fulltext.rb +36 -0
- data/lib/sunspot/query/connective.rb +206 -0
- data/lib/sunspot/query/date_field_facet.rb +14 -0
- data/lib/sunspot/query/dismax.rb +132 -0
- data/lib/sunspot/query/field_facet.rb +41 -0
- data/lib/sunspot/query/field_group.rb +36 -0
- data/lib/sunspot/query/filter.rb +38 -0
- data/lib/sunspot/query/function_query.rb +52 -0
- data/lib/sunspot/query/geo.rb +53 -0
- data/lib/sunspot/query/geofilt.rb +16 -0
- data/lib/sunspot/query/highlighting.rb +62 -0
- data/lib/sunspot/query/more_like_this.rb +61 -0
- data/lib/sunspot/query/more_like_this_query.rb +12 -0
- data/lib/sunspot/query/pagination.rb +42 -0
- data/lib/sunspot/query/query_facet.rb +16 -0
- data/lib/sunspot/query/restriction.rb +262 -0
- data/lib/sunspot/query/scope.rb +9 -0
- data/lib/sunspot/query/sort.rb +109 -0
- data/lib/sunspot/query/sort_composite.rb +34 -0
- data/lib/sunspot/query/standard_query.rb +16 -0
- data/lib/sunspot/query/text_field_boost.rb +17 -0
- data/lib/sunspot/query.rb +11 -0
- data/lib/sunspot/schema.rb +151 -0
- data/lib/sunspot/search/abstract_search.rb +281 -0
- data/lib/sunspot/search/date_facet.rb +35 -0
- data/lib/sunspot/search/facet_row.rb +27 -0
- data/lib/sunspot/search/field_facet.rb +88 -0
- data/lib/sunspot/search/field_group.rb +32 -0
- data/lib/sunspot/search/group.rb +50 -0
- data/lib/sunspot/search/highlight.rb +38 -0
- data/lib/sunspot/search/hit.rb +150 -0
- data/lib/sunspot/search/hit_enumerable.rb +72 -0
- data/lib/sunspot/search/more_like_this_search.rb +31 -0
- data/lib/sunspot/search/paginated_collection.rb +57 -0
- data/lib/sunspot/search/query_facet.rb +67 -0
- data/lib/sunspot/search/standard_search.rb +21 -0
- data/lib/sunspot/search.rb +9 -0
- data/lib/sunspot/session.rb +262 -0
- data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
- data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
- data/lib/sunspot/session_proxy.rb +95 -0
- data/lib/sunspot/setup.rb +350 -0
- data/lib/sunspot/text_field_setup.rb +29 -0
- data/lib/sunspot/type.rb +393 -0
- data/lib/sunspot/util.rb +252 -0
- data/lib/sunspot/version.rb +3 -0
- data/lib/sunspot.rb +579 -0
- data/log/.gitignore +1 -0
- data/pkg/.gitignore +1 -0
- data/script/console +10 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/batcher_spec.rb +112 -0
- data/spec/api/binding_spec.rb +50 -0
- data/spec/api/class_set_spec.rb +24 -0
- data/spec/api/hit_enumerable_spec.rb +47 -0
- data/spec/api/indexer/attributes_spec.rb +149 -0
- data/spec/api/indexer/batch_spec.rb +72 -0
- data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
- data/spec/api/indexer/fixed_fields_spec.rb +57 -0
- data/spec/api/indexer/fulltext_spec.rb +43 -0
- data/spec/api/indexer/removal_spec.rb +53 -0
- data/spec/api/indexer/spec_helper.rb +1 -0
- data/spec/api/indexer_spec.rb +14 -0
- data/spec/api/query/advanced_manipulation_examples.rb +35 -0
- data/spec/api/query/connectives_examples.rb +189 -0
- data/spec/api/query/dsl_spec.rb +18 -0
- data/spec/api/query/dynamic_fields_examples.rb +165 -0
- data/spec/api/query/faceting_examples.rb +397 -0
- data/spec/api/query/fulltext_examples.rb +313 -0
- data/spec/api/query/function_spec.rb +79 -0
- data/spec/api/query/geo_examples.rb +68 -0
- data/spec/api/query/group_spec.rb +32 -0
- data/spec/api/query/highlighting_examples.rb +245 -0
- data/spec/api/query/more_like_this_spec.rb +140 -0
- data/spec/api/query/ordering_pagination_examples.rb +116 -0
- data/spec/api/query/scope_examples.rb +275 -0
- data/spec/api/query/spatial_examples.rb +27 -0
- data/spec/api/query/spec_helper.rb +1 -0
- data/spec/api/query/standard_spec.rb +29 -0
- data/spec/api/query/text_field_scoping_examples.rb +30 -0
- data/spec/api/query/types_spec.rb +20 -0
- data/spec/api/search/dynamic_fields_spec.rb +33 -0
- data/spec/api/search/faceting_spec.rb +360 -0
- data/spec/api/search/highlighting_spec.rb +69 -0
- data/spec/api/search/hits_spec.rb +131 -0
- data/spec/api/search/paginated_collection_spec.rb +36 -0
- data/spec/api/search/results_spec.rb +72 -0
- data/spec/api/search/search_spec.rb +23 -0
- data/spec/api/search/spec_helper.rb +1 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
- data/spec/api/session_proxy/spec_helper.rb +9 -0
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
- data/spec/api/session_spec.rb +232 -0
- data/spec/api/spec_helper.rb +3 -0
- data/spec/api/sunspot_spec.rb +29 -0
- data/spec/ext.rb +11 -0
- data/spec/helpers/indexer_helper.rb +17 -0
- data/spec/helpers/integration_helper.rb +8 -0
- data/spec/helpers/mock_session_helper.rb +13 -0
- data/spec/helpers/query_helper.rb +26 -0
- data/spec/helpers/search_helper.rb +68 -0
- data/spec/integration/dynamic_fields_spec.rb +57 -0
- data/spec/integration/faceting_spec.rb +251 -0
- data/spec/integration/field_grouping_spec.rb +66 -0
- data/spec/integration/geospatial_spec.rb +85 -0
- data/spec/integration/highlighting_spec.rb +44 -0
- data/spec/integration/indexing_spec.rb +55 -0
- data/spec/integration/keyword_search_spec.rb +317 -0
- data/spec/integration/local_search_spec.rb +64 -0
- data/spec/integration/more_like_this_spec.rb +43 -0
- data/spec/integration/scoped_search_spec.rb +354 -0
- data/spec/integration/stored_fields_spec.rb +12 -0
- data/spec/integration/test_pagination.rb +43 -0
- data/spec/integration/unicode_spec.rb +15 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +21 -0
- data/spec/mocks/connection.rb +126 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
- data/spec/mocks/mock_record.rb +52 -0
- data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
- data/spec/mocks/photo.rb +11 -0
- data/spec/mocks/post.rb +86 -0
- data/spec/mocks/super_class.rb +2 -0
- data/spec/mocks/user.rb +13 -0
- data/spec/spec_helper.rb +40 -0
- data/sunspot.gemspec +42 -0
- data/tasks/rdoc.rake +27 -0
- data/tasks/schema.rake +19 -0
- data/tasks/todo.rake +4 -0
- metadata +261 -3
@@ -0,0 +1,397 @@
|
|
1
|
+
shared_examples_for "facetable query" do
|
2
|
+
describe 'on fields' do
|
3
|
+
it 'does not turn faceting on if no facet requested' do
|
4
|
+
search
|
5
|
+
connection.should_not have_last_search_with('facet')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'turns faceting on if facet is requested' do
|
9
|
+
search do
|
10
|
+
facet :category_ids
|
11
|
+
end
|
12
|
+
connection.should have_last_search_with(:facet => 'true')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'requests single field facet' do
|
16
|
+
search do
|
17
|
+
facet :category_ids
|
18
|
+
end
|
19
|
+
connection.should have_last_search_with(:"facet.field" => %w(category_ids_im))
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'requests multiple field facets' do
|
23
|
+
search do
|
24
|
+
facet :category_ids, :blog_id
|
25
|
+
end
|
26
|
+
connection.should have_last_search_with(:"facet.field" => %w(category_ids_im blog_id_i))
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sets facet sort by count' do
|
30
|
+
search do
|
31
|
+
facet :category_ids, :sort => :count
|
32
|
+
end
|
33
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.sort" => 'true')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sets facet sort by index' do
|
37
|
+
search do
|
38
|
+
facet :category_ids, :sort => :index
|
39
|
+
end
|
40
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.sort" => 'false')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises ArgumentError if bogus facet sort provided' do
|
44
|
+
lambda do
|
45
|
+
search do
|
46
|
+
facet :category_ids, :sort => :sideways
|
47
|
+
end
|
48
|
+
end.should raise_error(ArgumentError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'sets the facet limit' do
|
52
|
+
search do
|
53
|
+
facet :category_ids, :limit => 10
|
54
|
+
end
|
55
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.limit" => 10)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets the facet minimum count' do
|
59
|
+
search do
|
60
|
+
facet :category_ids, :minimum_count => 5
|
61
|
+
end
|
62
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 5)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'sets the facet minimum count to zero if zeros are allowed' do
|
66
|
+
search do
|
67
|
+
facet :category_ids, :zeros => true
|
68
|
+
end
|
69
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 0)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'sets the facet minimum count to one by default' do
|
73
|
+
search do
|
74
|
+
facet :category_ids
|
75
|
+
end
|
76
|
+
connection.should have_last_search_with(:"f.category_ids_im.facet.mincount" => 1)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'sets the facet prefix' do
|
80
|
+
search do
|
81
|
+
facet :title, :prefix => 'Test'
|
82
|
+
end
|
83
|
+
connection.should have_last_search_with(:"f.title_ss.facet.prefix" => 'Test')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'escapes the facet prefix' do
|
87
|
+
search do
|
88
|
+
facet :title, :prefix => 'Test Title'
|
89
|
+
end
|
90
|
+
connection.should have_last_search_with(:"f.title_ss.facet.prefix" => 'Test\ Title')
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'sends a query facet for :any extra' do
|
94
|
+
search do
|
95
|
+
facet :category_ids, :extra => :any
|
96
|
+
end
|
97
|
+
connection.should have_last_search_with(:"facet.query" => "category_ids_im:[* TO *]")
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'sends a query facet for :none extra' do
|
101
|
+
search do
|
102
|
+
facet :category_ids, :extra => :none
|
103
|
+
end
|
104
|
+
connection.should have_last_search_with(:"facet.query" => "-category_ids_im:[* TO *]")
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'raises an ArgumentError if bogus extra is passed' do
|
108
|
+
lambda do
|
109
|
+
search do
|
110
|
+
facet :category_ids, :extra => :bogus
|
111
|
+
end
|
112
|
+
end.should raise_error(ArgumentError)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'tags and excludes a scope filter in a field facet' do
|
116
|
+
search do
|
117
|
+
blog_filter = with(:blog_id, 1)
|
118
|
+
facet(:blog_id, :exclude => blog_filter)
|
119
|
+
end
|
120
|
+
filter_tag = get_filter_tag('blog_id_i:1')
|
121
|
+
connection.should have_last_search_with(
|
122
|
+
:"facet.field" => %W({!ex=#{filter_tag}}blog_id_i)
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'tags and excludes a disjunction filter in a field facet' do
|
127
|
+
search do
|
128
|
+
blog_filter = any_of do
|
129
|
+
with(:blog_id, 1)
|
130
|
+
with(:blog_id, 2)
|
131
|
+
end
|
132
|
+
facet(:blog_id, :exclude => blog_filter)
|
133
|
+
end
|
134
|
+
filter_tag = get_filter_tag('(blog_id_i:1 OR blog_id_i:2)')
|
135
|
+
connection.should have_last_search_with(
|
136
|
+
:"facet.field" => %W({!ex=#{filter_tag}}blog_id_i)
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'tags and excludes multiple filters in a field facet' do
|
141
|
+
search do
|
142
|
+
blog_filter = with(:blog_id, 1)
|
143
|
+
category_filter = with(:category_ids, 2)
|
144
|
+
facet(:blog_id, :exclude => [blog_filter, category_filter])
|
145
|
+
end
|
146
|
+
filter_tags = %w(blog_id_i:1 category_ids_im:2).map do |phrase|
|
147
|
+
get_filter_tag(phrase)
|
148
|
+
end.join(',')
|
149
|
+
connection.should have_last_search_with(
|
150
|
+
:"facet.field" => %W({!ex=#{filter_tags}}blog_id_i)
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'does not tag a filter if it is not excluded' do
|
155
|
+
search do
|
156
|
+
with(:blog_id, 1)
|
157
|
+
end
|
158
|
+
connection.should have_last_search_including(:fq, "blog_id_i:1")
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'names a field facet' do
|
162
|
+
search do
|
163
|
+
facet(:blog_id, :name => :blog)
|
164
|
+
end
|
165
|
+
connection.should have_last_search_including(:"facet.field", "{!key=blog}blog_id_i")
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'uses the custom field facet name in facet option parameters' do
|
169
|
+
search do
|
170
|
+
facet(:blog_id, :name => :blog, :sort => :count)
|
171
|
+
end
|
172
|
+
connection.should have_last_search_with(:"f.blog.facet.sort" => 'true')
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'raises an ArgumentError if exclusion attempted on a query facet' do
|
176
|
+
lambda do
|
177
|
+
search do
|
178
|
+
blog_filter = with(:blog_id, 1)
|
179
|
+
facet(:bad, :exclude => blog_filter) do
|
180
|
+
row(:bogus) { with(:blog_id, 1) }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end.should raise_error(ArgumentError)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'raises an ArgumentError if exclusion attempted on a restricted field facet' do
|
187
|
+
lambda do
|
188
|
+
search do
|
189
|
+
blog_filter = with(:blog_id, 1)
|
190
|
+
facet(:blog_id, :only => 1, :exclude => blog_filter)
|
191
|
+
end
|
192
|
+
end.should raise_error(ArgumentError)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'raises an ArgumentError if exclusion attempted on a facet with :extra' do
|
196
|
+
lambda do
|
197
|
+
search do
|
198
|
+
blog_filter = with(:blog_id, 1)
|
199
|
+
facet(:blog_id, :extra => :all, :exclude => blog_filter)
|
200
|
+
end
|
201
|
+
end.should raise_error(ArgumentError)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe 'on time ranges' do
|
206
|
+
before :each do
|
207
|
+
@time_range = (Time.parse('2009-06-01 00:00:00 -0400')..
|
208
|
+
Time.parse('2009-07-01 00:00:00 -0400'))
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'does not send date facet parameters if time range is not specified' do
|
212
|
+
search do |query|
|
213
|
+
query.facet :published_at
|
214
|
+
end
|
215
|
+
connection.should_not have_last_search_with(:"facet.date")
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'sets the facet to a date facet if time range is specified' do
|
219
|
+
search do |query|
|
220
|
+
query.facet :published_at, :time_range => @time_range
|
221
|
+
end
|
222
|
+
connection.should have_last_search_with(:"facet.date" => ['published_at_dt'])
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'sets the facet start and end' do
|
226
|
+
search do |query|
|
227
|
+
query.facet :published_at, :time_range => @time_range
|
228
|
+
end
|
229
|
+
connection.should have_last_search_with(
|
230
|
+
:"f.published_at_dt.facet.date.start" => '2009-06-01T04:00:00Z',
|
231
|
+
:"f.published_at_dt.facet.date.end" => '2009-07-01T04:00:00Z'
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'defaults the time interval to 1 day' do
|
236
|
+
search do |query|
|
237
|
+
query.facet :published_at, :time_range => @time_range
|
238
|
+
end
|
239
|
+
connection.should have_last_search_with(:"f.published_at_dt.facet.date.gap" => "+86400SECONDS")
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'uses custom time interval' do
|
243
|
+
search do |query|
|
244
|
+
query.facet :published_at, :time_range => @time_range, :time_interval => 3600
|
245
|
+
end
|
246
|
+
connection.should have_last_search_with(:"f.published_at_dt.facet.date.gap" => "+3600SECONDS")
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'does not allow date faceting on a non-date field' do
|
250
|
+
lambda do
|
251
|
+
search do |query|
|
252
|
+
query.facet :blog_id, :time_range => @time_range
|
253
|
+
end
|
254
|
+
end.should raise_error(ArgumentError)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe 'using queries' do
|
259
|
+
it 'turns faceting on' do
|
260
|
+
search do
|
261
|
+
facet :foo do
|
262
|
+
row :bar do
|
263
|
+
with(:average_rating).between(4.0..5.0)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
connection.should have_last_search_with(:facet => 'true')
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'facets by query' do
|
271
|
+
search do
|
272
|
+
facet :foo do
|
273
|
+
row :bar do
|
274
|
+
with(:average_rating).between(4.0..5.0)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
connection.should have_last_search_with(:"facet.query" => 'average_rating_ft:[4\.0 TO 5\.0]')
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'requests multiple query facets' do
|
282
|
+
search do
|
283
|
+
facet :foo do
|
284
|
+
row :bar do
|
285
|
+
with(:average_rating).between(3.0..4.0)
|
286
|
+
end
|
287
|
+
row :baz do
|
288
|
+
with(:average_rating).between(4.0..5.0)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
connection.should have_last_search_with(
|
293
|
+
:"facet.query" => [
|
294
|
+
'average_rating_ft:[3\.0 TO 4\.0]',
|
295
|
+
'average_rating_ft:[4\.0 TO 5\.0]'
|
296
|
+
]
|
297
|
+
)
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'requests query facet with multiple conditions' do
|
301
|
+
search do
|
302
|
+
facet :foo do
|
303
|
+
row :bar do
|
304
|
+
with(:category_ids, 1)
|
305
|
+
with(:blog_id, 2)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
connection.should have_last_search_with(
|
310
|
+
:"facet.query" => '(category_ids_im:1 AND blog_id_i:2)'
|
311
|
+
)
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'requests query facet with disjunction' do
|
315
|
+
search do
|
316
|
+
facet :foo do
|
317
|
+
row :bar do
|
318
|
+
any_of do
|
319
|
+
with(:category_ids, 1)
|
320
|
+
with(:blog_id, 2)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
connection.should have_last_search_with(
|
326
|
+
:"facet.query" => '(category_ids_im:1 OR blog_id_i:2)'
|
327
|
+
)
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'builds query facets when passed :only argument to field facet declaration' do
|
331
|
+
search do
|
332
|
+
facet :category_ids, :only => [1, 3]
|
333
|
+
end
|
334
|
+
connection.should have_last_search_with(
|
335
|
+
:"facet.query" => ['category_ids_im:1', 'category_ids_im:3']
|
336
|
+
)
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'converts limited query facet values to the correct type' do
|
340
|
+
search do
|
341
|
+
facet :published_at, :only => [Time.utc(2009, 8, 28, 15, 33), Time.utc(2008,8, 28, 15, 33)]
|
342
|
+
end
|
343
|
+
connection.should have_last_search_with(
|
344
|
+
:"facet.query" => [
|
345
|
+
'published_at_dt:2009\-08\-28T15\:33\:00Z',
|
346
|
+
'published_at_dt:2008\-08\-28T15\:33\:00Z'
|
347
|
+
]
|
348
|
+
)
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'ignores facet query with no rows' do
|
352
|
+
search do
|
353
|
+
facet(:foo) {}
|
354
|
+
end
|
355
|
+
connection.should_not have_last_search_with(:"facet.query")
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'ignores facet query row with no restrictions' do
|
359
|
+
search do
|
360
|
+
facet :foo do
|
361
|
+
row(:bar) do
|
362
|
+
with(:blog_id, 1)
|
363
|
+
end
|
364
|
+
row(:baz) {}
|
365
|
+
end
|
366
|
+
end
|
367
|
+
connection.searches.last[:"facet.query"].should be_a(String)
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'ignores facet query with only empty rows' do
|
371
|
+
search do
|
372
|
+
facet :foo do
|
373
|
+
row(:bar) {}
|
374
|
+
end
|
375
|
+
end
|
376
|
+
connection.should_not have_last_search_with(:"facet.query")
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'does not allow 0 arguments to facet method with block' do
|
380
|
+
lambda do
|
381
|
+
search do
|
382
|
+
facet do
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end.should raise_error(ArgumentError)
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'does not allow more than 1 argument to facet method with block' do
|
389
|
+
lambda do
|
390
|
+
search do
|
391
|
+
facet :foo, :bar do
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end.should raise_error(ArgumentError)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
shared_examples_for 'fulltext query' do
|
2
|
+
it 'searches by keywords' do
|
3
|
+
search do
|
4
|
+
keywords 'keyword search'
|
5
|
+
end
|
6
|
+
connection.should have_last_search_with(:q => 'keyword search')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'ignores keywords if empty' do
|
10
|
+
search do
|
11
|
+
keywords ''
|
12
|
+
end
|
13
|
+
connection.should_not have_last_search_with(:defType => 'dismax')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'ignores keywords if nil' do
|
17
|
+
search do
|
18
|
+
keywords nil
|
19
|
+
end
|
20
|
+
connection.should_not have_last_search_with(:defType => 'dismax')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'ignores keywords with only whitespace' do
|
24
|
+
search do
|
25
|
+
keywords " \t"
|
26
|
+
end
|
27
|
+
connection.should_not have_last_search_with(:defType => 'dismax')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'gracefully ignores keywords block if keywords ignored' do
|
31
|
+
search do
|
32
|
+
keywords(nil) { fields(:title) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sets default query parser to dismax when keywords used' do
|
37
|
+
search do
|
38
|
+
keywords 'keyword search'
|
39
|
+
end
|
40
|
+
connection.should have_last_search_with(:defType => 'dismax')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'searches types in filter query if keywords used' do
|
44
|
+
search do
|
45
|
+
keywords 'keyword search'
|
46
|
+
end
|
47
|
+
connection.should have_last_search_with(:fq => ['type:Post'])
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'with multiple keyword components' do
|
51
|
+
before :each do
|
52
|
+
session.search Post do
|
53
|
+
keywords 'first search', :fields => :title
|
54
|
+
keywords 'second search'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'puts specified keywords in subquery' do
|
59
|
+
subqueries(:q).map { |subquery| subquery[:v] }.should ==
|
60
|
+
['first search', 'second search']
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'puts specified dismax parameters in subquery' do
|
64
|
+
subqueries(:q).first[:qf].should == 'title_text'
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'puts default dismax parameters in subquery' do
|
68
|
+
subqueries(:q).last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'puts field list in main query' do
|
72
|
+
connection.should have_last_search_with(:fl => '* score')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'searches all text fields for searched class' do
|
77
|
+
search = search do
|
78
|
+
keywords 'keyword search'
|
79
|
+
end
|
80
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'searches both stored and unstored text fields' do
|
84
|
+
search Post, Namespaced::Comment do
|
85
|
+
keywords 'keyword search'
|
86
|
+
end
|
87
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(author_name_text backwards_title_text body_text body_textsv tags_textv title_text)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'searches only specified text fields when specified' do
|
91
|
+
search do
|
92
|
+
keywords 'keyword search', :fields => [:title, :body]
|
93
|
+
end
|
94
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv title_text)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'excludes text fields when instructed' do
|
98
|
+
search do
|
99
|
+
keywords 'keyword search' do
|
100
|
+
exclude_fields :backwards_title, :body_mlt
|
101
|
+
end
|
102
|
+
end
|
103
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv tags_textv title_text)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'assigns boost to fields when specified' do
|
107
|
+
search do
|
108
|
+
keywords 'keyword search' do
|
109
|
+
fields :title => 2.0, :body => 0.75
|
110
|
+
end
|
111
|
+
end
|
112
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(body_textsv^0.75 title_text^2.0)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'allows assignment of boosted and unboosted fields' do
|
116
|
+
search do
|
117
|
+
keywords 'keyword search' do
|
118
|
+
fields :body, :title => 2.0
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'searches both unstored and stored text field with same name when specified' do
|
124
|
+
search Post, Namespaced::Comment do
|
125
|
+
keywords 'keyword search', :fields => [:body]
|
126
|
+
end
|
127
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(body_text body_textsv)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'requests score when keywords used' do
|
131
|
+
search do
|
132
|
+
keywords 'keyword search'
|
133
|
+
end
|
134
|
+
connection.should have_last_search_with(:fl => '* score')
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'does not request score when keywords not used' do
|
138
|
+
search Post
|
139
|
+
connection.should_not have_last_search_with(:fl)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'sets phrase fields' do
|
143
|
+
search do
|
144
|
+
keywords 'great pizza' do
|
145
|
+
phrase_fields :title => 2.0
|
146
|
+
end
|
147
|
+
end
|
148
|
+
connection.should have_last_search_with(:pf => 'title_text^2.0')
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'sets phrase fields with boost' do
|
152
|
+
search do
|
153
|
+
keywords 'great pizza' do
|
154
|
+
phrase_fields :title => 1.5
|
155
|
+
end
|
156
|
+
end
|
157
|
+
connection.should have_last_search_with(:pf => 'title_text^1.5')
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'sets phrase slop from DSL' do
|
161
|
+
search do
|
162
|
+
keywords 'great pizza' do
|
163
|
+
phrase_slop 2
|
164
|
+
end
|
165
|
+
end
|
166
|
+
connection.should have_last_search_with(:ps => 2)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'sets boost for certain fields without restricting fields' do
|
170
|
+
search do
|
171
|
+
keywords 'great pizza' do
|
172
|
+
boost_fields :title => 1.5
|
173
|
+
end
|
174
|
+
end
|
175
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text^1.5)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'ignores boost fields that do not apply' do
|
179
|
+
search do
|
180
|
+
keywords 'great pizza' do
|
181
|
+
boost_fields :bogus => 1.2, :title => 1.5
|
182
|
+
end
|
183
|
+
end
|
184
|
+
connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_textsv tags_textv title_text^1.5)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'sets default boost with default fields' do
|
188
|
+
search Photo do
|
189
|
+
keywords 'great pizza'
|
190
|
+
end
|
191
|
+
connection.should have_last_search_with(:qf => 'caption_text^1.5')
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'sets default boost with fields specified in options' do
|
195
|
+
search Photo do
|
196
|
+
keywords 'great pizza', :fields => [:caption]
|
197
|
+
end
|
198
|
+
connection.should have_last_search_with(:qf => 'caption_text^1.5')
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'sets default boost with fields specified in DSL' do
|
202
|
+
search Photo do
|
203
|
+
keywords 'great pizza' do
|
204
|
+
fields :caption
|
205
|
+
end
|
206
|
+
end
|
207
|
+
connection.should have_last_search_with(:qf => 'caption_text^1.5')
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'overrides default boost when specified in DSL' do
|
211
|
+
search Photo do
|
212
|
+
keywords 'great pizza' do
|
213
|
+
fields :caption => 2.0
|
214
|
+
end
|
215
|
+
end
|
216
|
+
connection.should have_last_search_with(:qf => 'caption_text^2.0')
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'creates boost query' do
|
220
|
+
search do
|
221
|
+
keywords 'great pizza' do
|
222
|
+
boost 2.0 do
|
223
|
+
with(:average_rating).greater_than(2.0)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
connection.should have_last_search_with(:bq => ['average_rating_ft:[2\.0 TO *]^2.0'])
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'creates multiple boost queries' do
|
231
|
+
search do
|
232
|
+
keywords 'great pizza' do
|
233
|
+
boost(2.0) do
|
234
|
+
with(:average_rating).greater_than(2.0)
|
235
|
+
end
|
236
|
+
boost(1.5) do
|
237
|
+
with(:featured, true)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
connection.should have_last_search_with(
|
242
|
+
:bq => [
|
243
|
+
'average_rating_ft:[2\.0 TO *]^2.0',
|
244
|
+
'featured_bs:true^1.5'
|
245
|
+
]
|
246
|
+
)
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'sends minimum match parameter from options' do
|
250
|
+
search do
|
251
|
+
keywords 'great pizza', :minimum_match => 2
|
252
|
+
end
|
253
|
+
connection.should have_last_search_with(:mm => 2)
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'sends minimum match parameter from DSL' do
|
257
|
+
search do
|
258
|
+
keywords('great pizza') { minimum_match(2) }
|
259
|
+
end
|
260
|
+
connection.should have_last_search_with(:mm => 2)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'sends tiebreaker parameter from options' do
|
264
|
+
search do
|
265
|
+
keywords 'great pizza', :tie => 0.1
|
266
|
+
end
|
267
|
+
connection.should have_last_search_with(:tie => 0.1)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'sends tiebreaker parameter from DSL' do
|
271
|
+
search do
|
272
|
+
keywords('great pizza') { tie(0.1) }
|
273
|
+
end
|
274
|
+
connection.should have_last_search_with(:tie => 0.1)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'sends query phrase slop from options' do
|
278
|
+
search do
|
279
|
+
keywords 'great pizza', :query_phrase_slop => 2
|
280
|
+
end
|
281
|
+
connection.should have_last_search_with(:qs => 2)
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'sends query phrase slop from DSL' do
|
285
|
+
search do
|
286
|
+
keywords('great pizza') { query_phrase_slop(2) }
|
287
|
+
end
|
288
|
+
connection.should have_last_search_with(:qs => 2)
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'allows specification of a text field that only exists in one type' do
|
292
|
+
search Post, Namespaced::Comment do
|
293
|
+
keywords 'keywords', :fields => :author_name
|
294
|
+
end
|
295
|
+
connection.searches.last[:qf].should == 'author_name_text'
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'raises Sunspot::UnrecognizedFieldError for nonexistant fields in keywords' do
|
299
|
+
lambda do
|
300
|
+
search do
|
301
|
+
keywords :text, :fields => :bogus
|
302
|
+
end
|
303
|
+
end.should raise_error(Sunspot::UnrecognizedFieldError)
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'raises Sunspot::UnrecognizedFieldError if a text field that does not exist for any type is specified' do
|
307
|
+
lambda do
|
308
|
+
search Post, Namespaced::Comment do
|
309
|
+
keywords 'fulltext', :fields => :bogus
|
310
|
+
end
|
311
|
+
end.should raise_error(Sunspot::UnrecognizedFieldError)
|
312
|
+
end
|
313
|
+
end
|