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,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,67 @@
1
+ module Sunspot
2
+ module SessionProxy
3
+ class MulticoreSessionProxy < AbstractSessionProxy
4
+
5
+ def initialize( cores )
6
+ @sessions = {}
7
+ @cores ||= []
8
+ ( @cores << cores ).flatten!
9
+ cores.each do |core|
10
+
11
+ # can't find a better way grab the sunspot.yml configuration (:user_configuration is a private method)
12
+ modified_config = Marshal.load( Marshal.dump( Sunspot::Rails.configuration.send(:user_configuration) ) )
13
+
14
+ modified_config["solr"]["path"] = File.join(
15
+ ( modified_config["solr"]["path"] || Sunspot::Rails.configuration.path ), core )
16
+
17
+ extended_config = Sunspot::Rails::ExtendedConfiguration.new(modified_config)
18
+ session = @sessions[core] = Sunspot::Rails.build_session(extended_config)
19
+ end
20
+ end
21
+
22
+ attr_accessor :selected_session
23
+
24
+ def method_missing(method, *args, &block)
25
+ inspectable_args = args.flatten
26
+ types = inspectable_args.select(&:searchable?) rescue []
27
+ types += inspectable_args.select{|a| a.class.searchable? rescue false }
28
+
29
+ self.selected_session = session_for_types(types) || self.selected_session
30
+
31
+ if self.selected_session.respond_to?(method)
32
+ begin
33
+ self.selected_session.send(method, *args, &block)
34
+ rescue Exception => e
35
+ (puts "method #{method} caused: #{e.message}")
36
+ end
37
+ else
38
+ # raise NoMethodError, "Method #{method} isn't defined on session"
39
+ # logger.info("do nothing")
40
+ # super
41
+ # self.selected_session.send(method, *args, &block)
42
+ return false
43
+ end
44
+ end
45
+
46
+ protected
47
+ def infer_configuration_options_from_env_url( options )
48
+ if ENV["SOLR_URL"]
49
+
50
+ end
51
+ end
52
+
53
+ def session_for_types(*types)
54
+ cores = types.flatten.map{|t| t.sunspot_options[:core] }.uniq
55
+ raise ArgumentError, "Can't mix and match types that use different solr cores" if cores.length > 1
56
+
57
+ @sessions[cores.first]
58
+ end
59
+
60
+ def session_for_objects(*objects)
61
+ types = objects.map(&:class).uniq
62
+ session_for_types(types)
63
+ end
64
+
65
+ end
66
+ end
67
+ 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
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), 'abstract_session_proxy')
2
+
3
+ module Sunspot
4
+ module SessionProxy
5
+ class SilentFailSessionProxy < AbstractSessionProxy
6
+
7
+ attr_reader :search_session
8
+
9
+ delegate :new_search, :search, :config,
10
+ :new_more_like_this, :more_like_this,
11
+ :delete_dirty, :delete_dirty?,
12
+ :to => :search_session
13
+
14
+ def initialize(search_session = Sunspot.session)
15
+ @search_session = search_session
16
+ end
17
+
18
+ def rescued_exception(method, e)
19
+ $stderr.puts("Exception in #{method}: #{e.message}")
20
+ end
21
+
22
+ SUPPORTED_METHODS = [
23
+ :batch, :commit, :commit_if_dirty, :commit_if_delete_dirty, :dirty?,
24
+ :index!, :index, :optimize, :remove!, :remove, :remove_all!, :remove_all,
25
+ :remove_by_id!, :remove_by_id
26
+ ]
27
+
28
+ SUPPORTED_METHODS.each do |method|
29
+ module_eval(<<-RUBY)
30
+ def #{method}(*args, &block)
31
+ begin
32
+ search_session.#{method}(*args, &block)
33
+ rescue => e
34
+ self.rescued_exception(:#{method}, e)
35
+ end
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ require 'monitor'
2
+ require File.join(File.dirname(__FILE__), 'abstract_session_proxy')
3
+
4
+ module Sunspot
5
+ module SessionProxy
6
+ #
7
+ # This class implements a session proxy that creates a different Session
8
+ # object for each thread. Any multithreaded application should use this
9
+ # proxy.
10
+ #
11
+ class ThreadLocalSessionProxy < AbstractSessionProxy
12
+ FINALIZER = Proc.new do |object_id|
13
+ Thread.current[:"sunspot_session_#{object_id}"] = nil
14
+ end
15
+
16
+ # The configuration with which the thread-local sessions are initialized.
17
+ attr_reader :config
18
+ @@next_id = 0
19
+
20
+ delegate :batch, :commit, :commit_if_delete_dirty, :commit_if_dirty, :delete_dirty?, :dirty?, :index, :index!, :new_search, :optimize, :remove, :remove!, :remove_all, :remove_all!, :remove_by_id, :remove_by_id!, :search, :more_like_this, :new_more_like_this, :to => :session
21
+
22
+ #
23
+ # Optionally pass an existing Sunspot::Configuration object. If none is
24
+ # passed, a default configuration is used; it can then be modified using
25
+ # the #config attribute.
26
+ #
27
+ def initialize(config = Sunspot::Configuration.new)
28
+ @config = config
29
+ ObjectSpace.define_finalizer(self, FINALIZER)
30
+ end
31
+
32
+ def session #:nodoc:
33
+ Thread.current[:"sunspot_session_#{object_id}"] ||= Session.new(config)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,95 @@
1
+ module Sunspot
2
+ #
3
+ # This module contains several Session Proxy implementations, which can be
4
+ # used to decorate one or more Session objects and add extra functionality.
5
+ # The user can also implement their own Session Proxy classes; a Session Proxy
6
+ # must simply implement the same public API as the Sunspot::Session class.
7
+ #
8
+ # When implementing a session proxy, some methods of Session may not be
9
+ # practical, or even logical, to implement. In this case, the method should
10
+ # raise a Sunspot::SessionProxy::NotSupportedError (several methods in the
11
+ # built-in session proxies raise this error).
12
+ #
13
+ # To use a session proxy in normal Sunspot usage, you can use the
14
+ # Sunspot.session= method, which will cause Sunspot to delegate all of its
15
+ # session-related class methods (most of them) to the proxy. Session proxies
16
+ # can also easily be chained, although the details of chaining depend on the
17
+ # proxy implementation.
18
+ #
19
+ # ===== Example: Chain a MasterSlaveSessionProxy with a ThreadLocalSessionProxy
20
+ #
21
+ # master_session = Sunspot::SessionProxy::ThreadLocalSessionProxy.new
22
+ # slave_session = Sunspot::SessionProxy::ThreadLocalSessionProxy.new
23
+ # master_session.config.solr.url = 'http://master-solr.local:9080/solr'
24
+ # slave_session.config.solr.url = 'http://slave-solr.local:9080/solr'
25
+ # Sunspot.session = Sunspot::SessionProxy::MasterSlaveSessionProxy.new(master_session, slave_session)
26
+ #
27
+ module SessionProxy
28
+ NotSupportedError = Class.new(StandardError)
29
+
30
+ autoload(
31
+ :AbstractSessionProxy,
32
+ File.join(
33
+ File.dirname(__FILE__),
34
+ 'session_proxy',
35
+ 'abstract_session_proxy'
36
+ )
37
+ )
38
+ autoload(
39
+ :ThreadLocalSessionProxy,
40
+ File.join(
41
+ File.dirname(__FILE__),
42
+ 'session_proxy',
43
+ 'thread_local_session_proxy'
44
+ )
45
+ )
46
+ autoload(
47
+ :MasterSlaveSessionProxy,
48
+ File.join(
49
+ File.dirname(__FILE__),
50
+ 'session_proxy',
51
+ 'master_slave_session_proxy'
52
+ )
53
+ )
54
+ autoload(
55
+ :ShardingSessionProxy,
56
+ File.join(
57
+ File.dirname(__FILE__),
58
+ 'session_proxy',
59
+ 'sharding_session_proxy'
60
+ )
61
+ )
62
+ autoload(
63
+ :ClassShardingSessionProxy,
64
+ File.join(
65
+ File.dirname(__FILE__),
66
+ 'session_proxy',
67
+ 'class_sharding_session_proxy'
68
+ )
69
+ )
70
+ autoload(
71
+ :IdShardingSessionProxy,
72
+ File.join(
73
+ File.dirname(__FILE__),
74
+ 'session_proxy',
75
+ 'id_sharding_session_proxy'
76
+ )
77
+ )
78
+ autoload(
79
+ :SilentFailSessionProxy,
80
+ File.join(
81
+ File.dirname(__FILE__),
82
+ 'session_proxy',
83
+ 'silent_fail_session_proxy'
84
+ )
85
+ )
86
+ autoload(
87
+ :MulticoreSessionProxy,
88
+ File.join(
89
+ File.dirname(__FILE__),
90
+ 'session_proxy',
91
+ 'multicore_session_proxy'
92
+ )
93
+ )
94
+ end
95
+ end