lisausa-sunspot 1.2.1

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.
Files changed (217) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +4 -0
  3. data/History.txt +222 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +17 -0
  6. data/TODO +13 -0
  7. data/VERSION.yml +4 -0
  8. data/bin/sunspot-installer +19 -0
  9. data/bin/sunspot-solr +74 -0
  10. data/installer/config/schema.yml +95 -0
  11. data/lib/light_config.rb +40 -0
  12. data/lib/sunspot.rb +569 -0
  13. data/lib/sunspot/adapters.rb +265 -0
  14. data/lib/sunspot/composite_setup.rb +202 -0
  15. data/lib/sunspot/configuration.rb +46 -0
  16. data/lib/sunspot/data_extractor.rb +50 -0
  17. data/lib/sunspot/dsl.rb +5 -0
  18. data/lib/sunspot/dsl/adjustable.rb +47 -0
  19. data/lib/sunspot/dsl/field_query.rb +279 -0
  20. data/lib/sunspot/dsl/fields.rb +103 -0
  21. data/lib/sunspot/dsl/fulltext.rb +243 -0
  22. data/lib/sunspot/dsl/function.rb +14 -0
  23. data/lib/sunspot/dsl/functional.rb +44 -0
  24. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  25. data/lib/sunspot/dsl/paginatable.rb +28 -0
  26. data/lib/sunspot/dsl/query_facet.rb +36 -0
  27. data/lib/sunspot/dsl/restriction.rb +25 -0
  28. data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
  29. data/lib/sunspot/dsl/scope.rb +217 -0
  30. data/lib/sunspot/dsl/search.rb +30 -0
  31. data/lib/sunspot/dsl/standard_query.rb +121 -0
  32. data/lib/sunspot/field.rb +193 -0
  33. data/lib/sunspot/field_factory.rb +129 -0
  34. data/lib/sunspot/indexer.rb +131 -0
  35. data/lib/sunspot/installer.rb +31 -0
  36. data/lib/sunspot/installer/library_installer.rb +45 -0
  37. data/lib/sunspot/installer/schema_builder.rb +219 -0
  38. data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
  39. data/lib/sunspot/installer/task_helper.rb +18 -0
  40. data/lib/sunspot/query.rb +11 -0
  41. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  42. data/lib/sunspot/query/boost_query.rb +24 -0
  43. data/lib/sunspot/query/common_query.rb +85 -0
  44. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  45. data/lib/sunspot/query/connective.rb +206 -0
  46. data/lib/sunspot/query/date_field_facet.rb +14 -0
  47. data/lib/sunspot/query/dismax.rb +128 -0
  48. data/lib/sunspot/query/field_facet.rb +41 -0
  49. data/lib/sunspot/query/filter.rb +38 -0
  50. data/lib/sunspot/query/function_query.rb +52 -0
  51. data/lib/sunspot/query/geo.rb +53 -0
  52. data/lib/sunspot/query/highlighting.rb +67 -0
  53. data/lib/sunspot/query/more_like_this.rb +61 -0
  54. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  55. data/lib/sunspot/query/pagination.rb +38 -0
  56. data/lib/sunspot/query/query_facet.rb +16 -0
  57. data/lib/sunspot/query/restriction.rb +262 -0
  58. data/lib/sunspot/query/scope.rb +9 -0
  59. data/lib/sunspot/query/sort.rb +95 -0
  60. data/lib/sunspot/query/sort_composite.rb +33 -0
  61. data/lib/sunspot/query/standard_query.rb +16 -0
  62. data/lib/sunspot/query/text_field_boost.rb +17 -0
  63. data/lib/sunspot/schema.rb +151 -0
  64. data/lib/sunspot/search.rb +9 -0
  65. data/lib/sunspot/search/abstract_search.rb +293 -0
  66. data/lib/sunspot/search/date_facet.rb +35 -0
  67. data/lib/sunspot/search/facet_row.rb +27 -0
  68. data/lib/sunspot/search/field_facet.rb +88 -0
  69. data/lib/sunspot/search/highlight.rb +38 -0
  70. data/lib/sunspot/search/hit.rb +136 -0
  71. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  72. data/lib/sunspot/search/paginated_collection.rb +55 -0
  73. data/lib/sunspot/search/query_facet.rb +67 -0
  74. data/lib/sunspot/search/standard_search.rb +21 -0
  75. data/lib/sunspot/server.rb +152 -0
  76. data/lib/sunspot/session.rb +260 -0
  77. data/lib/sunspot/session_proxy.rb +87 -0
  78. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  79. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  80. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  81. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  82. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  83. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  84. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  85. data/lib/sunspot/setup.rb +350 -0
  86. data/lib/sunspot/text_field_setup.rb +29 -0
  87. data/lib/sunspot/type.rb +372 -0
  88. data/lib/sunspot/util.rb +243 -0
  89. data/lib/sunspot/version.rb +3 -0
  90. data/script/console +10 -0
  91. data/solr-1.3/etc/jetty.xml +212 -0
  92. data/solr-1.3/etc/webdefault.xml +379 -0
  93. data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
  94. data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
  95. data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  96. data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
  97. data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
  98. data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  99. data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
  100. data/solr-1.3/solr/conf/elevate.xml +36 -0
  101. data/solr-1.3/solr/conf/protwords.txt +21 -0
  102. data/solr-1.3/solr/conf/schema.xml +64 -0
  103. data/solr-1.3/solr/conf/solrconfig.xml +725 -0
  104. data/solr-1.3/solr/conf/stopwords.txt +57 -0
  105. data/solr-1.3/solr/conf/synonyms.txt +31 -0
  106. data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  107. data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  108. data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
  109. data/solr-1.3/solr/lib/locallucene.jar +0 -0
  110. data/solr-1.3/solr/lib/localsolr.jar +0 -0
  111. data/solr-1.3/start.jar +0 -0
  112. data/solr-1.3/webapps/solr.war +0 -0
  113. data/solr/README.txt +42 -0
  114. data/solr/etc/jetty.xml +218 -0
  115. data/solr/etc/webdefault.xml +379 -0
  116. data/solr/lib/jetty-6.1.3.jar +0 -0
  117. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  118. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  119. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  120. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  121. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  122. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  123. data/solr/solr/.gitignore +1 -0
  124. data/solr/solr/README.txt +54 -0
  125. data/solr/solr/conf/admin-extra.html +31 -0
  126. data/solr/solr/conf/elevate.xml +36 -0
  127. data/solr/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  128. data/solr/solr/conf/protwords.txt +21 -0
  129. data/solr/solr/conf/schema.xml +238 -0
  130. data/solr/solr/conf/scripts.conf +24 -0
  131. data/solr/solr/conf/solrconfig.xml +934 -0
  132. data/solr/solr/conf/spellings.txt +2 -0
  133. data/solr/solr/conf/stopwords.txt +58 -0
  134. data/solr/solr/conf/synonyms.txt +31 -0
  135. data/solr/solr/conf/xslt/example.xsl +132 -0
  136. data/solr/solr/conf/xslt/example_atom.xsl +67 -0
  137. data/solr/solr/conf/xslt/example_rss.xsl +66 -0
  138. data/solr/solr/conf/xslt/luke.xsl +337 -0
  139. data/solr/start.jar +0 -0
  140. data/solr/webapps/solr.war +0 -0
  141. data/spec/api/adapters_spec.rb +33 -0
  142. data/spec/api/binding_spec.rb +50 -0
  143. data/spec/api/indexer/attributes_spec.rb +149 -0
  144. data/spec/api/indexer/batch_spec.rb +46 -0
  145. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  146. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  147. data/spec/api/indexer/fulltext_spec.rb +43 -0
  148. data/spec/api/indexer/removal_spec.rb +53 -0
  149. data/spec/api/indexer/spec_helper.rb +1 -0
  150. data/spec/api/indexer_spec.rb +14 -0
  151. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  152. data/spec/api/query/connectives_examples.rb +189 -0
  153. data/spec/api/query/dsl_spec.rb +18 -0
  154. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  155. data/spec/api/query/faceting_examples.rb +397 -0
  156. data/spec/api/query/fulltext_examples.rb +313 -0
  157. data/spec/api/query/function_spec.rb +70 -0
  158. data/spec/api/query/geo_examples.rb +68 -0
  159. data/spec/api/query/highlighting_examples.rb +223 -0
  160. data/spec/api/query/more_like_this_spec.rb +140 -0
  161. data/spec/api/query/ordering_pagination_examples.rb +95 -0
  162. data/spec/api/query/scope_examples.rb +275 -0
  163. data/spec/api/query/spec_helper.rb +1 -0
  164. data/spec/api/query/standard_spec.rb +28 -0
  165. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  166. data/spec/api/query/types_spec.rb +20 -0
  167. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  168. data/spec/api/search/faceting_spec.rb +360 -0
  169. data/spec/api/search/highlighting_spec.rb +69 -0
  170. data/spec/api/search/hits_spec.rb +120 -0
  171. data/spec/api/search/paginated_collection_spec.rb +26 -0
  172. data/spec/api/search/results_spec.rb +66 -0
  173. data/spec/api/search/search_spec.rb +23 -0
  174. data/spec/api/search/spec_helper.rb +1 -0
  175. data/spec/api/server_spec.rb +91 -0
  176. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  177. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  178. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  179. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  180. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  181. data/spec/api/session_proxy/spec_helper.rb +9 -0
  182. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +50 -0
  183. data/spec/api/session_spec.rb +220 -0
  184. data/spec/api/spec_helper.rb +3 -0
  185. data/spec/api/sunspot_spec.rb +18 -0
  186. data/spec/ext.rb +11 -0
  187. data/spec/helpers/indexer_helper.rb +29 -0
  188. data/spec/helpers/query_helper.rb +38 -0
  189. data/spec/helpers/search_helper.rb +80 -0
  190. data/spec/integration/dynamic_fields_spec.rb +55 -0
  191. data/spec/integration/faceting_spec.rb +238 -0
  192. data/spec/integration/highlighting_spec.rb +22 -0
  193. data/spec/integration/indexing_spec.rb +33 -0
  194. data/spec/integration/keyword_search_spec.rb +317 -0
  195. data/spec/integration/local_search_spec.rb +64 -0
  196. data/spec/integration/more_like_this_spec.rb +43 -0
  197. data/spec/integration/scoped_search_spec.rb +354 -0
  198. data/spec/integration/spec_helper.rb +7 -0
  199. data/spec/integration/stored_fields_spec.rb +10 -0
  200. data/spec/integration/test_pagination.rb +32 -0
  201. data/spec/mocks/adapters.rb +32 -0
  202. data/spec/mocks/blog.rb +3 -0
  203. data/spec/mocks/comment.rb +21 -0
  204. data/spec/mocks/connection.rb +126 -0
  205. data/spec/mocks/mock_adapter.rb +30 -0
  206. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  207. data/spec/mocks/mock_record.rb +52 -0
  208. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  209. data/spec/mocks/photo.rb +11 -0
  210. data/spec/mocks/post.rb +85 -0
  211. data/spec/mocks/super_class.rb +2 -0
  212. data/spec/mocks/user.rb +13 -0
  213. data/spec/spec_helper.rb +30 -0
  214. data/tasks/rdoc.rake +27 -0
  215. data/tasks/schema.rake +19 -0
  216. data/tasks/todo.rake +4 -0
  217. metadata +457 -0
@@ -0,0 +1,193 @@
1
+ module Sunspot
2
+ class Field #:nodoc:
3
+ attr_accessor :name # The public-facing name of the field
4
+ attr_accessor :type # The Type of the field
5
+ attr_accessor :reference # Model class that the value of this field refers to
6
+ attr_reader :boost
7
+ attr_reader :indexed_name # Name with which this field is indexed internally. Based on public name and type or the +:as+ option.
8
+
9
+ #
10
+ #
11
+ def initialize(name, type, options = {}) #:nodoc
12
+ @name, @type = name.to_sym, type
13
+ @stored = !!options.delete(:stored)
14
+ @more_like_this = !!options.delete(:more_like_this)
15
+ set_indexed_name(options)
16
+ raise ArgumentError, "Field of type #{type} cannot be used for more_like_this" unless type.accepts_more_like_this? or !@more_like_this
17
+ end
18
+
19
+ # Convert a value to its representation for Solr indexing. This delegates
20
+ # to the #to_indexed method on the field's type.
21
+ #
22
+ # ==== Parameters
23
+ #
24
+ # value<Object>:: Value to convert to Solr representation
25
+ #
26
+ # ==== Returns
27
+ #
28
+ # String:: Solr representation of the object
29
+ #
30
+ # ==== Raises
31
+ #
32
+ # ArgumentError::
33
+ # the value is an array, but this field does not allow multiple values
34
+ #
35
+ def to_indexed(value)
36
+ if value.is_a? Array
37
+ if @multiple
38
+ value.map { |val| to_indexed(val) }
39
+ else
40
+ raise ArgumentError, "#{name} is not a multiple-value field, so it cannot index values #{value.inspect}"
41
+ end
42
+ else
43
+ @type.to_indexed(value)
44
+ end
45
+ end
46
+
47
+ # Cast the value into the appropriate Ruby class for the field's type
48
+ #
49
+ # ==== Parameters
50
+ #
51
+ # value<String>:: Solr's representation of the value
52
+ #
53
+ # ==== Returns
54
+ #
55
+ # Object:: The cast value
56
+ #
57
+ def cast(value)
58
+ @type.cast(value)
59
+ end
60
+
61
+ #
62
+ # Whether this field accepts multiple values.
63
+ #
64
+ # ==== Returns
65
+ #
66
+ # Boolean:: True if this field accepts multiple values.
67
+ #
68
+ def multiple?
69
+ !!@multiple
70
+ end
71
+
72
+ #
73
+ # Whether this field can be used for more_like_this queries.
74
+ # If true, the field is configured to store termVectors.
75
+ #
76
+ # ==== Returns
77
+ #
78
+ # Boolean:: True if this field can be used for more_like_this queries.
79
+ #
80
+ def more_like_this?
81
+ !!@more_like_this
82
+ end
83
+
84
+ def hash
85
+ indexed_name.hash
86
+ end
87
+
88
+ def eql?(field)
89
+ indexed_name == field.indexed_name
90
+ end
91
+ alias_method :==, :eql?
92
+
93
+ private
94
+
95
+ #
96
+ # Determine the indexed name. If the :as option is given use that, otherwise
97
+ # create the value based on the indexed_name of the type with additional
98
+ # suffixes for multiple, stored, and more_like_this.
99
+ #
100
+ # ==== Returns
101
+ #
102
+ # String: The field's indexed name
103
+ #
104
+ def set_indexed_name(options)
105
+ @indexed_name =
106
+ if options[:as]
107
+ options.delete(:as)
108
+ else
109
+ "#{@type.indexed_name(@name).to_s}#{'m' if @multiple }#{'s' if @stored}#{'v' if more_like_this?}"
110
+ end
111
+ end
112
+
113
+ end
114
+
115
+ #
116
+ # FulltextField instances represent fields that are indexed as fulltext.
117
+ # These fields are tokenized in the index, and can have boost applied to
118
+ # them. They also always allow multiple values (since the only downside of
119
+ # allowing multiple values is that it prevents the field from being sortable,
120
+ # and sorting on tokenized fields is nonsensical anyway, there is no reason
121
+ # to do otherwise). FulltextField instances always have the type TextType.
122
+ #
123
+ class FulltextField < Field #:nodoc:
124
+ attr_reader :default_boost
125
+
126
+ def initialize(name, options = {})
127
+ super(name, Type::TextType.instance, options)
128
+ @multiple = true
129
+ @boost = options.delete(:boost)
130
+ @default_boost = options.delete(:default_boost)
131
+ raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
132
+ end
133
+
134
+ def indexed_name
135
+ "#{super}"
136
+ end
137
+ end
138
+
139
+ #
140
+ # AttributeField instances encapsulate non-tokenized attribute data.
141
+ # AttributeFields can have any type except TextType, and can also have
142
+ # a reference (for instantiated facets), optionally allow multiple values
143
+ # (false by default), and can store their values (false by default). All
144
+ # scoping, sorting, and faceting is done with attribute fields.
145
+ #
146
+ class AttributeField < Field #:nodoc:
147
+ def initialize(name, type, options = {})
148
+ @multiple = !!options.delete(:multiple)
149
+ super(name, type, options)
150
+ @reference =
151
+ if (reference = options.delete(:references)).respond_to?(:name)
152
+ reference.name
153
+ elsif reference.respond_to?(:to_sym)
154
+ reference.to_sym
155
+ end
156
+ raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
157
+ end
158
+
159
+ end
160
+
161
+ class TypeField #:nodoc:
162
+ class <<self
163
+ def instance
164
+ @instance ||= new
165
+ end
166
+ end
167
+
168
+ def indexed_name
169
+ 'type'
170
+ end
171
+
172
+ def to_indexed(clazz)
173
+ clazz.name
174
+ end
175
+ end
176
+
177
+ class IdField #:nodoc:
178
+ class <<self
179
+ def instance
180
+ @instance ||= new
181
+ end
182
+ end
183
+
184
+ def indexed_name
185
+ 'id'
186
+ end
187
+
188
+ def to_indexed(id)
189
+ id.to_s
190
+ end
191
+ end
192
+ end
193
+
@@ -0,0 +1,129 @@
1
+ module Sunspot
2
+ #
3
+ # The FieldFactory module contains classes for generating fields. FieldFactory
4
+ # implementation classes should implement a #build method, although the arity
5
+ # of the method depends on the type of factory. They also must implement a
6
+ # #populate_document method, which extracts field data from a given model and
7
+ # adds it into the Solr document for indexing.
8
+ #
9
+ module FieldFactory #:nodoc:all
10
+ #
11
+ # Base class for field factories.
12
+ #
13
+ class Abstract
14
+ attr_reader :name
15
+
16
+ def initialize(name, options = {}, &block)
17
+ @name = name.to_sym
18
+ @data_extractor =
19
+ if block
20
+ DataExtractor::BlockExtractor.new(&block)
21
+ else
22
+ DataExtractor::AttributeExtractor.new(options.delete(:using) || name)
23
+ end
24
+ end
25
+ end
26
+
27
+ #
28
+ # A StaticFieldFactory generates normal static fields. Each factory instance
29
+ # contains an eager-initialized field instance, which is returned by the
30
+ # #build method.
31
+ #
32
+ class Static < Abstract
33
+ def initialize(name, type, options = {}, &block)
34
+ super(name, options, &block)
35
+ unless name.to_s =~ /^\w+$/
36
+ raise ArgumentError, "Invalid field name #{name}: only letters, numbers, and underscores are allowed."
37
+ end
38
+ @field =
39
+ if type.is_a?(Type::TextType)
40
+ FulltextField.new(name, options)
41
+ else
42
+ AttributeField.new(name, type, options)
43
+ end
44
+ end
45
+
46
+ #
47
+ # Return the field instance built by this factory
48
+ #
49
+ def build
50
+ @field
51
+ end
52
+
53
+ #
54
+ # Extract the encapsulated field's data from the given model and add it
55
+ # into the Solr document for indexing.
56
+ #
57
+ def populate_document(document, model) #:nodoc:
58
+ unless (value = @data_extractor.value_for(model)).nil?
59
+ Util.Array(@field.to_indexed(value)).each do |scalar_value|
60
+ options = {}
61
+ options[:boost] = @field.boost if @field.boost
62
+ document.add_field(
63
+ @field.indexed_name.to_sym,
64
+ scalar_value,
65
+ options
66
+ )
67
+ end
68
+ end
69
+ end
70
+
71
+ #
72
+ # A unique signature identifying this field by name and type.
73
+ #
74
+ def signature
75
+ [@field.name, @field.type]
76
+ end
77
+ end
78
+
79
+ #
80
+ # DynamicFieldFactories create dynamic field instances based on dynamic
81
+ # configuration.
82
+ #
83
+ class Dynamic < Abstract
84
+ attr_accessor :name, :type
85
+
86
+ def initialize(name, type, options = {}, &block)
87
+ super(name, options, &block)
88
+ @type, @options = type, options
89
+ end
90
+
91
+ #
92
+ # Build a field based on the dynamic name given.
93
+ #
94
+ def build(dynamic_name)
95
+ AttributeField.new("#{@name}:#{dynamic_name}", @type, @options.dup)
96
+ end
97
+ #
98
+ # This alias allows a DynamicFieldFactory to be used in place of a Setup
99
+ # or CompositeSetup instance by query components.
100
+ #
101
+ alias_method :field, :build
102
+
103
+ #
104
+ # Generate dynamic fields based on hash returned by data accessor and
105
+ # add the field data to the document.
106
+ #
107
+ def populate_document(document, model)
108
+ if values = @data_extractor.value_for(model)
109
+ values.each_pair do |dynamic_name, value|
110
+ field_instance = build(dynamic_name)
111
+ Util.Array(field_instance.to_indexed(value)).each do |scalar_value|
112
+ document.add_field(
113
+ field_instance.indexed_name.to_sym,
114
+ scalar_value
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ #
122
+ # Unique signature identifying this dynamic field based on name and type
123
+ #
124
+ def signature
125
+ [@name, @type]
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,131 @@
1
+ module Sunspot
2
+ #
3
+ # This class presents a service for adding, updating, and removing data
4
+ # from the Solr index. An Indexer instance is associated with a particular
5
+ # setup, and thus is capable of indexing instances of a certain class (and its
6
+ # subclasses).
7
+ #
8
+ class Indexer #:nodoc:
9
+ include RSolr::Char
10
+
11
+ def initialize(connection)
12
+ @connection = connection
13
+ end
14
+
15
+ #
16
+ # Construct a representation of the model for indexing and send it to the
17
+ # connection for indexing
18
+ #
19
+ # ==== Parameters
20
+ #
21
+ # model<Object>:: the model to index
22
+ #
23
+ def add(model)
24
+ documents = Util.Array(model).map { |m| prepare(m) }
25
+ if @batch.nil?
26
+ add_documents(documents)
27
+ else
28
+ @batch.concat(documents)
29
+ end
30
+ end
31
+
32
+ #
33
+ # Remove the given model from the Solr index
34
+ #
35
+ def remove(*models)
36
+ @connection.delete_by_id(
37
+ models.map { |model| Adapters::InstanceAdapter.adapt(model).index_id }
38
+ )
39
+ end
40
+
41
+ #
42
+ # Remove the model from the Solr index by specifying the class and ID
43
+ #
44
+ def remove_by_id(class_name, *ids)
45
+ @connection.delete_by_id(
46
+ ids.map { |id| Adapters::InstanceAdapter.index_id_for(class_name, id) }
47
+ )
48
+ end
49
+
50
+ #
51
+ # Delete all documents of the class indexed by this indexer from Solr.
52
+ #
53
+ def remove_all(clazz = nil)
54
+ if clazz
55
+ @connection.delete_by_query("type:#{escape(clazz.name)}")
56
+ else
57
+ @connection.delete_by_query("*:*")
58
+ end
59
+ end
60
+
61
+ #
62
+ # Remove all documents that match the scope given in the Query
63
+ #
64
+ def remove_by_scope(scope)
65
+ @connection.delete_by_query(scope.to_boolean_phrase)
66
+ end
67
+
68
+ #
69
+ # Start batch processing
70
+ #
71
+ def start_batch
72
+ @batch = []
73
+ end
74
+
75
+ #
76
+ # Write batch out to Solr and clear it
77
+ #
78
+ def flush_batch
79
+ add_documents(@batch)
80
+ @batch = nil
81
+ end
82
+
83
+ private
84
+
85
+ #
86
+ # Convert documents into hash of indexed properties
87
+ #
88
+ def prepare(model)
89
+ document = document_for(model)
90
+ setup = setup_for(model)
91
+ if boost = setup.document_boost_for(model)
92
+ document.attrs[:boost] = boost
93
+ end
94
+ setup.all_field_factories.each do |field_factory|
95
+ field_factory.populate_document(document, model)
96
+ end
97
+ document
98
+ end
99
+
100
+ def add_documents(documents)
101
+ @connection.add(documents)
102
+ end
103
+
104
+ #
105
+ # All indexed documents index and store the +id+ and +type+ fields.
106
+ # This method constructs the document hash containing those key-value
107
+ # pairs.
108
+ #
109
+ def document_for(model)
110
+ RSolr::Message::Document.new(
111
+ :id => Adapters::InstanceAdapter.adapt(model).index_id,
112
+ :type => Util.superclasses_for(model.class).map { |clazz| clazz.name }
113
+ )
114
+ end
115
+
116
+ #
117
+ # Get the Setup object for the given object's class.
118
+ #
119
+ # ==== Parameters
120
+ #
121
+ # object<Object>:: The object whose setup is to be retrieved
122
+ #
123
+ # ==== Returns
124
+ #
125
+ # Sunspot::Setup:: The setup for the object's class
126
+ #
127
+ def setup_for(object)
128
+ Setup.for(object.class) || raise(NoSetupError, "Sunspot is not configured for #{object.class.inspect}")
129
+ end
130
+ end
131
+ end