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,265 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
#
|
|
3
|
+
# Sunspot works by saving references to the primary key (or natural ID) of
|
|
4
|
+
# each indexed object, and then retrieving the objects from persistent storage
|
|
5
|
+
# when their IDs are referenced in search results. In order for Sunspot to
|
|
6
|
+
# know what an object's primary key is, and how to retrieve objects from
|
|
7
|
+
# persistent storage given a primary key, an adapter must be registered for
|
|
8
|
+
# that object's class or one of its superclasses (for instance, an adapter
|
|
9
|
+
# registered for ActiveRecord::Base would be used for all ActiveRecord
|
|
10
|
+
# models).
|
|
11
|
+
#
|
|
12
|
+
# To provide Sunspot with this ability, adapters must have two roles:
|
|
13
|
+
#
|
|
14
|
+
# Data accessor::
|
|
15
|
+
# A subclass of Sunspot::Adapters::DataAccessor, this object is instantiated
|
|
16
|
+
# with a particular class and must respond to the #load() method, which
|
|
17
|
+
# returns an object from persistent storage given that object's primary key.
|
|
18
|
+
# It can also optionally implement the #load_all() method, which returns
|
|
19
|
+
# a collection of objects given a collection of primary keys, if that can be
|
|
20
|
+
# done more efficiently than calling #load() on each key.
|
|
21
|
+
# Instance adapter::
|
|
22
|
+
# A subclass of Sunspot::Adapters::InstanceAdapter, this object is
|
|
23
|
+
# instantiated with a particular instance. Its only job is to tell Sunspot
|
|
24
|
+
# what the object's primary key is, by implementing the #id() method.
|
|
25
|
+
#
|
|
26
|
+
# Adapters are registered by registering their two components, telling Sunspot
|
|
27
|
+
# that they are available for one or more classes, and all of their
|
|
28
|
+
# subclasses. See Sunspot::Adapters::DataAccessor.register and
|
|
29
|
+
# Sunspot::Adapters::InstanceAdapter.register for the details.
|
|
30
|
+
#
|
|
31
|
+
# See spec/mocks/mock_adapter.rb for an example of how adapter classes should
|
|
32
|
+
# be implemented.
|
|
33
|
+
#
|
|
34
|
+
module Adapters
|
|
35
|
+
# Subclasses of the InstanceAdapter class should implement the #id method,
|
|
36
|
+
# which returns the primary key of the instance stored in the @instance
|
|
37
|
+
# variable. The primary key must be unique within the scope of the
|
|
38
|
+
# instance's class.
|
|
39
|
+
#
|
|
40
|
+
# ==== Example:
|
|
41
|
+
#
|
|
42
|
+
# class FileAdapter < Sunspot::Adapters::InstanceAdapter
|
|
43
|
+
# def id
|
|
44
|
+
# File.expand_path(@instance.path)
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# # then in your initializer
|
|
49
|
+
# Sunspot::Adapters::InstanceAdapter.register(MyAdapter, File)
|
|
50
|
+
#
|
|
51
|
+
class InstanceAdapter
|
|
52
|
+
def initialize(instance) #:nodoc:
|
|
53
|
+
@instance = instance
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# The universally-unique ID for this instance that will be stored in solr
|
|
58
|
+
#
|
|
59
|
+
# ==== Returns
|
|
60
|
+
#
|
|
61
|
+
# String:: ID for use in Solr
|
|
62
|
+
#
|
|
63
|
+
def index_id #:nodoc:
|
|
64
|
+
InstanceAdapter.index_id_for(@instance.class.name, id)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class <<self
|
|
68
|
+
# Instantiate an InstanceAdapter for the given object, searching for
|
|
69
|
+
# registered adapters for the object's class.
|
|
70
|
+
#
|
|
71
|
+
# ==== Parameters
|
|
72
|
+
#
|
|
73
|
+
# instance<Object>:: The instance to adapt
|
|
74
|
+
#
|
|
75
|
+
# ==== Returns
|
|
76
|
+
#
|
|
77
|
+
# InstanceAdapter::
|
|
78
|
+
# An instance of an InstanceAdapter implementation that
|
|
79
|
+
# wraps the given instance
|
|
80
|
+
#
|
|
81
|
+
def adapt(instance) #:nodoc:
|
|
82
|
+
self.for(instance.class).new(instance)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Register an instance adapter for a set of classes. When searching for
|
|
86
|
+
# an adapter for a given instance, Sunspot starts with the instance's
|
|
87
|
+
# class, and then searches for registered adapters up the class's
|
|
88
|
+
# ancestor chain.
|
|
89
|
+
#
|
|
90
|
+
# ==== Parameters
|
|
91
|
+
#
|
|
92
|
+
# instance_adapter<Class>:: The instance adapter class to register
|
|
93
|
+
# classes...<Class>::
|
|
94
|
+
# One or more classes that this instance adapter adapts
|
|
95
|
+
#
|
|
96
|
+
def register(instance_adapter, *classes)
|
|
97
|
+
classes.each do |clazz|
|
|
98
|
+
instance_adapters[clazz.name.to_sym] = instance_adapter
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Find the best InstanceAdapter implementation that adapts the given
|
|
103
|
+
# class. Starting with the class and then moving up the ancestor chain,
|
|
104
|
+
# looks for registered InstanceAdapter implementations.
|
|
105
|
+
#
|
|
106
|
+
# ==== Parameters
|
|
107
|
+
#
|
|
108
|
+
# clazz<Class>:: The class to find an InstanceAdapter for
|
|
109
|
+
#
|
|
110
|
+
# ==== Returns
|
|
111
|
+
#
|
|
112
|
+
# Class:: Subclass of InstanceAdapter, or nil if none found
|
|
113
|
+
#
|
|
114
|
+
# ==== Raises
|
|
115
|
+
#
|
|
116
|
+
# Sunspot::NoAdapterError:: If no adapter is registered for this class
|
|
117
|
+
#
|
|
118
|
+
def for(clazz) #:nodoc:
|
|
119
|
+
original_class_name = clazz.name
|
|
120
|
+
clazz.ancestors.each do |ancestor_class|
|
|
121
|
+
next if ancestor_class.name.nil? || ancestor_class.name.empty?
|
|
122
|
+
class_name = ancestor_class.name.to_sym
|
|
123
|
+
return instance_adapters[class_name] if instance_adapters[class_name]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
raise(Sunspot::NoAdapterError,
|
|
127
|
+
"No adapter is configured for #{original_class_name} or its superclasses. See the documentation for Sunspot::Adapters")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def index_id_for(class_name, id) #:nodoc:
|
|
131
|
+
"#{class_name} #{id}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
protected
|
|
135
|
+
|
|
136
|
+
# Lazy-initialize the hash of registered instance adapters
|
|
137
|
+
#
|
|
138
|
+
# ==== Returns
|
|
139
|
+
#
|
|
140
|
+
# Hash:: Hash containing class names keyed to instance adapter classes
|
|
141
|
+
#
|
|
142
|
+
def instance_adapters #:nodoc:
|
|
143
|
+
@instance_adapters ||= {}
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Subclasses of the DataAccessor class take care of retreiving instances of
|
|
149
|
+
# the adapted class from (usually persistent) storage. Subclasses must
|
|
150
|
+
# implement the #load method, which takes an id (the value returned by
|
|
151
|
+
# InstanceAdapter#id, as a string), and returns the instance referenced by
|
|
152
|
+
# that ID. Optionally, it can also override the #load_all method, which
|
|
153
|
+
# takes an array of IDs and returns an array of instances in the order
|
|
154
|
+
# given. #load_all need only be implemented if it can be done more
|
|
155
|
+
# efficiently than simply iterating over the IDs and calling #load on each
|
|
156
|
+
# individually.
|
|
157
|
+
#
|
|
158
|
+
# ==== Example
|
|
159
|
+
#
|
|
160
|
+
# class FileAccessor < Sunspot::Adapters::InstanceAdapter
|
|
161
|
+
# def load(id)
|
|
162
|
+
# @clazz.open(id)
|
|
163
|
+
# end
|
|
164
|
+
# end
|
|
165
|
+
#
|
|
166
|
+
# Sunspot::Adapters::DataAccessor.register(FileAccessor, File)
|
|
167
|
+
#
|
|
168
|
+
class DataAccessor
|
|
169
|
+
def initialize(clazz) #:nodoc:
|
|
170
|
+
@clazz = clazz
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Subclasses can override this class to provide more efficient bulk
|
|
174
|
+
# loading of instances. Instances must be returned in the same order
|
|
175
|
+
# that the IDs were given.
|
|
176
|
+
#
|
|
177
|
+
# ==== Parameters
|
|
178
|
+
#
|
|
179
|
+
# ids<Array>:: collection of IDs
|
|
180
|
+
#
|
|
181
|
+
# ==== Returns
|
|
182
|
+
#
|
|
183
|
+
# Array:: collection of instances, in order of IDs given
|
|
184
|
+
#
|
|
185
|
+
def load_all(ids)
|
|
186
|
+
ids.map { |id| self.load(id) }
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
class <<self
|
|
190
|
+
# Create a DataAccessor for the given class, searching registered
|
|
191
|
+
# adapters for the best match. See InstanceAdapter#adapt for discussion
|
|
192
|
+
# of inheritence.
|
|
193
|
+
#
|
|
194
|
+
# ==== Parameters
|
|
195
|
+
#
|
|
196
|
+
# clazz<Class>:: Class to create DataAccessor for
|
|
197
|
+
#
|
|
198
|
+
# ==== Returns
|
|
199
|
+
#
|
|
200
|
+
# DataAccessor::
|
|
201
|
+
# DataAccessor implementation which provides access to given class
|
|
202
|
+
#
|
|
203
|
+
def create(clazz) #:nodoc:
|
|
204
|
+
self.for(clazz).new(clazz)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Register data accessor for a set of classes. When searching for
|
|
208
|
+
# an accessor for a given class, Sunspot starts with the class,
|
|
209
|
+
# and then searches for registered adapters up the class's ancestor
|
|
210
|
+
# chain.
|
|
211
|
+
#
|
|
212
|
+
# ==== Parameters
|
|
213
|
+
#
|
|
214
|
+
# data_accessor<Class>:: The data accessor class to register
|
|
215
|
+
# classes...<Class>::
|
|
216
|
+
# One or more classes that this data accessor providess access to
|
|
217
|
+
#
|
|
218
|
+
def register(data_accessor, *classes)
|
|
219
|
+
classes.each do |clazz|
|
|
220
|
+
data_accessors[clazz.name.to_sym] = data_accessor
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Find the best DataAccessor implementation that adapts the given class.
|
|
225
|
+
# Starting with the class and then moving up the ancestor chain, looks
|
|
226
|
+
# for registered DataAccessor implementations.
|
|
227
|
+
#
|
|
228
|
+
# ==== Parameters
|
|
229
|
+
#
|
|
230
|
+
# clazz<Class>:: The class to find a DataAccessor for
|
|
231
|
+
#
|
|
232
|
+
# ==== Returns
|
|
233
|
+
#
|
|
234
|
+
# Class:: Implementation of DataAccessor
|
|
235
|
+
#
|
|
236
|
+
# ==== Raises
|
|
237
|
+
#
|
|
238
|
+
# Sunspot::NoAdapterError:: If no data accessor exists for the given class
|
|
239
|
+
#
|
|
240
|
+
def for(clazz) #:nodoc:
|
|
241
|
+
original_class_name = clazz.name
|
|
242
|
+
clazz.ancestors.each do |ancestor_class|
|
|
243
|
+
next if ancestor_class.name.nil? || ancestor_class.name.empty?
|
|
244
|
+
class_name = ancestor_class.name.to_sym
|
|
245
|
+
return data_accessors[class_name] if data_accessors[class_name]
|
|
246
|
+
end
|
|
247
|
+
raise(Sunspot::NoAdapterError,
|
|
248
|
+
"No data accessor is configured for #{original_class_name} or its superclasses. See the documentation for Sunspot::Adapters")
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
protected
|
|
252
|
+
|
|
253
|
+
# Lazy-initialize the hash of registered data accessors
|
|
254
|
+
#
|
|
255
|
+
# ==== Returns
|
|
256
|
+
#
|
|
257
|
+
# Hash:: Hash containing class names keyed to data accessor classes
|
|
258
|
+
#
|
|
259
|
+
def data_accessors #:nodoc:
|
|
260
|
+
@adapters ||= {}
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
#
|
|
3
|
+
# The CompositeSetup class encapsulates a collection of setups, and responds
|
|
4
|
+
# to a subset of the methods that Setup responds to (in particular, the
|
|
5
|
+
# methods required to build queries).
|
|
6
|
+
#
|
|
7
|
+
class CompositeSetup #:nodoc:
|
|
8
|
+
class << self
|
|
9
|
+
alias_method :for, :new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(types)
|
|
13
|
+
@types = types
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Collection of Setup objects for the enclosed types
|
|
18
|
+
#
|
|
19
|
+
# ==== Returns
|
|
20
|
+
#
|
|
21
|
+
# Array:: Collection of Setup objects
|
|
22
|
+
#
|
|
23
|
+
def setups
|
|
24
|
+
@setups ||= @types.map { |type| Setup.for(type) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Return the names of the encapsulated types
|
|
29
|
+
#
|
|
30
|
+
# ==== Returns
|
|
31
|
+
#
|
|
32
|
+
# Array:: Collection of class names
|
|
33
|
+
#
|
|
34
|
+
def type_names
|
|
35
|
+
@type_names ||= @types.map { |clazz| clazz.name }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Get a text field object by its public name. A field will be returned if
|
|
40
|
+
# it is configured for any of the enclosed types.
|
|
41
|
+
#
|
|
42
|
+
# ==== Returns
|
|
43
|
+
#
|
|
44
|
+
# Sunspot::FulltextField:: Text field with the given public name
|
|
45
|
+
#
|
|
46
|
+
# ==== Raises
|
|
47
|
+
#
|
|
48
|
+
# UnrecognizedFieldError::
|
|
49
|
+
# If no field with that name is configured for any of the enclosed types.
|
|
50
|
+
#
|
|
51
|
+
def text_fields(field_name)
|
|
52
|
+
if text_fields = text_fields_hash[field_name.to_sym]
|
|
53
|
+
text_fields.to_a
|
|
54
|
+
else
|
|
55
|
+
raise(
|
|
56
|
+
UnrecognizedFieldError,
|
|
57
|
+
"No text field configured for #{@types * ', '} with name '#{field_name}'"
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#
|
|
63
|
+
# Get a Sunspot::AttributeField instance corresponding to the given field name
|
|
64
|
+
#
|
|
65
|
+
# ==== Parameters
|
|
66
|
+
#
|
|
67
|
+
# field_name<Symbol>:: The public field name for which to find a field
|
|
68
|
+
#
|
|
69
|
+
# ==== Returns
|
|
70
|
+
#
|
|
71
|
+
# Sunspot::AttributeField The field object corresponding to the given name
|
|
72
|
+
#
|
|
73
|
+
# ==== Raises
|
|
74
|
+
#
|
|
75
|
+
# ArgumentError::
|
|
76
|
+
# If the given field name is not configured for the types being queried
|
|
77
|
+
#
|
|
78
|
+
def field(field_name) #:nodoc:
|
|
79
|
+
fields_hash[field_name.to_sym] || raise(
|
|
80
|
+
UnrecognizedFieldError,
|
|
81
|
+
"No field configured for #{@types * ', '} with name '#{field_name}'"
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# Get a dynamic field factory for the given base name.
|
|
87
|
+
#
|
|
88
|
+
# ==== Returns
|
|
89
|
+
#
|
|
90
|
+
# DynamicFieldFactory:: Factory for dynamic fields with the given base name
|
|
91
|
+
#
|
|
92
|
+
# ==== Raises
|
|
93
|
+
#
|
|
94
|
+
# UnrecognizedFieldError::
|
|
95
|
+
# If the given base name is not configured as a dynamic field for the types being queried
|
|
96
|
+
#
|
|
97
|
+
def dynamic_field_factory(field_name)
|
|
98
|
+
dynamic_field_factories_hash[field_name.to_sym] || raise(
|
|
99
|
+
UnrecognizedFieldError,
|
|
100
|
+
"No dynamic field configured for #{@types * ', '} with name #{field_name.inspect}"
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# Collection of all text fields configured for any of the enclosed types.
|
|
106
|
+
#
|
|
107
|
+
# === Returns
|
|
108
|
+
#
|
|
109
|
+
# Array:: Text fields configured for the enclosed types
|
|
110
|
+
#
|
|
111
|
+
def all_text_fields
|
|
112
|
+
@text_fields ||= text_fields_hash.values.map { |set| set.to_a }.flatten
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def all_more_like_this_fields
|
|
116
|
+
@more_like_this_fields ||= more_like_this_fields_hash.values.map { |set| set.to_a }.flatten
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
#
|
|
122
|
+
# Return a hash of field names to text field objects, containing all fields
|
|
123
|
+
# that are configured for any of the types enclosed.
|
|
124
|
+
#
|
|
125
|
+
# ==== Returns
|
|
126
|
+
#
|
|
127
|
+
# Hash:: Hash of field names to text field objects.
|
|
128
|
+
#
|
|
129
|
+
def text_fields_hash
|
|
130
|
+
@text_fields_hash ||=
|
|
131
|
+
setups.inject({}) do |hash, setup|
|
|
132
|
+
setup.all_text_fields.each do |text_field|
|
|
133
|
+
(hash[text_field.name] ||= Set.new) << text_field
|
|
134
|
+
end
|
|
135
|
+
hash
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def more_like_this_fields_hash
|
|
140
|
+
@more_like_this_fields_hash ||=
|
|
141
|
+
setups.inject({}) do |hash, setup|
|
|
142
|
+
setup.all_more_like_this_fields.each do |more_like_this_field|
|
|
143
|
+
(hash[more_like_this_field.name] ||= Set.new) << more_like_this_field
|
|
144
|
+
end
|
|
145
|
+
hash
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# Return a hash of field names to field objects, containing all fields
|
|
151
|
+
# that are common to all of the classes enclosed. In order for fields
|
|
152
|
+
# to be common, they must be of the same type and have the same
|
|
153
|
+
# value for allow_multiple? and stored?. This method is memoized.
|
|
154
|
+
#
|
|
155
|
+
# ==== Returns
|
|
156
|
+
#
|
|
157
|
+
# Hash:: field names keyed to field objects
|
|
158
|
+
#
|
|
159
|
+
def fields_hash
|
|
160
|
+
@fields_hash ||=
|
|
161
|
+
begin
|
|
162
|
+
field_sets_hash = Hash.new { |h, k| h[k] = Set.new }
|
|
163
|
+
@types.each do |type|
|
|
164
|
+
Setup.for(type).fields.each do |field|
|
|
165
|
+
field_sets_hash[field.name.to_sym] << field
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
fields_hash = {}
|
|
169
|
+
field_sets_hash.each_pair do |field_name, set|
|
|
170
|
+
if set.length == 1
|
|
171
|
+
fields_hash[field_name] = set.to_a.first
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
fields_hash
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
#
|
|
179
|
+
# Return a hash of dynamic field base names to dynamic field factories for
|
|
180
|
+
# those base names. Criteria for the inclusion are the same as for
|
|
181
|
+
# #fields_hash()
|
|
182
|
+
#
|
|
183
|
+
def dynamic_field_factories_hash
|
|
184
|
+
@dynamic_field_factories_hash ||=
|
|
185
|
+
begin
|
|
186
|
+
dynamic_field_factories_hash = @types.inject({}) do |hash, type|
|
|
187
|
+
Setup.for(type).dynamic_field_factories.each do |field_factory|
|
|
188
|
+
(hash[field_factory.name.to_sym] ||= {})[type.name] = field_factory
|
|
189
|
+
end
|
|
190
|
+
hash
|
|
191
|
+
end
|
|
192
|
+
dynamic_field_factories_hash.each_pair do |field_name, field_configurations_hash|
|
|
193
|
+
if @types.any? { |type| field_configurations_hash[type.name].nil? }
|
|
194
|
+
dynamic_field_factories_hash.delete(field_name)
|
|
195
|
+
else
|
|
196
|
+
dynamic_field_factories_hash[field_name] = field_configurations_hash.values.first
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
# The Sunspot::Configuration module provides a factory method for Sunspot
|
|
3
|
+
# configuration objects. Available properties are:
|
|
4
|
+
#
|
|
5
|
+
# Sunspot.config.solr.url::
|
|
6
|
+
# The URL at which to connect to Solr
|
|
7
|
+
# (default: 'http://localhost:8983/solr')
|
|
8
|
+
# Sunspot.config.pagination.default_per_page::
|
|
9
|
+
# Solr always paginates its results. This sets Sunspot's default result
|
|
10
|
+
# count per page if it is not explicitly specified in the query.
|
|
11
|
+
#
|
|
12
|
+
module Configuration
|
|
13
|
+
class <<self
|
|
14
|
+
# Factory method to build configuration instances.
|
|
15
|
+
#
|
|
16
|
+
# ==== Returns
|
|
17
|
+
#
|
|
18
|
+
# LightConfig::Configuration:: new configuration instance with defaults
|
|
19
|
+
#
|
|
20
|
+
def build #:nodoc:
|
|
21
|
+
LightConfig.build do
|
|
22
|
+
solr do
|
|
23
|
+
url 'http://127.0.0.1:8983/solr'
|
|
24
|
+
end
|
|
25
|
+
master_solr do
|
|
26
|
+
url nil
|
|
27
|
+
end
|
|
28
|
+
pagination do
|
|
29
|
+
default_per_page 30
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Location for the default solr configuration files,
|
|
35
|
+
# required for bootstrapping a new solr installation
|
|
36
|
+
#
|
|
37
|
+
# ==== Returns
|
|
38
|
+
#
|
|
39
|
+
# String:: Directory with default solr config files
|
|
40
|
+
#
|
|
41
|
+
def solr_default_configuration_location
|
|
42
|
+
File.join( File.dirname(__FILE__), '../../solr/solr/conf' )
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
#
|
|
3
|
+
# DataExtractors present an internal API for the indexer to use to extract
|
|
4
|
+
# field values from models for indexing. They must implement the #value_for
|
|
5
|
+
# method, which takes an object and returns the value extracted from it.
|
|
6
|
+
#
|
|
7
|
+
module DataExtractor #:nodoc: all
|
|
8
|
+
#
|
|
9
|
+
# AttributeExtractors extract data by simply calling a method on the block.
|
|
10
|
+
#
|
|
11
|
+
class AttributeExtractor
|
|
12
|
+
def initialize(attribute_name)
|
|
13
|
+
@attribute_name = attribute_name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def value_for(object)
|
|
17
|
+
object.send(@attribute_name)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# BlockExtractors extract data by evaluating a block in the context of the
|
|
23
|
+
# object instance, or if the block takes an argument, by passing the object
|
|
24
|
+
# as the argument to the block. Either way, the return value of the block is
|
|
25
|
+
# the value returned by the extractor.
|
|
26
|
+
#
|
|
27
|
+
class BlockExtractor
|
|
28
|
+
def initialize(&block)
|
|
29
|
+
@block = block
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def value_for(object)
|
|
33
|
+
Util.instance_eval_or_call(object, &@block)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Constant data extractors simply return the same value for every object.
|
|
39
|
+
#
|
|
40
|
+
class Constant
|
|
41
|
+
def initialize(value)
|
|
42
|
+
@value = value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def value_for(object)
|
|
46
|
+
@value
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module DSL #:nodoc:
|
|
3
|
+
module Adjustable #:nodoc
|
|
4
|
+
# <strong>Expert:</strong> Adjust or reset the parameters passed to Solr.
|
|
5
|
+
# The adjustment will take place just before sending the params to solr,
|
|
6
|
+
# after Sunspot builds the Solr params based on the methods called in the
|
|
7
|
+
# DSL.
|
|
8
|
+
#
|
|
9
|
+
# Under normal circumstances, using this method should not be necessary;
|
|
10
|
+
# if you find that it is, please consider submitting a feature request.
|
|
11
|
+
# Using this method requires knowledge of Sunspot's internal Solr schema
|
|
12
|
+
# and Solr query representations, which are not part of Sunspot's public
|
|
13
|
+
# API; they could change at any time. <strong>This method is unsupported
|
|
14
|
+
# and your mileage may vary.</strong>
|
|
15
|
+
#
|
|
16
|
+
# ==== Examples
|
|
17
|
+
#
|
|
18
|
+
# Sunspot.search(Post) do
|
|
19
|
+
# adjust_solr_params do |params|
|
|
20
|
+
# params[:q] += ' AND something_s:more'
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# Sunspot.more_like_this(my_post) do
|
|
25
|
+
# adjust_solr_params do |params|
|
|
26
|
+
# params["mlt.match.include"] = true
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
def adjust_solr_params( &block )
|
|
31
|
+
@query.solr_parameter_adjustment = block
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# <strong>Expert:</strong> Use a custom request handler for this search.
|
|
36
|
+
# The general use case for this would be a request handler configuration
|
|
37
|
+
# you've defined in solrconfig that has different search components,
|
|
38
|
+
# defaults, etc. Using this to point at an entirely different type of
|
|
39
|
+
# request handler that Sunspot doesn't support probably won't get you very
|
|
40
|
+
# far.
|
|
41
|
+
#
|
|
42
|
+
def request_handler(request_handler)
|
|
43
|
+
@search.request_handler = request_handler
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|