sunspot 0.9.8 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +32 -0
- data/README.rdoc +40 -3
- data/TODO +10 -8
- data/VERSION.yml +2 -2
- data/bin/sunspot-configure-solr +22 -28
- data/bin/sunspot-solr +50 -29
- data/lib/sunspot/adapters.rb +1 -1
- data/lib/sunspot/composite_setup.rb +13 -15
- data/lib/sunspot/configuration.rb +14 -0
- data/lib/sunspot/data_extractor.rb +3 -0
- data/lib/sunspot/dsl/field_query.rb +33 -6
- data/lib/sunspot/dsl/fields.rb +14 -1
- data/lib/sunspot/dsl/fulltext.rb +168 -0
- data/lib/sunspot/dsl/query.rb +82 -5
- data/lib/sunspot/dsl/query_facet.rb +3 -3
- data/lib/sunspot/dsl/restriction.rb +7 -7
- data/lib/sunspot/dsl/scope.rb +17 -10
- data/lib/sunspot/dsl/search.rb +2 -2
- data/lib/sunspot/dsl.rb +2 -1
- data/lib/sunspot/facet.rb +9 -1
- data/lib/sunspot/facet_data.rb +56 -7
- data/lib/sunspot/facet_row.rb +2 -0
- data/lib/sunspot/field.rb +50 -26
- data/lib/sunspot/field_factory.rb +15 -0
- data/lib/sunspot/indexer.rb +6 -0
- data/lib/sunspot/instantiated_facet.rb +6 -9
- data/lib/sunspot/instantiated_facet_row.rb +7 -2
- data/lib/sunspot/query/boost_query.rb +20 -0
- data/lib/sunspot/query/connective.rb +98 -35
- data/lib/sunspot/query/dismax.rb +69 -0
- data/lib/sunspot/query/field_facet.rb +1 -22
- data/lib/sunspot/query/fulltext_base_query.rb +47 -0
- data/lib/sunspot/query/highlighting.rb +43 -0
- data/lib/sunspot/query/local.rb +24 -0
- data/lib/sunspot/query/pagination.rb +3 -4
- data/lib/sunspot/query/query.rb +93 -0
- data/lib/sunspot/query/query_facet.rb +14 -9
- data/lib/sunspot/query/query_facet_row.rb +3 -3
- data/lib/sunspot/query/query_field_facet.rb +10 -3
- data/lib/sunspot/query/restriction.rb +36 -15
- data/lib/sunspot/query/scope.rb +3 -159
- data/lib/sunspot/query/sort.rb +84 -15
- data/lib/sunspot/query/text_field_boost.rb +15 -0
- data/lib/sunspot/query.rb +2 -188
- data/lib/sunspot/schema.rb +7 -25
- data/lib/sunspot/search/highlight.rb +38 -0
- data/lib/sunspot/search/hit.rb +50 -3
- data/lib/sunspot/search.rb +51 -32
- data/lib/sunspot/session.rb +32 -12
- data/lib/sunspot/setup.rb +47 -10
- data/lib/sunspot/text_field_setup.rb +29 -0
- data/lib/sunspot/type.rb +4 -4
- data/lib/sunspot/util.rb +27 -1
- data/lib/sunspot.rb +8 -17
- data/solr/solr/conf/schema.xml +54 -40
- data/solr/solr/conf/solrconfig.xml +30 -0
- data/solr/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
- data/solr/solr/lib/gt2-referencing-2.3.1.jar +0 -0
- data/solr/solr/lib/jsr108-0.01.jar +0 -0
- data/solr/solr/lib/locallucene.jar +0 -0
- data/solr/solr/lib/localsolr.jar +0 -0
- data/spec/api/indexer/attributes_spec.rb +100 -0
- data/spec/api/indexer/batch_spec.rb +46 -0
- data/spec/api/indexer/dynamic_fields_spec.rb +33 -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 +46 -0
- data/spec/api/indexer/spec_helper.rb +1 -0
- data/spec/api/indexer_spec.rb +1 -308
- data/spec/api/query/connectives_spec.rb +162 -0
- data/spec/api/query/dsl_spec.rb +12 -0
- data/spec/api/query/dynamic_fields_spec.rb +149 -0
- data/spec/api/query/faceting_spec.rb +272 -0
- data/spec/api/query/fulltext_spec.rb +193 -0
- data/spec/api/query/highlighting_spec.rb +138 -0
- data/spec/api/query/local_spec.rb +54 -0
- data/spec/api/query/ordering_pagination_spec.rb +95 -0
- data/spec/api/query/scope_spec.rb +266 -0
- data/spec/api/query/spec_helper.rb +1 -0
- data/spec/api/query/text_field_scoping_spec.rb +30 -0
- data/spec/api/query/types_spec.rb +20 -0
- data/spec/api/search/dynamic_fields_spec.rb +27 -0
- data/spec/api/search/faceting_spec.rb +206 -0
- data/spec/api/search/highlighting_spec.rb +65 -0
- data/spec/api/search/hits_spec.rb +62 -0
- data/spec/api/search/results_spec.rb +52 -0
- data/spec/api/search/search_spec.rb +23 -0
- data/spec/api/search/spec_helper.rb +1 -0
- data/spec/api/spec_helper.rb +1 -1
- data/spec/helpers/indexer_helper.rb +29 -0
- data/spec/helpers/query_helper.rb +13 -0
- data/spec/helpers/search_helper.rb +78 -0
- data/spec/integration/faceting_spec.rb +1 -1
- data/spec/integration/highlighting_spec.rb +22 -0
- data/spec/integration/keyword_search_spec.rb +65 -0
- data/spec/integration/local_search_spec.rb +56 -0
- data/spec/integration/scoped_search_spec.rb +15 -1
- data/spec/integration/spec_helper.rb +3 -3
- data/spec/mocks/connection.rb +14 -1
- data/spec/mocks/photo.rb +1 -1
- data/spec/mocks/post.rb +5 -3
- data/spec/mocks/super_class.rb +2 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/gemspec.rake +18 -7
- data/tasks/schema.rake +1 -1
- data/tasks/spec.rake +1 -1
- data/templates/schema.xml.erb +36 -0
- metadata +117 -48
- data/lib/sunspot/query/base_query.rb +0 -90
- data/lib/sunspot/query/dynamic_query.rb +0 -69
- data/lib/sunspot/query/field_query.rb +0 -63
- data/spec/api/build_search_spec.rb +0 -1017
- data/spec/api/query_spec.rb +0 -153
- data/spec/api/search_retrieval_spec.rb +0 -362
- data/templates/schema.xml.haml +0 -24
data/lib/sunspot/setup.rb
CHANGED
@@ -57,6 +57,20 @@ module Sunspot
|
|
57
57
|
@dynamic_field_factories_cache[field_factory.name] = field_factory
|
58
58
|
end
|
59
59
|
|
60
|
+
#
|
61
|
+
# The coordinates field factory is used for populating the coordinate fields
|
62
|
+
# of documents during index, but does not actually generate fields (since
|
63
|
+
# the field names used in search are static).
|
64
|
+
#
|
65
|
+
def set_coordinates_field(name)
|
66
|
+
@coordinates_field_factory = FieldFactory::Coordinates.new(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Add a document boost to documents at index time. Document boost can be
|
71
|
+
# static (the same for all documents of this class), or extracted on a per-
|
72
|
+
# document basis using either attribute or block extraction as per usual.
|
73
|
+
#
|
60
74
|
def add_document_boost(attr_name, &block)
|
61
75
|
@document_boost_extractor =
|
62
76
|
if attr_name
|
@@ -77,6 +91,9 @@ module Sunspot
|
|
77
91
|
@dsl.instance_eval(&block)
|
78
92
|
end
|
79
93
|
|
94
|
+
#
|
95
|
+
# Return the Field with the given (public-facing) name
|
96
|
+
#
|
80
97
|
def field(field_name)
|
81
98
|
if field_factory = @field_factories_cache[field_name.to_sym]
|
82
99
|
field_factory.build
|
@@ -88,17 +105,27 @@ module Sunspot
|
|
88
105
|
end
|
89
106
|
end
|
90
107
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
108
|
+
#
|
109
|
+
# Return one or more text fields with the given public-facing name. This
|
110
|
+
# implementation will always return a single field (in an array), but
|
111
|
+
# CompositeSetup objects might return more than one.
|
112
|
+
#
|
113
|
+
def text_fields(field_name)
|
114
|
+
text_field =
|
115
|
+
if field_factory = @text_field_factories_cache[field_name.to_sym]
|
116
|
+
field_factory.build
|
117
|
+
else
|
118
|
+
raise(
|
119
|
+
UnrecognizedFieldError,
|
120
|
+
"No text field configured for #{@clazz.name} with name '#{field_name}'"
|
121
|
+
)
|
122
|
+
end
|
123
|
+
[text_field]
|
100
124
|
end
|
101
125
|
|
126
|
+
#
|
127
|
+
# Return the DynamicFieldFactory with the given base name
|
128
|
+
#
|
102
129
|
def dynamic_field_factory(field_name)
|
103
130
|
@dynamic_field_factories_cache[field_name.to_sym] || raise(
|
104
131
|
UnrecognizedFieldError,
|
@@ -106,11 +133,17 @@ module Sunspot
|
|
106
133
|
)
|
107
134
|
end
|
108
135
|
|
136
|
+
#
|
137
|
+
# Return all attribute fields
|
138
|
+
#
|
109
139
|
def fields
|
110
140
|
field_factories.map { |field_factory| field_factory.build }
|
111
141
|
end
|
112
142
|
|
113
|
-
|
143
|
+
#
|
144
|
+
# Return all text fields
|
145
|
+
#
|
146
|
+
def all_text_fields
|
114
147
|
text_field_factories.map { |text_field_factory| text_field_factory.build }
|
115
148
|
end
|
116
149
|
|
@@ -148,6 +181,7 @@ module Sunspot
|
|
148
181
|
def all_field_factories
|
149
182
|
all_field_factories = []
|
150
183
|
all_field_factories.concat(field_factories).concat(text_field_factories).concat(dynamic_field_factories)
|
184
|
+
all_field_factories << @coordinates_field_factory if @coordinates_field_factory
|
151
185
|
all_field_factories
|
152
186
|
end
|
153
187
|
|
@@ -173,6 +207,9 @@ module Sunspot
|
|
173
207
|
Util.full_const_get(@class_name)
|
174
208
|
end
|
175
209
|
|
210
|
+
#
|
211
|
+
# Get the document boost for a given model
|
212
|
+
#
|
176
213
|
def document_boost_for(model)
|
177
214
|
if @document_boost_extractor
|
178
215
|
@document_boost_extractor.value_for(model)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# A TextFieldSetup encapsulates a regular (or composite) setup, and exposes
|
4
|
+
# the #field() method returning text fields instead of attribute fields.
|
5
|
+
#
|
6
|
+
class TextFieldSetup #:nodoc:
|
7
|
+
def initialize(setup)
|
8
|
+
@setup = setup
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# Return a text field with the given name. Duck-type compatible with
|
13
|
+
# Setup and CompositeSetup, but return text fields instead.
|
14
|
+
#
|
15
|
+
def field(name)
|
16
|
+
fields = @setup.text_fields(name)
|
17
|
+
if fields
|
18
|
+
if fields.length == 1
|
19
|
+
fields.first
|
20
|
+
else
|
21
|
+
raise(
|
22
|
+
Sunspot::UnrecognizedFieldError,
|
23
|
+
"The text field with name #{name} has incompatible configurations for the classes #{@setup.type_names.join(', ')}"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/sunspot/type.rb
CHANGED
@@ -147,7 +147,7 @@ module Sunspot
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
def cast(string)
|
150
|
+
def cast(string) #:nodoc:
|
151
151
|
time = Time.xmlschema(string)
|
152
152
|
Date.civil(time.year, time.mon, time.mday)
|
153
153
|
end
|
@@ -183,15 +183,15 @@ module Sunspot
|
|
183
183
|
|
184
184
|
module ClassType
|
185
185
|
class <<self
|
186
|
-
def indexed_name(name)
|
186
|
+
def indexed_name(name) #:nodoc:
|
187
187
|
'class_name'
|
188
188
|
end
|
189
189
|
|
190
|
-
def to_indexed(value)
|
190
|
+
def to_indexed(value) #:nodoc:
|
191
191
|
value.name
|
192
192
|
end
|
193
193
|
|
194
|
-
def cast(string)
|
194
|
+
def cast(string) #:nodoc:
|
195
195
|
Sunspot::Util.full_const_get(string)
|
196
196
|
end
|
197
197
|
end
|
data/lib/sunspot/util.rb
CHANGED
@@ -66,7 +66,7 @@ module Sunspot
|
|
66
66
|
#
|
67
67
|
def full_const_get(string)
|
68
68
|
string.split('::').inject(Object) do |context, const_name|
|
69
|
-
context.const_get(const_name)
|
69
|
+
context.const_defined?(const_name) ? context.const_get(const_name) : context.const_missing(const_name)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -160,5 +160,31 @@ module Sunspot
|
|
160
160
|
destination
|
161
161
|
end
|
162
162
|
end
|
163
|
+
|
164
|
+
class Coordinates #:nodoc:
|
165
|
+
def initialize(coords)
|
166
|
+
@coords = coords
|
167
|
+
end
|
168
|
+
|
169
|
+
def lat
|
170
|
+
if @coords.respond_to?(:[])
|
171
|
+
@coords[0]
|
172
|
+
else
|
173
|
+
@coords.lat
|
174
|
+
end.to_f
|
175
|
+
end
|
176
|
+
|
177
|
+
def lng
|
178
|
+
if @coords.respond_to?(:[])
|
179
|
+
@coords[1]
|
180
|
+
elsif @coords.respond_to?(:lng)
|
181
|
+
@coords.lng
|
182
|
+
elsif @coords.respond_to?(:lon)
|
183
|
+
@coords.lon
|
184
|
+
elsif @coords.respond_to?(:long)
|
185
|
+
@coords.long
|
186
|
+
end.to_f
|
187
|
+
end
|
188
|
+
end
|
163
189
|
end
|
164
190
|
end
|
data/lib/sunspot.rb
CHANGED
@@ -9,9 +9,10 @@ end
|
|
9
9
|
|
10
10
|
require File.join(File.dirname(__FILE__), 'light_config')
|
11
11
|
|
12
|
-
%w(util adapters configuration setup composite_setup field
|
13
|
-
data_extractor indexer query search facet facet_row
|
14
|
-
instantiated_facet_row facet_data session type
|
12
|
+
%w(util adapters configuration setup composite_setup text_field_setup field
|
13
|
+
field_factory data_extractor indexer query search facet facet_row
|
14
|
+
instantiated_facet instantiated_facet_row facet_data session type
|
15
|
+
dsl).each do |filename|
|
15
16
|
require File.join(File.dirname(__FILE__), 'sunspot', filename)
|
16
17
|
end
|
17
18
|
|
@@ -35,6 +36,7 @@ module Sunspot
|
|
35
36
|
UnrecognizedRestrictionError = Class.new(Exception)
|
36
37
|
NoAdapterError = Class.new(Exception)
|
37
38
|
NoSetupError = Class.new(Exception)
|
39
|
+
IllegalSearchError = Class.new(Exception)
|
38
40
|
|
39
41
|
class <<self
|
40
42
|
# Configures indexing and search for a given class.
|
@@ -221,18 +223,6 @@ module Sunspot
|
|
221
223
|
# Zero, one, or more types to search for. If no types are passed, all
|
222
224
|
# configured types will be searched.
|
223
225
|
#
|
224
|
-
# ==== Options (last argument, optional)
|
225
|
-
#
|
226
|
-
# :keywords<String>:: Fulltext search string
|
227
|
-
# :conditions<Hash>::
|
228
|
-
# Hash of key-value pairs to be used as restrictions. Keys are field
|
229
|
-
# names. Scalar values are used as equality restrictions; arrays are used
|
230
|
-
# as "any of" restrictions; and Ranges are used as range restrictions.
|
231
|
-
# :order<String>:: order field and direction (e.g., 'updated_at desc')
|
232
|
-
# :page<Integer>:: Page to start on for pagination
|
233
|
-
# :per_page<Integer>::
|
234
|
-
# Number of results to use per page. Ignored if :page is not specified.
|
235
|
-
#
|
236
226
|
# ==== Returns
|
237
227
|
#
|
238
228
|
# Sunspot::Search:: Object containing results, facets, count, etc.
|
@@ -241,9 +231,10 @@ module Sunspot
|
|
241
231
|
# the following criteria:
|
242
232
|
#
|
243
233
|
# * They are not of type +text+.
|
244
|
-
# * They are defined for
|
245
|
-
# * They have the same data type for all of the classes being searched
|
234
|
+
# * They are defined for at least one of the classes being searched
|
235
|
+
# * They have the same data type for all of the classes being searched.
|
246
236
|
# * They have the same multiple flag for all of the classes being searched.
|
237
|
+
# * They have the same stored flag for all of the classes being searched.
|
247
238
|
#
|
248
239
|
# The restrictions available are the constants defined in the
|
249
240
|
# Sunspot::Restriction class. The standard restrictions are:
|
data/solr/solr/conf/schema.xml
CHANGED
@@ -1,50 +1,64 @@
|
|
1
|
-
<?xml version=
|
2
|
-
<schema
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<schema version="0.9" name="sunspot">
|
3
3
|
<types>
|
4
|
-
<fieldtype class=
|
4
|
+
<fieldtype class="solr.TextField" positionIncrementGap="100" name="text">
|
5
5
|
<analyzer>
|
6
|
-
<tokenizer class=
|
7
|
-
<filter class=
|
8
|
-
<filter class=
|
6
|
+
<tokenizer class="solr.StandardTokenizerFactory"/>
|
7
|
+
<filter class="solr.StandardFilterFactory"/>
|
8
|
+
<filter class="solr.LowerCaseFilterFactory"/>
|
9
9
|
</analyzer>
|
10
10
|
</fieldtype>
|
11
|
-
<fieldtype class=
|
12
|
-
<fieldtype class=
|
13
|
-
<fieldtype class=
|
14
|
-
<fieldtype class=
|
15
|
-
<fieldtype class=
|
16
|
-
<fieldtype class=
|
11
|
+
<fieldtype class="solr.RandomSortField" name="rand"/>
|
12
|
+
<fieldtype name="boolean" class="solr.BoolField" omitNorms="true"/>
|
13
|
+
<fieldtype name="sfloat" class="solr.SortableFloatField" omitNorms="true"/>
|
14
|
+
<fieldtype name="date" class="solr.DateField" omitNorms="true"/>
|
15
|
+
<fieldtype name="sint" class="solr.SortableIntField" omitNorms="true"/>
|
16
|
+
<fieldtype name="string" class="solr.StrField" omitNorms="true"/>
|
17
|
+
<fieldtype name="sdouble" class="solr.SortableDoubleField" omitNorms="true"/>
|
18
|
+
<fieldtype name="slong" class="solr.SortableLongField" omitNorms="true"/>
|
17
19
|
</types>
|
18
20
|
<fields>
|
19
|
-
<field indexed=
|
20
|
-
<field indexed=
|
21
|
-
<field
|
22
|
-
<field indexed=
|
23
|
-
<
|
24
|
-
<
|
25
|
-
<dynamicField indexed=
|
26
|
-
<dynamicField indexed=
|
27
|
-
<dynamicField
|
28
|
-
<dynamicField
|
29
|
-
<dynamicField
|
30
|
-
<dynamicField
|
31
|
-
<dynamicField
|
32
|
-
<dynamicField
|
33
|
-
<dynamicField
|
34
|
-
<dynamicField
|
35
|
-
<dynamicField
|
36
|
-
<dynamicField
|
37
|
-
<dynamicField
|
38
|
-
<dynamicField
|
39
|
-
<dynamicField
|
40
|
-
<dynamicField
|
41
|
-
<dynamicField
|
42
|
-
<dynamicField
|
43
|
-
<dynamicField
|
44
|
-
<dynamicField
|
21
|
+
<field name="id" type="string" indexed="true" stored="true" multiValued="false" />
|
22
|
+
<field name="type" type="string" indexed="true" stored="false" multiValued="true" />
|
23
|
+
<field name="class_name" type="string" indexed="true" stored="false" multiValued="false" />
|
24
|
+
<field name="text" type="text" indexed="true" stored="false" multiValued="true" />
|
25
|
+
<field name="lat" type="sdouble" indexed="true" stored="true" multiValued="false" />
|
26
|
+
<field name="long" type="sdouble" indexed="true" stored="true" multiValued="false" />
|
27
|
+
<dynamicField name="*_text" type="text" indexed="true" stored="false" multiValued="true" />
|
28
|
+
<dynamicField name="*_texts" type="text" indexed="true" stored="true" multiValued="true" />
|
29
|
+
<dynamicField name="random_*" type="rand" indexed="true" stored="false" multiValued="false" />
|
30
|
+
<dynamicField name="_local*" type="sdouble" indexed="true" stored="false" multiValued="false" />
|
31
|
+
<dynamicField name="*_b" type="boolean" indexed="true" stored="false" multiValued="false" />
|
32
|
+
<dynamicField name="*_f" type="sfloat" indexed="true" stored="false" multiValued="false" />
|
33
|
+
<dynamicField name="*_d" type="date" indexed="true" stored="false" multiValued="false" />
|
34
|
+
<dynamicField name="*_i" type="sint" indexed="true" stored="false" multiValued="false" />
|
35
|
+
<dynamicField name="*_s" type="string" indexed="true" stored="false" multiValued="false" />
|
36
|
+
<dynamicField name="*_e" type="sdouble" indexed="true" stored="false" multiValued="false" />
|
37
|
+
<dynamicField name="*_l" type="slong" indexed="true" stored="false" multiValued="false" />
|
38
|
+
<dynamicField name="*_bm" type="boolean" indexed="true" stored="false" multiValued="true" />
|
39
|
+
<dynamicField name="*_fm" type="sfloat" indexed="true" stored="false" multiValued="true" />
|
40
|
+
<dynamicField name="*_dm" type="date" indexed="true" stored="false" multiValued="true" />
|
41
|
+
<dynamicField name="*_im" type="sint" indexed="true" stored="false" multiValued="true" />
|
42
|
+
<dynamicField name="*_sm" type="string" indexed="true" stored="false" multiValued="true" />
|
43
|
+
<dynamicField name="*_em" type="sdouble" indexed="true" stored="false" multiValued="true" />
|
44
|
+
<dynamicField name="*_lm" type="slong" indexed="true" stored="false" multiValued="true" />
|
45
|
+
<dynamicField name="*_bs" type="boolean" indexed="true" stored="true" multiValued="false" />
|
46
|
+
<dynamicField name="*_fs" type="sfloat" indexed="true" stored="true" multiValued="false" />
|
47
|
+
<dynamicField name="*_ds" type="date" indexed="true" stored="true" multiValued="false" />
|
48
|
+
<dynamicField name="*_is" type="sint" indexed="true" stored="true" multiValued="false" />
|
49
|
+
<dynamicField name="*_ss" type="string" indexed="true" stored="true" multiValued="false" />
|
50
|
+
<dynamicField name="*_es" type="sdouble" indexed="true" stored="true" multiValued="false" />
|
51
|
+
<dynamicField name="*_ls" type="slong" indexed="true" stored="true" multiValued="false" />
|
52
|
+
<dynamicField name="*_bms" type="boolean" indexed="true" stored="true" multiValued="true" />
|
53
|
+
<dynamicField name="*_fms" type="sfloat" indexed="true" stored="true" multiValued="true" />
|
54
|
+
<dynamicField name="*_dms" type="date" indexed="true" stored="true" multiValued="true" />
|
55
|
+
<dynamicField name="*_ims" type="sint" indexed="true" stored="true" multiValued="true" />
|
56
|
+
<dynamicField name="*_sms" type="string" indexed="true" stored="true" multiValued="true" />
|
57
|
+
<dynamicField name="*_ems" type="sdouble" indexed="true" stored="true" multiValued="true" />
|
58
|
+
<dynamicField name="*_lms" type="slong" indexed="true" stored="true" multiValued="true" />
|
45
59
|
</fields>
|
46
60
|
<uniqueKey>id</uniqueKey>
|
47
61
|
<defaultSearchField>text</defaultSearchField>
|
48
|
-
<solrQueryParser defaultOperator=
|
49
|
-
<copyField dest=
|
62
|
+
<solrQueryParser defaultOperator="AND"/>
|
63
|
+
<copyField dest="text" source="*_text"/>
|
50
64
|
</schema>
|
@@ -34,6 +34,12 @@
|
|
34
34
|
|
35
35
|
<indexDefaults>
|
36
36
|
<!-- Values here affect all index writers and act as a default unless overridden. -->
|
37
|
+
%arr{ :name => 'components' }
|
38
|
+
%str localsolr
|
39
|
+
%str facet
|
40
|
+
%str mlt
|
41
|
+
%str highlight
|
42
|
+
%str debug
|
37
43
|
<useCompoundFile>false</useCompoundFile>
|
38
44
|
|
39
45
|
<mergeFactor>10</mergeFactor>
|
@@ -693,4 +699,28 @@
|
|
693
699
|
-->
|
694
700
|
</admin>
|
695
701
|
|
702
|
+
<!-- configuration for LocalSolr -->
|
703
|
+
<updateRequestProcessorChain>
|
704
|
+
<processor class='com.pjaol.search.solr.update.LocalUpdateProcessorFactory'>
|
705
|
+
<str name='latField'>lat</str>
|
706
|
+
<str name='lngField'>long</str>
|
707
|
+
<int name='startTier'>9</int>
|
708
|
+
<int name='endTier'>16</int>
|
709
|
+
</processor>
|
710
|
+
<processor class='solr.RunUpdateProcessorFactory'></processor>
|
711
|
+
<processor class='solr.LogUpdateProcessorFactory'></processor>
|
712
|
+
</updateRequestProcessorChain>
|
713
|
+
<searchComponent class='com.pjaol.search.solr.component.LocalSolrQueryComponent' name='localsolr'>
|
714
|
+
<str name='latField'>lat</str>
|
715
|
+
<str name='lngField'>long</str>
|
716
|
+
</searchComponent>
|
717
|
+
<requestHandler class='org.apache.solr.handler.component.SearchHandler' name='geo'>
|
718
|
+
<arr name='components'>
|
719
|
+
<str>localsolr</str>
|
720
|
+
<str>facet</str>
|
721
|
+
<str>mlt</str>
|
722
|
+
<str>highlight</str>
|
723
|
+
<str>debug</str>
|
724
|
+
</arr>
|
725
|
+
</requestHandler>
|
696
726
|
</config>
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe 'indexing attribute fields', :type => :indexer do
|
4
|
+
it 'should correctly index a stored string attribute field' do
|
5
|
+
session.index(post(:title => 'A Title'))
|
6
|
+
connection.should have_add_with(:title_ss => 'A Title')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should correctly index an integer attribute field' do
|
10
|
+
session.index(post(:blog_id => 4))
|
11
|
+
connection.should have_add_with(:blog_id_i => '4')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should correctly index a float attribute field' do
|
15
|
+
session.index(post(:ratings_average => 2.23))
|
16
|
+
connection.should have_add_with(:average_rating_f => '2.23')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should allow indexing by a multiple-value field' do
|
20
|
+
session.index(post(:category_ids => [3, 14]))
|
21
|
+
connection.should have_add_with(:category_ids_im => ['3', '14'])
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should correctly index a time field' do
|
25
|
+
session.index(
|
26
|
+
post(:published_at => Time.parse('1983-07-08 05:00:00 -0400'))
|
27
|
+
)
|
28
|
+
connection.should have_add_with(:published_at_d => '1983-07-08T09:00:00Z')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should correctly index a date field' do
|
32
|
+
session.index(post(:expire_date => Date.new(2009, 07, 13)))
|
33
|
+
connection.should have_add_with(:expire_date_d => '2009-07-13T00:00:00Z')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should correctly index a boolean field' do
|
37
|
+
session.index(post(:featured => true))
|
38
|
+
connection.should have_add_with(:featured_b => 'true')
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should correctly index a false boolean field' do
|
42
|
+
session.index(post(:featured => false))
|
43
|
+
connection.should have_add_with(:featured_b => 'false')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should not index a nil boolean field' do
|
47
|
+
session.index(post)
|
48
|
+
connection.should_not have_add_with(:featured_b)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should index latitude and longitude as a pair' do
|
52
|
+
session.index(post(:coordinates => [40.7, -73.5]))
|
53
|
+
connection.should have_add_with(:lat => 40.7, :long => -73.5)
|
54
|
+
end
|
55
|
+
|
56
|
+
[
|
57
|
+
[:lat, :lng],
|
58
|
+
[:lat, :lon],
|
59
|
+
[:lat, :long]
|
60
|
+
].each do |lat_attr, lng_attr|
|
61
|
+
it "should index latitude and longitude from #{lat_attr.inspect}, #{lng_attr.inspect}" do
|
62
|
+
session.index(post(
|
63
|
+
:coordinates => OpenStruct.new(lat_attr => 40.7, lng_attr => -73.5)
|
64
|
+
))
|
65
|
+
connection.should have_add_with(:lat => 40.7, :long => -73.5)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should correctly index an attribute field with block access' do
|
70
|
+
session.index(post(:title => 'The Blog Post'))
|
71
|
+
connection.should have_add_with(:sort_title_s => 'blog post')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should correctly index an attribute field with instance-external block access' do
|
75
|
+
session.index(post(:category_ids => [1, 2, 3]))
|
76
|
+
connection.should have_add_with(:primary_category_id_i => '1')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should correctly index a field that is defined on a superclass' do
|
80
|
+
Sunspot.setup(SuperClass) { string :author_name }
|
81
|
+
session.index(post(:author_name => 'Mat Brown'))
|
82
|
+
connection.should have_add_with(:author_name_s => 'Mat Brown')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should throw a NoMethodError only if a nonexistent type is defined' do
|
86
|
+
lambda { Sunspot.setup(Post) { string :author_name }}.should_not raise_error
|
87
|
+
lambda { Sunspot.setup(Post) { bogus :journey }}.should raise_error(NoMethodError)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should throw a NoMethodError if a nonexistent field argument is passed' do
|
91
|
+
lambda { Sunspot.setup(Post) { string :author_name, :bogus => :argument }}.should raise_error(ArgumentError)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should throw an ArgumentError if single-value field tries to index multiple values' do
|
95
|
+
lambda do
|
96
|
+
Sunspot.setup(Post) { string :author_name }
|
97
|
+
session.index(post(:author_name => ['Mat Brown', 'Matthew Brown']))
|
98
|
+
end.should raise_error(ArgumentError)
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe 'batch indexing', :type => :indexer do
|
4
|
+
it 'should send all batched adds in a single request' do
|
5
|
+
posts = Array.new(2) { Post.new }
|
6
|
+
session.batch do
|
7
|
+
for post in posts
|
8
|
+
session.index(post)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
connection.adds.length.should == 1
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should add all batched adds' do
|
15
|
+
posts = Array.new(2) { Post.new }
|
16
|
+
session.batch do
|
17
|
+
for post in posts
|
18
|
+
session.index(post)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
add = connection.adds.last
|
22
|
+
connection.adds.first.map { |add| add.field_by_name(:id).value }.should ==
|
23
|
+
posts.map { |post| "Post #{post.id}" }
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not index changes to models that happen after index call' do
|
27
|
+
post = Post.new
|
28
|
+
session.batch do
|
29
|
+
session.index(post)
|
30
|
+
post.title = 'Title'
|
31
|
+
end
|
32
|
+
connection.adds.first.first.field_by_name(:title_ss).should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should batch an add and a delete' do
|
36
|
+
pending 'batching all operations'
|
37
|
+
connection.should_not_receive(:add)
|
38
|
+
connection.should_not_receive(:remove)
|
39
|
+
posts = Array.new(2) { Post.new }
|
40
|
+
session.batch do
|
41
|
+
session.index(posts[0])
|
42
|
+
session.remove(posts[1])
|
43
|
+
end
|
44
|
+
connection.adds
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe 'indexing dynamic fields' do
|
4
|
+
it 'indexes string data' do
|
5
|
+
session.index(post(:custom_string => { :test => 'string' }))
|
6
|
+
connection.should have_add_with(:"custom_string:test_s" => 'string')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'indexes integer data with virtual accessor' do
|
10
|
+
session.index(post(:category_ids => [1, 2]))
|
11
|
+
connection.should have_add_with(:"custom_integer:1_i" => '1', :"custom_integer:2_i" => '1')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'indexes float data' do
|
15
|
+
session.index(post(:custom_fl => { :test => 1.5 }))
|
16
|
+
connection.should have_add_with(:"custom_float:test_fm" => '1.5')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'indexes time data' do
|
20
|
+
session.index(post(:custom_time => { :test => Time.parse('2009-05-18 18:05:00 -0400') }))
|
21
|
+
connection.should have_add_with(:"custom_time:test_d" => '2009-05-18T22:05:00Z')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'indexes boolean data' do
|
25
|
+
session.index(post(:custom_boolean => { :test => false }))
|
26
|
+
connection.should have_add_with(:"custom_boolean:test_b" => 'false')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'indexes multiple values for a field' do
|
30
|
+
session.index(post(:custom_fl => { :test => [1.0, 2.1, 3.2] }))
|
31
|
+
connection.should have_add_with(:"custom_float:test_fm" => %w(1.0 2.1 3.2))
|
32
|
+
end
|
33
|
+
end
|