gojee-sunspot 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +252 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +13 -0
  6. data/TODO +13 -0
  7. data/lib/light_config.rb +40 -0
  8. data/lib/sunspot/adapters.rb +265 -0
  9. data/lib/sunspot/batcher.rb +62 -0
  10. data/lib/sunspot/class_set.rb +23 -0
  11. data/lib/sunspot/composite_setup.rb +202 -0
  12. data/lib/sunspot/configuration.rb +53 -0
  13. data/lib/sunspot/data_extractor.rb +50 -0
  14. data/lib/sunspot/dsl/adjustable.rb +47 -0
  15. data/lib/sunspot/dsl/field_group.rb +57 -0
  16. data/lib/sunspot/dsl/field_query.rb +327 -0
  17. data/lib/sunspot/dsl/fields.rb +103 -0
  18. data/lib/sunspot/dsl/fulltext.rb +243 -0
  19. data/lib/sunspot/dsl/function.rb +27 -0
  20. data/lib/sunspot/dsl/functional.rb +44 -0
  21. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  22. data/lib/sunspot/dsl/paginatable.rb +32 -0
  23. data/lib/sunspot/dsl/query_facet.rb +36 -0
  24. data/lib/sunspot/dsl/restriction.rb +25 -0
  25. data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  26. data/lib/sunspot/dsl/scope.rb +217 -0
  27. data/lib/sunspot/dsl/search.rb +30 -0
  28. data/lib/sunspot/dsl/standard_query.rb +123 -0
  29. data/lib/sunspot/dsl.rb +5 -0
  30. data/lib/sunspot/field.rb +193 -0
  31. data/lib/sunspot/field_factory.rb +129 -0
  32. data/lib/sunspot/indexer.rb +136 -0
  33. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  34. data/lib/sunspot/query/bbox.rb +15 -0
  35. data/lib/sunspot/query/boost_query.rb +24 -0
  36. data/lib/sunspot/query/common_query.rb +96 -0
  37. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  38. data/lib/sunspot/query/connective.rb +206 -0
  39. data/lib/sunspot/query/date_field_facet.rb +14 -0
  40. data/lib/sunspot/query/dismax.rb +132 -0
  41. data/lib/sunspot/query/field_facet.rb +41 -0
  42. data/lib/sunspot/query/field_group.rb +36 -0
  43. data/lib/sunspot/query/filter.rb +38 -0
  44. data/lib/sunspot/query/function_query.rb +52 -0
  45. data/lib/sunspot/query/geo.rb +53 -0
  46. data/lib/sunspot/query/geofilt.rb +16 -0
  47. data/lib/sunspot/query/highlighting.rb +62 -0
  48. data/lib/sunspot/query/more_like_this.rb +61 -0
  49. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  50. data/lib/sunspot/query/pagination.rb +42 -0
  51. data/lib/sunspot/query/query_facet.rb +16 -0
  52. data/lib/sunspot/query/restriction.rb +262 -0
  53. data/lib/sunspot/query/scope.rb +9 -0
  54. data/lib/sunspot/query/sort.rb +109 -0
  55. data/lib/sunspot/query/sort_composite.rb +34 -0
  56. data/lib/sunspot/query/standard_query.rb +16 -0
  57. data/lib/sunspot/query/text_field_boost.rb +17 -0
  58. data/lib/sunspot/query.rb +11 -0
  59. data/lib/sunspot/schema.rb +151 -0
  60. data/lib/sunspot/search/abstract_search.rb +281 -0
  61. data/lib/sunspot/search/date_facet.rb +35 -0
  62. data/lib/sunspot/search/facet_row.rb +27 -0
  63. data/lib/sunspot/search/field_facet.rb +88 -0
  64. data/lib/sunspot/search/field_group.rb +32 -0
  65. data/lib/sunspot/search/group.rb +50 -0
  66. data/lib/sunspot/search/highlight.rb +38 -0
  67. data/lib/sunspot/search/hit.rb +150 -0
  68. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  69. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  70. data/lib/sunspot/search/paginated_collection.rb +57 -0
  71. data/lib/sunspot/search/query_facet.rb +67 -0
  72. data/lib/sunspot/search/standard_search.rb +21 -0
  73. data/lib/sunspot/search.rb +9 -0
  74. data/lib/sunspot/session.rb +262 -0
  75. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  76. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  77. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  78. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  79. data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
  80. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  81. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  82. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  83. data/lib/sunspot/session_proxy.rb +95 -0
  84. data/lib/sunspot/setup.rb +350 -0
  85. data/lib/sunspot/text_field_setup.rb +29 -0
  86. data/lib/sunspot/type.rb +393 -0
  87. data/lib/sunspot/util.rb +252 -0
  88. data/lib/sunspot/version.rb +3 -0
  89. data/lib/sunspot.rb +579 -0
  90. data/log/.gitignore +1 -0
  91. data/pkg/.gitignore +1 -0
  92. data/script/console +10 -0
  93. data/spec/api/adapters_spec.rb +33 -0
  94. data/spec/api/batcher_spec.rb +112 -0
  95. data/spec/api/binding_spec.rb +50 -0
  96. data/spec/api/class_set_spec.rb +24 -0
  97. data/spec/api/hit_enumerable_spec.rb +47 -0
  98. data/spec/api/indexer/attributes_spec.rb +149 -0
  99. data/spec/api/indexer/batch_spec.rb +72 -0
  100. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  101. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  102. data/spec/api/indexer/fulltext_spec.rb +43 -0
  103. data/spec/api/indexer/removal_spec.rb +53 -0
  104. data/spec/api/indexer/spec_helper.rb +1 -0
  105. data/spec/api/indexer_spec.rb +14 -0
  106. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  107. data/spec/api/query/connectives_examples.rb +189 -0
  108. data/spec/api/query/dsl_spec.rb +18 -0
  109. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  110. data/spec/api/query/faceting_examples.rb +397 -0
  111. data/spec/api/query/fulltext_examples.rb +313 -0
  112. data/spec/api/query/function_spec.rb +79 -0
  113. data/spec/api/query/geo_examples.rb +68 -0
  114. data/spec/api/query/group_spec.rb +32 -0
  115. data/spec/api/query/highlighting_examples.rb +245 -0
  116. data/spec/api/query/more_like_this_spec.rb +140 -0
  117. data/spec/api/query/ordering_pagination_examples.rb +116 -0
  118. data/spec/api/query/scope_examples.rb +275 -0
  119. data/spec/api/query/spatial_examples.rb +27 -0
  120. data/spec/api/query/spec_helper.rb +1 -0
  121. data/spec/api/query/standard_spec.rb +29 -0
  122. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  123. data/spec/api/query/types_spec.rb +20 -0
  124. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  125. data/spec/api/search/faceting_spec.rb +360 -0
  126. data/spec/api/search/highlighting_spec.rb +69 -0
  127. data/spec/api/search/hits_spec.rb +131 -0
  128. data/spec/api/search/paginated_collection_spec.rb +36 -0
  129. data/spec/api/search/results_spec.rb +72 -0
  130. data/spec/api/search/search_spec.rb +23 -0
  131. data/spec/api/search/spec_helper.rb +1 -0
  132. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  133. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  134. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  135. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  136. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  137. data/spec/api/session_proxy/spec_helper.rb +9 -0
  138. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  139. data/spec/api/session_spec.rb +232 -0
  140. data/spec/api/spec_helper.rb +3 -0
  141. data/spec/api/sunspot_spec.rb +29 -0
  142. data/spec/ext.rb +11 -0
  143. data/spec/helpers/indexer_helper.rb +17 -0
  144. data/spec/helpers/integration_helper.rb +8 -0
  145. data/spec/helpers/mock_session_helper.rb +13 -0
  146. data/spec/helpers/query_helper.rb +26 -0
  147. data/spec/helpers/search_helper.rb +68 -0
  148. data/spec/integration/dynamic_fields_spec.rb +57 -0
  149. data/spec/integration/faceting_spec.rb +251 -0
  150. data/spec/integration/field_grouping_spec.rb +66 -0
  151. data/spec/integration/geospatial_spec.rb +85 -0
  152. data/spec/integration/highlighting_spec.rb +44 -0
  153. data/spec/integration/indexing_spec.rb +55 -0
  154. data/spec/integration/keyword_search_spec.rb +317 -0
  155. data/spec/integration/local_search_spec.rb +64 -0
  156. data/spec/integration/more_like_this_spec.rb +43 -0
  157. data/spec/integration/scoped_search_spec.rb +354 -0
  158. data/spec/integration/stored_fields_spec.rb +12 -0
  159. data/spec/integration/test_pagination.rb +43 -0
  160. data/spec/integration/unicode_spec.rb +15 -0
  161. data/spec/mocks/adapters.rb +32 -0
  162. data/spec/mocks/blog.rb +3 -0
  163. data/spec/mocks/comment.rb +21 -0
  164. data/spec/mocks/connection.rb +126 -0
  165. data/spec/mocks/mock_adapter.rb +30 -0
  166. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  167. data/spec/mocks/mock_record.rb +52 -0
  168. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  169. data/spec/mocks/photo.rb +11 -0
  170. data/spec/mocks/post.rb +86 -0
  171. data/spec/mocks/super_class.rb +2 -0
  172. data/spec/mocks/user.rb +13 -0
  173. data/spec/spec_helper.rb +40 -0
  174. data/sunspot.gemspec +42 -0
  175. data/tasks/rdoc.rake +27 -0
  176. data/tasks/schema.rake +19 -0
  177. data/tasks/todo.rake +4 -0
  178. metadata +261 -3
@@ -0,0 +1,150 @@
1
+ module Sunspot
2
+ module Search
3
+ #
4
+ # Hit objects represent the raw information returned by Solr for a single
5
+ # document. As well as the primary key and class name, hit objects give
6
+ # access to stored field values, keyword relevance score, and keyword
7
+ # highlighting.
8
+ #
9
+ class Hit
10
+ SPECIAL_KEYS = Set.new(%w(id type score)) #:nodoc:
11
+
12
+ #
13
+ # Primary key of object associated with this hit, as string.
14
+ #
15
+ attr_reader :primary_key
16
+ #
17
+ # Class name of object associated with this hit, as string.
18
+ #
19
+ attr_reader :class_name
20
+ #
21
+ # Keyword relevance score associated with this result. Nil if this hit
22
+ # is not from a keyword search.
23
+ #
24
+ attr_reader :score
25
+ #
26
+
27
+ attr_writer :result #:nodoc:
28
+
29
+ def initialize(raw_hit, highlights, search) #:nodoc:
30
+ @class_name, @primary_key = *raw_hit['id'].match(/([^ ]+) (.+)/)[1..2]
31
+ @score = raw_hit['score']
32
+ @search = search
33
+ @stored_values = raw_hit
34
+ @stored_cache = {}
35
+ @highlights = highlights
36
+ end
37
+
38
+ #
39
+ # Returns all highlights for this hit when called without parameters.
40
+ # When a field_name is provided, returns only the highlight for this field.
41
+ #
42
+ def highlights(field_name = nil)
43
+ if field_name.nil?
44
+ highlights_cache.values.flatten
45
+ else
46
+ highlights_cache[field_name.to_sym]
47
+ end || []
48
+ end
49
+
50
+ #
51
+ # Return the first highlight found for a given field, or nil if there is
52
+ # none.
53
+ #
54
+ def highlight(field_name)
55
+ highlights(field_name).first
56
+ end
57
+
58
+ #
59
+ # Retrieve stored field value. For any attribute field configured with
60
+ # :stored => true, the Hit object will contain the stored value for
61
+ # that field. The value of this field will be typecast according to the
62
+ # type of the field.
63
+ #
64
+ # ==== Parameters
65
+ #
66
+ # field_name<Symbol>::
67
+ # The name of the field for which to retrieve the stored value.
68
+ # dynamic_field_name<Symbol>::
69
+ # If you want to access a stored dynamic field, this should be the
70
+ # dynamic component of the field name.
71
+ #
72
+ def stored(field_name, dynamic_field_name = nil)
73
+ field_key =
74
+ if dynamic_field_name
75
+ [field_name.to_sym, dynamic_field_name.to_sym]
76
+ else
77
+ field_name.to_sym
78
+ end
79
+ return @stored_cache[field_key] if @stored_cache.has_key?(field_key)
80
+ @stored_cache[field_key] = stored_value(field_name, dynamic_field_name)
81
+ end
82
+
83
+ #
84
+ # Retrieve the instance associated with this hit. This is lazy-loaded, but
85
+ # the first time it is called on any hit, all the hits for the search will
86
+ # load their instances using the adapter's #load_all method.
87
+ #
88
+ def result
89
+ return @result if defined?(@result)
90
+ @search.populate_hits
91
+ @result
92
+ end
93
+ alias_method :instance, :result
94
+
95
+ def inspect #:nodoc:
96
+ "#<Sunspot::Search::Hit:#{@class_name} #{@primary_key}>"
97
+ end
98
+
99
+ #
100
+ # Returns the instance primary key when the Hit is used to generate urls
101
+ # For example, using a search that stores the :name attribute:
102
+ #
103
+ # hits = Sunspot.search(Object) do ...
104
+ #
105
+ # hits.each do |hit|
106
+ # link_to hit.stored(:name), edit_object_path(hit)
107
+ # end
108
+ #
109
+ def to_param
110
+ self.primary_key
111
+ end
112
+
113
+ private
114
+
115
+ def setup
116
+ @setup ||= Sunspot::Setup.for(Util.full_const_get(@class_name))
117
+ end
118
+
119
+ def highlights_cache
120
+ @highlights_cache ||=
121
+ begin
122
+ cache = {}
123
+ if @highlights
124
+ @highlights.each_pair do |indexed_field_name, highlight_strings|
125
+ field_name = indexed_field_name.sub(/_[a-z]+$/, '').to_sym
126
+ cache[field_name] = highlight_strings.map do |highlight_string|
127
+ Highlight.new(field_name, highlight_string)
128
+ end
129
+ end
130
+ end
131
+ cache
132
+ end
133
+ end
134
+
135
+ def stored_value(field_name, dynamic_field_name)
136
+ setup.stored_fields(field_name, dynamic_field_name).each do |field|
137
+ value = @stored_values[field.indexed_name]
138
+
139
+ if Array === value
140
+ return value.map { |item| field.cast(item) }
141
+ elsif !value.nil?
142
+ return field.cast(value)
143
+ end
144
+ end
145
+
146
+ nil
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,72 @@
1
+ module Sunspot
2
+ module Search
3
+ module HitEnumerable #:nodoc:
4
+ def hits(options = {})
5
+ if options[:verify]
6
+ verified_hits
7
+ elsif solr_docs
8
+ solr_docs.map { |d| Hit.new(d, highlights_for(d), self) }
9
+ else
10
+ []
11
+ end
12
+ end
13
+
14
+ def verified_hits
15
+ hits.select { |h| h.result }
16
+ end
17
+
18
+ #
19
+ # Populate the Hit objects with their instances. This is invoked the first
20
+ # time any hit has its instance requested, and all hits are loaded as a
21
+ # batch.
22
+ #
23
+ def populate_hits #:nodoc:
24
+ id_hit_hash = Hash.new { |h, k| h[k] = {} }
25
+ hits.each do |hit|
26
+ id_hit_hash[hit.class_name][hit.primary_key] = hit
27
+ end
28
+ id_hit_hash.each_pair do |class_name, hits|
29
+ ids = hits.map { |id, hit| hit.primary_key }
30
+ data_accessor = data_accessor_for(Util.full_const_get(class_name))
31
+ hits_for_class = id_hit_hash[class_name]
32
+ data_accessor.load_all(ids).each do |result|
33
+ hit = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
34
+ hit.result = result
35
+ end
36
+ hits_for_class.values.each { |hit| hit.result = nil }
37
+ end
38
+ end
39
+
40
+ #
41
+ # Convenience method to iterate over hit and result objects. Block is
42
+ # yielded a Sunspot::Server::Hit instance and a Sunspot::Server::Result
43
+ # instance.
44
+ #
45
+ # Note that this method iterates over verified hits (see #hits method
46
+ # for more information).
47
+ #
48
+ def each_hit_with_result
49
+ verified_hits.each do |hit|
50
+ yield(hit, hit.result)
51
+ end
52
+ end
53
+
54
+ #
55
+ # Get the data accessor that will be used to load a particular class out of
56
+ # persistent storage. Data accessors can implement any methods that may be
57
+ # useful for refining how data is loaded out of storage. When building a
58
+ # search manually (e.g., using the Sunspot#new_search method), this should
59
+ # be used before calling #execute(). Use the
60
+ # Sunspot::DSL::Search#data_accessor_for method when building searches using
61
+ # the block DSL.
62
+ #
63
+ def data_accessor_for(clazz) #:nodoc:
64
+ # FIXME: This method does not belong here, but I was getting bogged
65
+ # down trying to figure out where it should go. Punted for now.
66
+ # - AL 27 Nov 2011
67
+ (@data_accessors ||= {})[clazz.name.to_sym] ||=
68
+ Adapters::DataAccessor.create(clazz)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ module Sunspot
2
+ #
3
+ # This class encapsulates the results of a Solr MoreLikeThis search. It provides access
4
+ # to search results, total result count, and pagination information.
5
+ # Instances of MoreLikeThis are returned by the Sunspot.more_like_this and
6
+ # Sunspot.new_more_like_this methods.
7
+ #
8
+ module Search
9
+ class MoreLikeThisSearch < AbstractSearch
10
+ def execute
11
+ if @query.more_like_this.fields.empty?
12
+ @setup.all_more_like_this_fields.each do |field|
13
+ @query.more_like_this.add_field(field)
14
+ end
15
+ end
16
+ super
17
+ end
18
+
19
+ def request_handler
20
+ super || :mlt
21
+ end
22
+
23
+ private
24
+
25
+ # override
26
+ def dsl
27
+ DSL::MoreLikeThisQuery.new(self, @query, @setup)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ module Sunspot
2
+ module Search
3
+
4
+ class PaginatedCollection
5
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
6
+
7
+ attr_reader :current_page, :per_page
8
+ attr_accessor :total_count
9
+ alias :total_entries :total_count
10
+ alias :total_entries= :total_count=
11
+ alias :limit_value :per_page
12
+
13
+ def initialize(collection, page, per_page, total)
14
+ @collection = collection
15
+ @current_page = page
16
+ @per_page = per_page
17
+ @total_count = total
18
+ end
19
+
20
+ def total_pages
21
+ (total_count.to_f / per_page).ceil
22
+ end
23
+ alias :num_pages :total_pages
24
+
25
+ def first_page?
26
+ current_page == 1
27
+ end
28
+
29
+ def last_page?
30
+ current_page >= total_pages
31
+ end
32
+
33
+ def previous_page
34
+ current_page > 1 ? (current_page - 1) : nil
35
+ end
36
+
37
+ def next_page
38
+ current_page < total_pages ? (current_page + 1) : nil
39
+ end
40
+
41
+ def out_of_bounds?
42
+ current_page > total_pages
43
+ end
44
+
45
+ def offset
46
+ (current_page - 1) * per_page
47
+ end
48
+
49
+ private
50
+
51
+ def method_missing(method, *args, &block)
52
+ @collection.send(method, *args, &block)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,67 @@
1
+ module Sunspot
2
+ module Search
3
+ class QueryFacet
4
+ RequestedFacet = Struct.new(:label, :boolean_phrase) #:nodoc:
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name, search, options) #:nodoc:
9
+ @name, @search, @options = name, search, options
10
+ @requested_facets = []
11
+ end
12
+
13
+ def rows(options = {})
14
+ @rows ||=
15
+ begin
16
+ data = @search.facet_response['facet_queries']
17
+ rows = []
18
+ minimum_count =
19
+ case
20
+ when @options[:minimum_count] then @options[:minimum_count]
21
+ when @options[:zeros] then 0
22
+ else 1
23
+ end
24
+ @requested_facets.each do |requested_facet|
25
+ count = data[requested_facet.boolean_phrase] || 0
26
+ if count >= minimum_count
27
+ rows << FacetRow.new(requested_facet.label, count, self)
28
+ end
29
+ end
30
+ sort_rows!(rows)
31
+ end
32
+ end
33
+
34
+ def add_row(label, boolean_phrase) #:nodoc:
35
+ @requested_facets << RequestedFacet.new(label, boolean_phrase)
36
+ end
37
+
38
+ private
39
+
40
+ def sort_rows!(rows)
41
+ case @options[:sort] || (:count if limit)
42
+ when :count
43
+ rows.sort! { |lrow, rrow| rrow.count <=> lrow.count }
44
+ when :index
45
+ rows.sort! do |lrow, rrow|
46
+ if lrow.respond_to?(:<=>)
47
+ lrow.value <=> rrow.value
48
+ elsif lrow.respond_to?(:first) && rrow.respond_to?(:first) && lrow.first.respond_to?(:<=>)
49
+ lrow.first.value <=> rrow.first.value
50
+ else
51
+ lrow.value.to_s <=> rrow.value.to_s
52
+ end
53
+ end
54
+ end
55
+ if limit
56
+ rows.replace(rows.first(limit))
57
+ end
58
+ rows
59
+ end
60
+
61
+ def limit
62
+ return @limit if defined?(@limit)
63
+ @limit = (@options[:limit].to_i if @options[:limit].to_i > 0)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,21 @@
1
+ module Sunspot
2
+ module Search
3
+ #
4
+ # This class encapsulates the results of a Solr search. It provides access
5
+ # to search results, total result count, facets, and pagination information.
6
+ # Instances of Search are returned by the Sunspot.search and
7
+ # Sunspot.new_search methods.
8
+ #
9
+ class StandardSearch < AbstractSearch
10
+ def request_handler
11
+ super || :select
12
+ end
13
+
14
+ private
15
+
16
+ def dsl
17
+ DSL::Search.new(self, @setup)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ %w(abstract_search standard_search more_like_this_search query_facet field_facet
2
+ date_facet facet_row hit highlight field_group group hit_enumerable).each do |file|
3
+ require File.join(File.dirname(__FILE__), 'search', file)
4
+ end
5
+
6
+ module Sunspot
7
+ module Search
8
+ end
9
+ end
@@ -0,0 +1,262 @@
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
+ :read_timeout => config.solr.read_timeout,
244
+ :open_timeout => config.solr.open_timeout)
245
+ end
246
+
247
+ def indexer
248
+ @indexer ||= Indexer.new(connection)
249
+ end
250
+
251
+ def setup_for_types(types)
252
+ if types.empty?
253
+ raise(ArgumentError, "You must specify at least one type to search")
254
+ end
255
+ if types.length == 1
256
+ Setup.for(types.first)
257
+ else
258
+ CompositeSetup.for(types)
259
+ end
260
+ end
261
+ end
262
+ 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