sunspot_rbg 1.3.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.
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/History.txt +222 -0
- data/LICENSE +18 -0
- data/Rakefile +17 -0
- data/TODO +13 -0
- data/VERSION.yml +4 -0
- data/bin/sunspot-installer +19 -0
- data/bin/sunspot-solr +74 -0
- data/installer/config/schema.yml +95 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/composite_setup.rb +202 -0
- data/lib/sunspot/configuration.rb +46 -0
- data/lib/sunspot/data_extractor.rb +50 -0
- data/lib/sunspot/dsl/adjustable.rb +47 -0
- data/lib/sunspot/dsl/field_query.rb +279 -0
- data/lib/sunspot/dsl/fields.rb +103 -0
- data/lib/sunspot/dsl/fulltext.rb +243 -0
- data/lib/sunspot/dsl/function.rb +14 -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 +28 -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 +121 -0
- data/lib/sunspot/dsl/scope.rb +217 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/dsl/standard_query.rb +121 -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 +131 -0
- data/lib/sunspot/installer/library_installer.rb +45 -0
- data/lib/sunspot/installer/schema_builder.rb +219 -0
- data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
- data/lib/sunspot/installer/task_helper.rb +18 -0
- data/lib/sunspot/installer.rb +31 -0
- data/lib/sunspot/query/abstract_field_facet.rb +52 -0
- data/lib/sunspot/query/boost_query.rb +24 -0
- data/lib/sunspot/query/common_query.rb +85 -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 +128 -0
- data/lib/sunspot/query/field_facet.rb +41 -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/highlighting.rb +55 -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 +38 -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 +95 -0
- data/lib/sunspot/query/sort_composite.rb +33 -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 +293 -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/highlight.rb +38 -0
- data/lib/sunspot/search/hit.rb +136 -0
- data/lib/sunspot/search/more_like_this_search.rb +31 -0
- data/lib/sunspot/search/paginated_collection.rb +55 -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/server.rb +152 -0
- data/lib/sunspot/session.rb +260 -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/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 +87 -0
- data/lib/sunspot/setup.rb +350 -0
- data/lib/sunspot/text_field_setup.rb +29 -0
- data/lib/sunspot/type.rb +372 -0
- data/lib/sunspot/util.rb +243 -0
- data/lib/sunspot/version.rb +3 -0
- data/lib/sunspot.rb +569 -0
- data/lib/sunspot_rbg.rb +7 -0
- data/log/.gitignore +1 -0
- data/pkg/.gitignore +1 -0
- data/script/console +10 -0
- data/solr/README.txt +42 -0
- data/solr/etc/jetty.xml +218 -0
- data/solr/etc/webdefault.xml +379 -0
- data/solr/lib/jetty-6.1.3.jar +0 -0
- data/solr/lib/jetty-util-6.1.3.jar +0 -0
- data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr/logs/.gitignore +1 -0
- data/solr/solr/.gitignore +1 -0
- data/solr/solr/README.txt +54 -0
- data/solr/solr/conf/admin-extra.html +31 -0
- data/solr/solr/conf/elevate.xml +36 -0
- data/solr/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/solr/solr/conf/protwords.txt +21 -0
- data/solr/solr/conf/schema.xml +238 -0
- data/solr/solr/conf/scripts.conf +24 -0
- data/solr/solr/conf/solrconfig.xml +934 -0
- data/solr/solr/conf/spellings.txt +2 -0
- data/solr/solr/conf/stopwords.txt +58 -0
- data/solr/solr/conf/synonyms.txt +31 -0
- data/solr/solr/conf/xslt/example.xsl +132 -0
- data/solr/solr/conf/xslt/example_atom.xsl +67 -0
- data/solr/solr/conf/xslt/example_rss.xsl +66 -0
- data/solr/solr/conf/xslt/luke.xsl +337 -0
- data/solr/start.jar +0 -0
- data/solr/webapps/solr.war +0 -0
- data/solr-1.3/etc/jetty.xml +212 -0
- data/solr-1.3/etc/webdefault.xml +379 -0
- data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
- data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr-1.3/solr/conf/elevate.xml +36 -0
- data/solr-1.3/solr/conf/protwords.txt +21 -0
- data/solr-1.3/solr/conf/schema.xml +64 -0
- data/solr-1.3/solr/conf/solrconfig.xml +725 -0
- data/solr-1.3/solr/conf/stopwords.txt +57 -0
- data/solr-1.3/solr/conf/synonyms.txt +31 -0
- data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
- data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
- data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
- data/solr-1.3/solr/lib/locallucene.jar +0 -0
- data/solr-1.3/solr/lib/localsolr.jar +0 -0
- data/solr-1.3/start.jar +0 -0
- data/solr-1.3/webapps/solr.war +0 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/binding_spec.rb +50 -0
- data/spec/api/indexer/attributes_spec.rb +149 -0
- data/spec/api/indexer/batch_spec.rb +46 -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 +70 -0
- data/spec/api/query/geo_examples.rb +68 -0
- data/spec/api/query/highlighting_examples.rb +223 -0
- data/spec/api/query/more_like_this_spec.rb +140 -0
- data/spec/api/query/ordering_pagination_examples.rb +95 -0
- data/spec/api/query/scope_examples.rb +275 -0
- data/spec/api/query/spec_helper.rb +1 -0
- data/spec/api/query/standard_spec.rb +28 -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 +120 -0
- data/spec/api/search/paginated_collection_spec.rb +26 -0
- data/spec/api/search/results_spec.rb +66 -0
- data/spec/api/search/search_spec.rb +23 -0
- data/spec/api/search/spec_helper.rb +1 -0
- data/spec/api/server_spec.rb +91 -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 +50 -0
- data/spec/api/session_spec.rb +220 -0
- data/spec/api/spec_helper.rb +3 -0
- data/spec/api/sunspot_spec.rb +18 -0
- data/spec/ext.rb +11 -0
- data/spec/helpers/indexer_helper.rb +29 -0
- data/spec/helpers/query_helper.rb +38 -0
- data/spec/helpers/search_helper.rb +80 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/integration/faceting_spec.rb +238 -0
- data/spec/integration/highlighting_spec.rb +22 -0
- data/spec/integration/indexing_spec.rb +33 -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/spec_helper.rb +7 -0
- data/spec/integration/stored_fields_spec.rb +10 -0
- data/spec/integration/test_pagination.rb +32 -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 +85 -0
- data/spec/mocks/super_class.rb +2 -0
- data/spec/mocks/user.rb +13 -0
- data/spec/spec_helper.rb +30 -0
- data/sunspot.gemspec +40 -0
- data/tasks/rdoc.rake +27 -0
- data/tasks/schema.rake +19 -0
- data/tasks/todo.rake +4 -0
- metadata +457 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
#
|
|
3
|
+
# A Sunspot session encapsulates a connection to Solr and a set of
|
|
4
|
+
# configuration choices. Though users of Sunspot may manually instantiate
|
|
5
|
+
# Session objects, in the general case it's easier to use the singleton
|
|
6
|
+
# stored in the Sunspot module. Since the Sunspot module provides all of
|
|
7
|
+
# the instance methods of Session as class methods, they are not documented
|
|
8
|
+
# again here.
|
|
9
|
+
#
|
|
10
|
+
class Session
|
|
11
|
+
class <<self
|
|
12
|
+
attr_writer :connection_class #:nodoc:
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# For testing purposes
|
|
16
|
+
#
|
|
17
|
+
def connection_class #:nodoc:
|
|
18
|
+
@connection_class ||= RSolr
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# Sunspot::Configuration object for this session
|
|
24
|
+
#
|
|
25
|
+
attr_reader :config
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Sessions are initialized with a Sunspot configuration and a Solr
|
|
29
|
+
# connection. Usually you will want to stick with the default arguments
|
|
30
|
+
# when instantiating your own sessions.
|
|
31
|
+
#
|
|
32
|
+
def initialize(config = Configuration.build, connection = nil)
|
|
33
|
+
@config = config
|
|
34
|
+
yield(@config) if block_given?
|
|
35
|
+
@connection = connection
|
|
36
|
+
@deletes = @adds = 0
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# See Sunspot.new_search
|
|
41
|
+
#
|
|
42
|
+
def new_search(*types, &block)
|
|
43
|
+
types.flatten!
|
|
44
|
+
search = Search::StandardSearch.new(
|
|
45
|
+
connection,
|
|
46
|
+
setup_for_types(types),
|
|
47
|
+
Query::StandardQuery.new(types),
|
|
48
|
+
@config
|
|
49
|
+
)
|
|
50
|
+
search.build(&block) if block
|
|
51
|
+
search
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# See Sunspot.search
|
|
56
|
+
#
|
|
57
|
+
def search(*types, &block)
|
|
58
|
+
search = new_search(*types, &block)
|
|
59
|
+
search.execute
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#
|
|
63
|
+
# See Sunspot.new_more_like_this
|
|
64
|
+
#
|
|
65
|
+
def new_more_like_this(object, *types, &block)
|
|
66
|
+
types[0] ||= object.class
|
|
67
|
+
mlt = Search::MoreLikeThisSearch.new(
|
|
68
|
+
connection,
|
|
69
|
+
setup_for_types(types),
|
|
70
|
+
Query::MoreLikeThisQuery.new(object, types),
|
|
71
|
+
@config
|
|
72
|
+
)
|
|
73
|
+
mlt.build(&block) if block
|
|
74
|
+
mlt
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#
|
|
78
|
+
# See Sunspot.more_like_this
|
|
79
|
+
#
|
|
80
|
+
def more_like_this(object, *types, &block)
|
|
81
|
+
mlt = new_more_like_this(object, *types, &block)
|
|
82
|
+
mlt.execute
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# See Sunspot.index
|
|
87
|
+
#
|
|
88
|
+
def index(*objects)
|
|
89
|
+
objects.flatten!
|
|
90
|
+
@adds += objects.length
|
|
91
|
+
indexer.add(objects)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#
|
|
95
|
+
# See Sunspot.index!
|
|
96
|
+
#
|
|
97
|
+
def index!(*objects)
|
|
98
|
+
index(*objects)
|
|
99
|
+
commit
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
#
|
|
103
|
+
# See Sunspot.commit
|
|
104
|
+
#
|
|
105
|
+
def commit
|
|
106
|
+
@adds = @deletes = 0
|
|
107
|
+
connection.commit
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# See Sunspot.optimize
|
|
112
|
+
#
|
|
113
|
+
def optimize
|
|
114
|
+
@adds = @deletes = 0
|
|
115
|
+
connection.optimize
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# See Sunspot.remove
|
|
120
|
+
#
|
|
121
|
+
def remove(*objects, &block)
|
|
122
|
+
if block
|
|
123
|
+
types = objects
|
|
124
|
+
conjunction = Query::Connective::Conjunction.new
|
|
125
|
+
if types.length == 1
|
|
126
|
+
conjunction.add_positive_restriction(TypeField.instance, Query::Restriction::EqualTo, types.first)
|
|
127
|
+
else
|
|
128
|
+
conjunction.add_positive_restriction(TypeField.instance, Query::Restriction::AnyOf, types)
|
|
129
|
+
end
|
|
130
|
+
dsl = DSL::Scope.new(conjunction, setup_for_types(types))
|
|
131
|
+
Util.instance_eval_or_call(dsl, &block)
|
|
132
|
+
indexer.remove_by_scope(conjunction)
|
|
133
|
+
else
|
|
134
|
+
objects.flatten!
|
|
135
|
+
@deletes += objects.length
|
|
136
|
+
objects.each do |object|
|
|
137
|
+
indexer.remove(object)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
# See Sunspot.remove!
|
|
144
|
+
#
|
|
145
|
+
def remove!(*objects)
|
|
146
|
+
remove(*objects)
|
|
147
|
+
commit
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#
|
|
151
|
+
# See Sunspot.remove_by_id
|
|
152
|
+
#
|
|
153
|
+
def remove_by_id(clazz, id)
|
|
154
|
+
class_name =
|
|
155
|
+
if clazz.is_a?(Class)
|
|
156
|
+
clazz.name
|
|
157
|
+
else
|
|
158
|
+
clazz.to_s
|
|
159
|
+
end
|
|
160
|
+
indexer.remove_by_id(class_name, id)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
#
|
|
164
|
+
# See Sunspot.remove_by_id!
|
|
165
|
+
#
|
|
166
|
+
def remove_by_id!(clazz, id)
|
|
167
|
+
remove_by_id(clazz, id)
|
|
168
|
+
commit
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#
|
|
172
|
+
# See Sunspot.remove_all
|
|
173
|
+
#
|
|
174
|
+
def remove_all(*classes)
|
|
175
|
+
classes.flatten!
|
|
176
|
+
if classes.empty?
|
|
177
|
+
@deletes += 1
|
|
178
|
+
indexer.remove_all
|
|
179
|
+
else
|
|
180
|
+
@deletes += classes.length
|
|
181
|
+
classes.each { |clazz| indexer.remove_all(clazz) }
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# See Sunspot.remove_all!
|
|
187
|
+
#
|
|
188
|
+
def remove_all!(*classes)
|
|
189
|
+
remove_all(*classes)
|
|
190
|
+
commit
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
#
|
|
194
|
+
# See Sunspot.dirty?
|
|
195
|
+
#
|
|
196
|
+
def dirty?
|
|
197
|
+
(@deletes + @adds) > 0
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
#
|
|
201
|
+
# See Sunspot.commit_if_dirty
|
|
202
|
+
#
|
|
203
|
+
def commit_if_dirty
|
|
204
|
+
commit if dirty?
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
#
|
|
208
|
+
# See Sunspot.delete_dirty?
|
|
209
|
+
#
|
|
210
|
+
def delete_dirty?
|
|
211
|
+
@deletes > 0
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
#
|
|
215
|
+
# See Sunspot.commit_if_delete_dirty
|
|
216
|
+
#
|
|
217
|
+
def commit_if_delete_dirty
|
|
218
|
+
commit if delete_dirty?
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
#
|
|
222
|
+
# See Sunspot.batch
|
|
223
|
+
#
|
|
224
|
+
def batch
|
|
225
|
+
indexer.start_batch
|
|
226
|
+
yield
|
|
227
|
+
indexer.flush_batch
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
private
|
|
231
|
+
|
|
232
|
+
#
|
|
233
|
+
# Retrieve the Solr connection for this session, creating one if it does not
|
|
234
|
+
# already exist.
|
|
235
|
+
#
|
|
236
|
+
# ==== Returns
|
|
237
|
+
#
|
|
238
|
+
# RSolr::Connection::Base:: The connection for this session
|
|
239
|
+
#
|
|
240
|
+
def connection
|
|
241
|
+
@connection ||=
|
|
242
|
+
self.class.connection_class.connect(:url => config.solr.url)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def indexer
|
|
246
|
+
@indexer ||= Indexer.new(connection)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def setup_for_types(types)
|
|
250
|
+
if types.empty?
|
|
251
|
+
raise(ArgumentError, "You must specify at least one type to search")
|
|
252
|
+
end
|
|
253
|
+
if types.length == 1
|
|
254
|
+
Setup.for(types.first)
|
|
255
|
+
else
|
|
256
|
+
CompositeSetup.for(types)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module SessionProxy
|
|
3
|
+
class AbstractSessionProxy #:nodoc:
|
|
4
|
+
class <<self
|
|
5
|
+
def delegate(*args)
|
|
6
|
+
options = Util.extract_options_from(args)
|
|
7
|
+
delegate = options[:to]
|
|
8
|
+
args.each do |method|
|
|
9
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
10
|
+
def #{method}(*args, &block)
|
|
11
|
+
#{delegate}.#{method}(*args, &block)
|
|
12
|
+
end
|
|
13
|
+
RUBY
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def not_supported(*methods)
|
|
18
|
+
methods.each do |method|
|
|
19
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
20
|
+
def #{method}(*args, &block)
|
|
21
|
+
raise NotSupportedError, "#{name} does not support #{method.inspect}"
|
|
22
|
+
end
|
|
23
|
+
RUBY
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module SessionProxy
|
|
3
|
+
#
|
|
4
|
+
# An abstract subclass of ShardingSessionProxy that shards by class.
|
|
5
|
+
# Concrete subclasses should not override the #session_for method, but
|
|
6
|
+
# should instead implement the #session_for_class method. They must also
|
|
7
|
+
# still implement the #all_sessions method.
|
|
8
|
+
#
|
|
9
|
+
# Unlike its parent class, ClassShardingSessionProxy implements
|
|
10
|
+
# #remove_by_id and all flavors of #remove_all.
|
|
11
|
+
#
|
|
12
|
+
class ClassShardingSessionProxy < ShardingSessionProxy
|
|
13
|
+
#
|
|
14
|
+
# Remove the Session object pointing at the shard that indexes the given
|
|
15
|
+
# class.
|
|
16
|
+
#
|
|
17
|
+
# <strong>Concrete subclasses must implement this method.</strong>
|
|
18
|
+
#
|
|
19
|
+
def session_for_class(clazz)
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# See Sunspot.remove_by_id
|
|
25
|
+
#
|
|
26
|
+
def remove_by_id(clazz, id)
|
|
27
|
+
session_for_class(clazz).remove_by_id(clazz, id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# See Sunspot.remove_by_id!
|
|
32
|
+
#
|
|
33
|
+
def remove_by_id!(clazz, id)
|
|
34
|
+
session_for_class(clazz).remove_by_id!(clazz, id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# See Sunspot.remove_all
|
|
39
|
+
#
|
|
40
|
+
def remove_all(clazz = nil)
|
|
41
|
+
if clazz
|
|
42
|
+
session_for_class(clazz).remove_all(clazz)
|
|
43
|
+
else
|
|
44
|
+
all_sessions.each { |session| session.remove_all }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# See Sunspot.remove_all!
|
|
50
|
+
#
|
|
51
|
+
def remove_all!(clazz = nil)
|
|
52
|
+
if clazz
|
|
53
|
+
session_for_class(clazz).remove_all!(clazz)
|
|
54
|
+
else
|
|
55
|
+
all_sessions.each { |session| session.remove_all! }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def session_for(object)
|
|
62
|
+
session_for_class(object.class)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module SessionProxy
|
|
3
|
+
#
|
|
4
|
+
# A concrete implementation of ShardingSessionProxy that determines the
|
|
5
|
+
# shard for a given object based on the hash of its class and ID.
|
|
6
|
+
#
|
|
7
|
+
# <strong>If you change the number of shard sessions that this proxy
|
|
8
|
+
# encapsulates, all objects will point to a different shard.</strong> If you
|
|
9
|
+
# plan on adding more shards over time, consider your own
|
|
10
|
+
# ShardingSessionProxy implementation that does not determine the session
|
|
11
|
+
# using modular arithmetic (e.g., IDs 1-10000 go to shard 1, 10001-20000 go
|
|
12
|
+
# to shard 2, etc.)
|
|
13
|
+
#
|
|
14
|
+
# This implementation will, on average, yield an even distribution of
|
|
15
|
+
# objects across shards.
|
|
16
|
+
#
|
|
17
|
+
# Unlike the abstract ShardingSessionProxy, this proxy supports the
|
|
18
|
+
# #remove_by_id method.
|
|
19
|
+
#
|
|
20
|
+
class IdShardingSessionProxy < ShardingSessionProxy
|
|
21
|
+
#
|
|
22
|
+
# The shard sessions encapsulated by this class.
|
|
23
|
+
#
|
|
24
|
+
attr_reader :sessions
|
|
25
|
+
alias_method :all_sessions, :sessions #:nodoc:
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Initialize with a search session (see ShardingSessionProxy.new) and a
|
|
29
|
+
# collection of one or more shard sessions. See note about changing the
|
|
30
|
+
# number of shard sessions in the documentation for this class.
|
|
31
|
+
#
|
|
32
|
+
def initialize(search_session, shard_sessions)
|
|
33
|
+
super(search_session)
|
|
34
|
+
@sessions = shard_sessions
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Return a session based on the hash of the class and ID, modulo the
|
|
39
|
+
# number of shard sessions.
|
|
40
|
+
#
|
|
41
|
+
def session_for(object) #:nodoc:
|
|
42
|
+
session_for_index_id(Adapters::InstanceAdapter.adapt(object).index_id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# See Sunspot.remove_by_id
|
|
47
|
+
#
|
|
48
|
+
def remove_by_id(clazz, id)
|
|
49
|
+
session_for_index_id(
|
|
50
|
+
Adapters::InstanceAdapter.index_id_for(clazz, id)
|
|
51
|
+
).remove_by_id(clazz, id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# See Sunspot.remove_by_id!
|
|
56
|
+
#
|
|
57
|
+
def remove_by_id!(clazz, id)
|
|
58
|
+
session_for_index_id(
|
|
59
|
+
Adapters::InstanceAdapter.index_id_for(clazz, id)
|
|
60
|
+
).remove_by_id!(clazz, id)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def session_for_index_id(index_id)
|
|
66
|
+
@sessions[id_hash(index_id) % @sessions.length]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# This method is implemented explicitly instead of using String#hash to
|
|
71
|
+
# give predictable behavior across different Ruby interpreters.
|
|
72
|
+
#
|
|
73
|
+
if "".respond_to?(:bytes) # Ruby 1.9
|
|
74
|
+
def id_hash(id)
|
|
75
|
+
id.bytes.inject { |hash, byte| hash * 31 + byte }
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
def id_hash(id)
|
|
79
|
+
hash, i, len = 0, 0, id.length
|
|
80
|
+
while i < len
|
|
81
|
+
hash = hash * 31 + id[i]
|
|
82
|
+
i += 1
|
|
83
|
+
end
|
|
84
|
+
hash
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'abstract_session_proxy')
|
|
2
|
+
|
|
3
|
+
module Sunspot
|
|
4
|
+
module SessionProxy
|
|
5
|
+
#
|
|
6
|
+
# This session proxy implementation allows Sunspot to be used with a
|
|
7
|
+
# master/slave Solr deployment. All write methods are delegated to a master
|
|
8
|
+
# session, and read methods are delegated to a slave session.
|
|
9
|
+
#
|
|
10
|
+
class MasterSlaveSessionProxy < AbstractSessionProxy
|
|
11
|
+
#
|
|
12
|
+
# The session that connects to the master Solr instance.
|
|
13
|
+
#
|
|
14
|
+
attr_reader :master_session
|
|
15
|
+
#
|
|
16
|
+
# The session that connects to the slave Solr instance.
|
|
17
|
+
#
|
|
18
|
+
attr_reader :slave_session
|
|
19
|
+
|
|
20
|
+
delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty,
|
|
21
|
+
:config, :delete_dirty?, :dirty?, :index, :index!, :optimize, :remove,
|
|
22
|
+
:remove!, :remove_all, :remove_all!, :remove_by_id,
|
|
23
|
+
:remove_by_id!, :to => :master_session
|
|
24
|
+
delegate :new_search, :search, :new_more_like_this, :more_like_this, :to => :slave_session
|
|
25
|
+
|
|
26
|
+
def initialize(master_session, slave_session)
|
|
27
|
+
@master_session, @slave_session = master_session, slave_session
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# By default, return the configuration for the master session. If the
|
|
32
|
+
# +delegate+ param is +:slave+, then return config for the slave session.
|
|
33
|
+
#
|
|
34
|
+
def config(delegate = :master)
|
|
35
|
+
case delegate
|
|
36
|
+
when :master then @master_session.config
|
|
37
|
+
when :slave then @slave_session.config
|
|
38
|
+
else raise(ArgumentError, "Expected :master or :slave")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'abstract_session_proxy')
|
|
2
|
+
|
|
3
|
+
module Sunspot
|
|
4
|
+
module SessionProxy
|
|
5
|
+
#
|
|
6
|
+
# This is a generic abstract implementation of a session proxy that allows
|
|
7
|
+
# Sunspot to be used with a distributed (sharded) Solr deployment. Concrete
|
|
8
|
+
# subclasses should implement the #session_for method, which takes a
|
|
9
|
+
# searchable object and returns a Session that points to the appropriate
|
|
10
|
+
# Solr shard for that object. Subclasses should also implement the
|
|
11
|
+
# #all_sessions object, which returns the collection of all sharded Session
|
|
12
|
+
# objects.
|
|
13
|
+
#
|
|
14
|
+
# The class is initialized with a session that points to the Solr instance
|
|
15
|
+
# used to perform searches. Searches will have the +:shards+ param injected,
|
|
16
|
+
# containing references to the Solr instances returned by #all_sessions.
|
|
17
|
+
#
|
|
18
|
+
# For more on distributed search, see:
|
|
19
|
+
# http://wiki.apache.org/solr/DistributedSearch
|
|
20
|
+
#
|
|
21
|
+
# The following methods are not supported (although subclasses may in some
|
|
22
|
+
# cases be able to support them):
|
|
23
|
+
#
|
|
24
|
+
# * batch
|
|
25
|
+
# * config
|
|
26
|
+
# * remove_by_id
|
|
27
|
+
# * remove_by_id!
|
|
28
|
+
# * remove_all with an argument
|
|
29
|
+
# * remove_all! with an argument
|
|
30
|
+
#
|
|
31
|
+
class ShardingSessionProxy < AbstractSessionProxy
|
|
32
|
+
not_supported :batch, :config, :remove_by_id, :remove_by_id!
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# +search_session+ is the session that should be used for searching.
|
|
36
|
+
#
|
|
37
|
+
def initialize(search_session = Sunspot.session.new)
|
|
38
|
+
@search_session = search_session
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# Return the appropriate shard session for the object.
|
|
43
|
+
#
|
|
44
|
+
# <strong>Concrete subclasses must implement this method.</strong>
|
|
45
|
+
#
|
|
46
|
+
def session_for(object)
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# Return all shard sessions.
|
|
52
|
+
#
|
|
53
|
+
# <strong>Concrete subclasses must implement this method.</strong>
|
|
54
|
+
#
|
|
55
|
+
def all_sessions
|
|
56
|
+
raise NotImplementedError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# See Sunspot.index
|
|
61
|
+
#
|
|
62
|
+
def index(*objects)
|
|
63
|
+
using_sharded_session(objects) { |session, group| session.index(group) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# See Sunspot.index!
|
|
68
|
+
#
|
|
69
|
+
def index!(*objects)
|
|
70
|
+
using_sharded_session(objects) { |session, group| session.index!(group) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# See Sunspot.remove
|
|
75
|
+
#
|
|
76
|
+
def remove(*objects)
|
|
77
|
+
using_sharded_session(objects) { |session, group| session.remove(group) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# See Sunspot.remove!
|
|
82
|
+
#
|
|
83
|
+
def remove!(*objects)
|
|
84
|
+
using_sharded_session(objects) { |session, group| session.remove!(group) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
#
|
|
88
|
+
# If no argument is passed, behaves like Sunspot.remove_all
|
|
89
|
+
#
|
|
90
|
+
# If an argument is passed, will raise NotSupportedError, as the proxy
|
|
91
|
+
# does not know which session(s) to which to delegate this operation.
|
|
92
|
+
#
|
|
93
|
+
def remove_all(clazz = nil)
|
|
94
|
+
if clazz
|
|
95
|
+
raise NotSupportedError, "Sharding session proxy does not support remove_all with an argument."
|
|
96
|
+
else
|
|
97
|
+
all_sessions.each { |session| session.remove_all }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# If no argument is passed, behaves like Sunspot.remove_all!
|
|
103
|
+
#
|
|
104
|
+
# If an argument is passed, will raise NotSupportedError, as the proxy
|
|
105
|
+
# does not know which session(s) to which to delegate this operation.
|
|
106
|
+
#
|
|
107
|
+
def remove_all!(clazz = nil)
|
|
108
|
+
if clazz
|
|
109
|
+
raise NotSupportedError, "Sharding session proxy does not support remove_all! with an argument."
|
|
110
|
+
else
|
|
111
|
+
all_sessions.each { |session| session.remove_all! }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
# Commit all shards. See Sunspot.commit
|
|
117
|
+
#
|
|
118
|
+
def commit
|
|
119
|
+
all_sessions.each { |session| session.commit }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
#
|
|
123
|
+
# Optimize all shards. See Sunspot.optimize
|
|
124
|
+
#
|
|
125
|
+
def optimize
|
|
126
|
+
all_sessions.each { |session| session.optimize }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
#
|
|
130
|
+
# Commit all dirty sessions. Only dirty sessions will be committed.
|
|
131
|
+
#
|
|
132
|
+
# See Sunspot.commit_if_dirty
|
|
133
|
+
#
|
|
134
|
+
def commit_if_dirty
|
|
135
|
+
all_sessions.each { |session| session.commit_if_dirty }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#
|
|
139
|
+
# Commit all delete-dirty sessions. Only delete-dirty sessions will be
|
|
140
|
+
# committed.
|
|
141
|
+
#
|
|
142
|
+
# See Sunspot.commit_if_delete_dirty
|
|
143
|
+
#
|
|
144
|
+
def commit_if_delete_dirty
|
|
145
|
+
all_sessions.each { |session| session.commit_if_delete_dirty }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
#
|
|
149
|
+
# Instantiate a new Search object, but don't execute it. The search will
|
|
150
|
+
# have an extra :shards param injected into the query, which will tell the
|
|
151
|
+
# Solr instance referenced by the search session to search across all
|
|
152
|
+
# shards.
|
|
153
|
+
#
|
|
154
|
+
# See Sunspot.new_search
|
|
155
|
+
#
|
|
156
|
+
def new_search(*types)
|
|
157
|
+
shard_urls = all_sessions.map { |session| session.config.solr.url }
|
|
158
|
+
search = @search_session.new_search(*types)
|
|
159
|
+
search.build do
|
|
160
|
+
adjust_solr_params { |params| params[:shards] = shard_urls.join(',') }
|
|
161
|
+
# I feel a little dirty doing this.
|
|
162
|
+
end
|
|
163
|
+
search
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
#
|
|
167
|
+
# Build and execute a new Search. The search will have an extra :shards
|
|
168
|
+
# param injected into the query, which will tell the Solr instance
|
|
169
|
+
# referenced by the search session to search across all shards.
|
|
170
|
+
#
|
|
171
|
+
# See Sunspot.search
|
|
172
|
+
#
|
|
173
|
+
def search(*types, &block)
|
|
174
|
+
new_search(*types).execute
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def more_like_this(object, &block)
|
|
178
|
+
#FIXME should use shards
|
|
179
|
+
new_more_like_this(object, &block).execute
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def new_more_like_this(object, &block)
|
|
183
|
+
@search_session.new_more_like_this(object, &block)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
#
|
|
187
|
+
# True if any shard session is dirty. Note that directly using the
|
|
188
|
+
# #commit_if_dirty method is more efficient if that's what you're
|
|
189
|
+
# trying to do, since in that case only the dirty sessions are committed.
|
|
190
|
+
#
|
|
191
|
+
# See Sunspot.dirty?
|
|
192
|
+
#
|
|
193
|
+
def dirty?
|
|
194
|
+
all_sessions.any? { |session| session.dirty? }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
#
|
|
198
|
+
# True if any shard session is delete-dirty. Note that directly using the
|
|
199
|
+
# #commit_if_delete_dirty method is more efficient if that's what you're
|
|
200
|
+
# trying to do, since in that case only the delete-dirty sessions are
|
|
201
|
+
# committed.
|
|
202
|
+
#
|
|
203
|
+
def delete_dirty?
|
|
204
|
+
all_sessions.any? { |session| session.delete_dirty? }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
private
|
|
208
|
+
|
|
209
|
+
#
|
|
210
|
+
# Group the objects by which shard session they correspond to, and yield
|
|
211
|
+
# each session and is corresponding group of objects.
|
|
212
|
+
#
|
|
213
|
+
def using_sharded_session(objects)
|
|
214
|
+
grouped_objects = Hash.new { |h, k| h[k] = [] }
|
|
215
|
+
objects.flatten.each { |object| grouped_objects[session_for(object)] << object }
|
|
216
|
+
grouped_objects.each_pair do |session, group|
|
|
217
|
+
yield(session, group)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|