acts_as_ferret 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/README +32 -6
  2. data/acts_as_ferret.gemspec +260 -0
  3. data/config/ferret_server.yml +1 -0
  4. data/doc/demo/README +154 -0
  5. data/doc/demo/README_DEMO +23 -0
  6. data/doc/demo/Rakefile +10 -0
  7. data/doc/demo/app/controllers/admin/backend_controller.rb +14 -0
  8. data/doc/demo/app/controllers/admin_area_controller.rb +4 -0
  9. data/doc/demo/app/controllers/application.rb +5 -0
  10. data/doc/demo/app/controllers/contents_controller.rb +49 -0
  11. data/doc/demo/app/controllers/searches_controller.rb +8 -0
  12. data/doc/demo/app/helpers/admin/backend_helper.rb +2 -0
  13. data/doc/demo/app/helpers/application_helper.rb +3 -0
  14. data/doc/demo/app/helpers/content_helper.rb +2 -0
  15. data/doc/demo/app/helpers/search_helper.rb +2 -0
  16. data/doc/demo/app/models/comment.rb +48 -0
  17. data/doc/demo/app/models/content.rb +12 -0
  18. data/doc/demo/app/models/content_base.rb +28 -0
  19. data/doc/demo/app/models/search.rb +19 -0
  20. data/doc/demo/app/models/shared_index1.rb +3 -0
  21. data/doc/demo/app/models/shared_index2.rb +3 -0
  22. data/doc/demo/app/models/special_content.rb +3 -0
  23. data/doc/demo/app/models/stats.rb +20 -0
  24. data/doc/demo/app/views/admin/backend/search.rhtml +18 -0
  25. data/doc/demo/app/views/contents/_form.rhtml +10 -0
  26. data/doc/demo/app/views/contents/edit.rhtml +9 -0
  27. data/doc/demo/app/views/contents/index.rhtml +24 -0
  28. data/doc/demo/app/views/contents/new.rhtml +8 -0
  29. data/doc/demo/app/views/contents/show.rhtml +8 -0
  30. data/doc/demo/app/views/layouts/application.html.erb +17 -0
  31. data/doc/demo/app/views/searches/_content.html.erb +2 -0
  32. data/doc/demo/app/views/searches/search.html.erb +20 -0
  33. data/doc/demo/config/boot.rb +109 -0
  34. data/doc/demo/config/database.yml +38 -0
  35. data/doc/demo/config/environment.rb +69 -0
  36. data/doc/demo/config/environments/development.rb +16 -0
  37. data/doc/demo/config/environments/production.rb +19 -0
  38. data/doc/demo/config/environments/test.rb +21 -0
  39. data/doc/demo/config/ferret_server.yml +18 -0
  40. data/doc/demo/config/lighttpd.conf +40 -0
  41. data/doc/demo/config/routes.rb +9 -0
  42. data/doc/demo/db/development_structure.sql +15 -0
  43. data/doc/demo/db/migrate/001_initial_migration.rb +18 -0
  44. data/doc/demo/db/migrate/002_add_type_to_contents.rb +9 -0
  45. data/doc/demo/db/migrate/003_create_shared_index1s.rb +11 -0
  46. data/doc/demo/db/migrate/004_create_shared_index2s.rb +11 -0
  47. data/doc/demo/db/migrate/005_special_field.rb +9 -0
  48. data/doc/demo/db/migrate/006_create_stats.rb +15 -0
  49. data/doc/demo/db/schema.sql +18 -0
  50. data/doc/demo/doc/README_FOR_APP +2 -0
  51. data/doc/demo/doc/howto.txt +70 -0
  52. data/doc/demo/public/.htaccess +40 -0
  53. data/doc/demo/public/404.html +8 -0
  54. data/doc/demo/public/500.html +8 -0
  55. data/doc/demo/public/dispatch.cgi +10 -0
  56. data/doc/demo/public/dispatch.fcgi +24 -0
  57. data/doc/demo/public/dispatch.rb +10 -0
  58. data/doc/demo/public/favicon.ico +0 -0
  59. data/doc/demo/public/images/rails.png +0 -0
  60. data/doc/demo/public/index.html +277 -0
  61. data/doc/demo/public/robots.txt +1 -0
  62. data/doc/demo/public/stylesheets/scaffold.css +74 -0
  63. data/doc/demo/script/about +3 -0
  64. data/doc/demo/script/breakpointer +3 -0
  65. data/doc/demo/script/console +3 -0
  66. data/doc/demo/script/destroy +3 -0
  67. data/doc/demo/script/ferret_server +10 -0
  68. data/doc/demo/script/generate +3 -0
  69. data/doc/demo/script/performance/benchmarker +3 -0
  70. data/doc/demo/script/performance/profiler +3 -0
  71. data/doc/demo/script/plugin +3 -0
  72. data/doc/demo/script/process/inspector +3 -0
  73. data/doc/demo/script/process/reaper +3 -0
  74. data/doc/demo/script/process/spawner +3 -0
  75. data/doc/demo/script/process/spinner +3 -0
  76. data/doc/demo/script/runner +3 -0
  77. data/doc/demo/script/server +3 -0
  78. data/doc/demo/test/fixtures/comments.yml +12 -0
  79. data/doc/demo/test/fixtures/contents.yml +13 -0
  80. data/doc/demo/test/fixtures/remote_contents.yml +9 -0
  81. data/doc/demo/test/fixtures/shared_index1s.yml +7 -0
  82. data/doc/demo/test/fixtures/shared_index2s.yml +7 -0
  83. data/doc/demo/test/functional/admin/backend_controller_test.rb +35 -0
  84. data/doc/demo/test/functional/contents_controller_test.rb +81 -0
  85. data/doc/demo/test/functional/searches_controller_test.rb +71 -0
  86. data/doc/demo/test/smoke/drb_smoke_test.rb +321 -0
  87. data/doc/demo/test/smoke/process_stats.rb +21 -0
  88. data/doc/demo/test/test_helper.rb +30 -0
  89. data/doc/demo/test/unit/comment_test.rb +217 -0
  90. data/doc/demo/test/unit/content_test.rb +705 -0
  91. data/doc/demo/test/unit/ferret_result_test.rb +24 -0
  92. data/doc/demo/test/unit/multi_index_test.rb +329 -0
  93. data/doc/demo/test/unit/remote_index_test.rb +23 -0
  94. data/doc/demo/test/unit/shared_index1_test.rb +108 -0
  95. data/doc/demo/test/unit/shared_index2_test.rb +13 -0
  96. data/doc/demo/test/unit/sort_test.rb +21 -0
  97. data/doc/demo/test/unit/special_content_test.rb +25 -0
  98. data/doc/demo/vendor/plugins/will_paginate/LICENSE +18 -0
  99. data/doc/demo/vendor/plugins/will_paginate/README +108 -0
  100. data/doc/demo/vendor/plugins/will_paginate/Rakefile +23 -0
  101. data/doc/demo/vendor/plugins/will_paginate/init.rb +21 -0
  102. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +45 -0
  103. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb +44 -0
  104. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/finder.rb +159 -0
  105. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb +95 -0
  106. data/doc/demo/vendor/plugins/will_paginate/test/array_pagination_test.rb +23 -0
  107. data/doc/demo/vendor/plugins/will_paginate/test/boot.rb +27 -0
  108. data/doc/demo/vendor/plugins/will_paginate/test/console +10 -0
  109. data/doc/demo/vendor/plugins/will_paginate/test/finder_test.rb +219 -0
  110. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/admin.rb +3 -0
  111. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/companies.yml +24 -0
  112. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/company.rb +23 -0
  113. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developer.rb +11 -0
  114. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml +13 -0
  115. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/project.rb +4 -0
  116. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/projects.yml +7 -0
  117. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/replies.yml +20 -0
  118. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/reply.rb +5 -0
  119. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/schema.sql +44 -0
  120. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topic.rb +19 -0
  121. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topics.yml +30 -0
  122. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/user.rb +2 -0
  123. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/users.yml +35 -0
  124. data/doc/demo/vendor/plugins/will_paginate/test/helper.rb +42 -0
  125. data/doc/demo/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb +64 -0
  126. data/doc/demo/vendor/plugins/will_paginate/test/lib/load_fixtures.rb +10 -0
  127. data/doc/demo/vendor/plugins/will_paginate/test/pagination_test.rb +136 -0
  128. data/init.rb +2 -0
  129. data/lib/act_methods.rb +50 -157
  130. data/lib/acts_as_ferret.rb +457 -23
  131. data/lib/ar_mysql_auto_reconnect_patch.rb +41 -0
  132. data/lib/blank_slate.rb +53 -0
  133. data/lib/bulk_indexer.rb +4 -1
  134. data/lib/class_methods.rb +106 -295
  135. data/lib/ferret_extensions.rb +78 -5
  136. data/lib/ferret_find_methods.rb +141 -0
  137. data/lib/ferret_result.rb +26 -9
  138. data/lib/ferret_server.rb +100 -65
  139. data/lib/index.rb +83 -15
  140. data/lib/instance_methods.rb +32 -17
  141. data/lib/local_index.rb +106 -112
  142. data/lib/more_like_this.rb +13 -13
  143. data/lib/multi_index.rb +115 -72
  144. data/lib/rdig_adapter.rb +148 -0
  145. data/lib/remote_functions.rb +43 -0
  146. data/lib/remote_index.rb +25 -21
  147. data/lib/remote_multi_index.rb +20 -0
  148. data/lib/search_results.rb +7 -10
  149. data/lib/server_manager.rb +16 -4
  150. data/lib/unix_daemon.rb +23 -0
  151. data/lib/without_ar.rb +52 -0
  152. data/rakefile +23 -16
  153. data/recipes/aaf_recipes.rb +114 -0
  154. data/tasks/ferret.rake +22 -0
  155. metadata +233 -54
  156. data/lib/ferret_cap_tasks.rb +0 -21
  157. data/lib/shared_index.rb +0 -14
  158. data/lib/shared_index_class_methods.rb +0 -90
@@ -0,0 +1,41 @@
1
+ # Source: http://pastie.caboo.se/154842
2
+ #
3
+ # in /etc/my.cnf on the MySQL server, you can set the interactive-timeout parameter,
4
+ # for example, 12 hours = 28800 sec
5
+ # interactive-timeout=28800
6
+
7
+ # in ActiveRecord, setting the verification_timeout to something less than
8
+ # the interactive-timeout parameter; 14400 sec = 6 hours
9
+ ActiveRecord::Base.verification_timeout = 14400
10
+ ActiveRecord::Base.establish_connection
11
+
12
+ # Below is a monkey patch for keeping ActiveRecord connections alive.
13
+ # http://www.sparecycles.org/2007/7/2/saying-goodbye-to-lost-connections-in-rails
14
+
15
+ module ActiveRecord
16
+ module ConnectionAdapters
17
+ class MysqlAdapter
18
+ def execute(sql, name = nil) #:nodoc:
19
+ reconnect_lost_connections = true
20
+ begin
21
+ log(sql, name) { @connection.query(sql) }
22
+ rescue ActiveRecord::StatementInvalid => exception
23
+ if reconnect_lost_connections and exception.message =~ /(Lost connection to MySQL server during query
24
+ |MySQL server has gone away)/
25
+ reconnect_lost_connections = false
26
+ reconnect!
27
+ retry
28
+ elsif exception.message.split(":").first =~ /Packets out of order/
29
+ raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database.
30
+ Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hash
31
+ ing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql
32
+ bindings."
33
+ else
34
+ raise
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,53 @@
1
+ if defined?(BlankSlate)
2
+ # Rails 2.x has it already
3
+ module ActsAsFerret
4
+ class BlankSlate < ::BlankSlate
5
+ end
6
+ end
7
+ else
8
+ module ActsAsFerret
9
+ # 'backported' for Rails pre 2.0
10
+ #
11
+ #--
12
+ # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
13
+ # All rights reserved.
14
+
15
+ # Permission is granted for use, copying, modification, distribution,
16
+ # and distribution of modified versions of this work as long as the
17
+ # above copyright notice is included.
18
+ #++
19
+
20
+ ######################################################################
21
+ # BlankSlate provides an abstract base class with no predefined
22
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
23
+ # BlankSlate is useful as a base class when writing classes that
24
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
25
+ #
26
+ class BlankSlate
27
+ class << self
28
+ # Hide the method named +name+ in the BlankSlate class. Don't
29
+ # hide +instance_eval+ or any method beginning with "__".
30
+ def hide(name)
31
+ if instance_methods.include?(name.to_s) and name !~ /^(__|instance_eval|methods)/
32
+ @hidden_methods ||= {}
33
+ @hidden_methods[name.to_sym] = instance_method(name)
34
+ undef_method name
35
+ end
36
+ end
37
+
38
+ # Redefine a previously hidden method so that it may be called on a blank
39
+ # slate object.
40
+ #
41
+ # no-op here since we don't hide the methods we reveal where this is
42
+ # used in this implementation
43
+ def reveal(name)
44
+ end
45
+ end
46
+
47
+ instance_methods.each { |m| hide(m) }
48
+
49
+ end
50
+ end
51
+
52
+ end
53
+
data/lib/bulk_indexer.rb CHANGED
@@ -16,7 +16,10 @@ module ActsAsFerret
16
16
 
17
17
  def index_records(records, offset)
18
18
  batch_time = measure_time {
19
- records.each { |rec| @index << rec.to_doc if rec.ferret_enabled?(true) }
19
+ docs = []
20
+ records.each { |rec| docs << [rec.to_doc, rec.ferret_analyzer] if rec.ferret_enabled?(true) }
21
+ @index.update_batch(docs)
22
+ # records.each { |rec| @index.add_document(rec.to_doc, rec.ferret_analyzer) if rec.ferret_enabled?(true) }
20
23
  }.to_f
21
24
  @work_done = offset.to_f / @model_count * 100.0 if @model_count > 0
22
25
  remaining_time = ( batch_time / @batch_size ) * ( @model_count - offset + @batch_size )
data/lib/class_methods.rb CHANGED
@@ -20,18 +20,12 @@ module ActsAsFerret
20
20
  aaf_configuration[:enabled]
21
21
  end
22
22
 
23
- # rebuild the index from all data stored for this model.
23
+ # rebuild the index from all data stored for this model, and any other
24
+ # model classes associated with the same index.
24
25
  # This is called automatically when no index exists yet.
25
26
  #
26
- # When calling this method manually, you can give any additional
27
- # model classes that should also go into this index as parameters.
28
- # Useful when using the :single_index option.
29
- # Note that attributes named the same in different models will share
30
- # the same field options in the shared index.
31
- def rebuild_index(*models)
32
- models << self unless models.include?(self)
33
- aaf_index.rebuild_index models.map(&:to_s)
34
- index_dir = find_last_index_version(aaf_configuration[:index_base_dir]) unless aaf_configuration[:remote]
27
+ def rebuild_index
28
+ aaf_index.rebuild_index
35
29
  end
36
30
 
37
31
  # re-index a number records specified by the given ids. Use for large
@@ -43,7 +37,7 @@ module ActsAsFerret
43
37
  def bulk_index(*ids)
44
38
  options = Hash === ids.last ? ids.pop : {}
45
39
  ids = ids.first if ids.size == 1 && ids.first.is_a?(Enumerable)
46
- aaf_index.bulk_index(ids, options)
40
+ aaf_index.bulk_index(self.name, ids, options)
47
41
  end
48
42
 
49
43
  # true if our db and table appear to be suitable for the mysql fast batch
@@ -56,6 +50,25 @@ module ActsAsFerret
56
50
  end
57
51
  end
58
52
 
53
+ # Returns all records modified or created after the specified time.
54
+ # Used by the rake rebuild task to find models that need to be updated in
55
+ # the index after the rebuild finished because they changed while the
56
+ # rebuild was running.
57
+ # Override if your models don't stick to the created_at/updated_at
58
+ # convention.
59
+ def records_modified_since(time)
60
+ condition = []
61
+ %w(updated_at created_at).each do |col|
62
+ condition << "#{col} >= ?" if column_names.include? col
63
+ end
64
+ if condition.empty?
65
+ logger.warn "#{self.name}: Override records_modified_since(time) to keep the index up to date with records changed during rebuild."
66
+ []
67
+ else
68
+ find :all, :conditions => [ condition.join(' AND '), *([time]*condition.size) ]
69
+ end
70
+ end
71
+
59
72
  # runs across all records yielding those to be indexed when the index is rebuilt
60
73
  def records_for_rebuild(batch_size = 1000)
61
74
  transaction do
@@ -66,8 +79,7 @@ module ActsAsFerret
66
79
  yield rows, offset
67
80
  end
68
81
  else
69
- # sql server adapter won't batch correctly without defined ordering
70
- order = "#{primary_key} ASC" if connection.class.name =~ /SQLServer/
82
+ order = "#{primary_key} ASC" # fixes #212
71
83
  0.step(self.count, batch_size) do |offset|
72
84
  yield find( :all, :limit => batch_size, :offset => offset, :order => order ), offset
73
85
  end
@@ -80,9 +92,7 @@ module ActsAsFerret
80
92
  transaction do
81
93
  offset = 0
82
94
  ids.each_slice(batch_size) do |id_slice|
83
- logger.debug "########## slice: #{id_slice.join(',')}"
84
95
  records = find( :all, :conditions => ["id in (?)", id_slice] )
85
- logger.debug "########## slice records: #{records.inspect}"
86
96
  #yield records, offset
87
97
  yield find( :all, :conditions => ["id in (?)", id_slice] ), offset
88
98
  offset += batch_size
@@ -90,23 +100,11 @@ module ActsAsFerret
90
100
  end
91
101
  end
92
102
 
93
- # Switches this class to a new index located in dir.
94
- # Used by the DRb server when switching to a new index version.
95
- def index_dir=(dir)
96
- logger.debug "changing index dir to #{dir}"
97
- aaf_configuration[:index_dir] = aaf_configuration[:ferret][:path] = dir
98
- aaf_index.reopen!
99
- logger.debug "index dir is now #{dir}"
100
- end
101
-
102
103
  # Retrieve the index instance for this model class. This can either be a
103
104
  # LocalIndex, or a RemoteIndex instance.
104
105
  #
105
- # Index instances are stored in a hash, using the index directory
106
- # as the key. So model classes sharing a single index will share their
107
- # Index object, too.
108
106
  def aaf_index
109
- ActsAsFerret::ferret_indexes[aaf_configuration[:index_dir]] ||= create_index_instance
107
+ @index ||= ActsAsFerret::get_index(aaf_configuration[:name])
110
108
  end
111
109
 
112
110
  # Finds instances by searching the Ferret index. Terms are ANDed by default, use
@@ -131,12 +129,6 @@ module ActsAsFerret
131
129
  # stored fields. Note that if you have a shared index, you have
132
130
  # to explicitly state the fields you want to fetch, true won't
133
131
  # work here)
134
- # models:: only for single_index scenarios: an Array of other Model classes to
135
- # include in this search. Use :all to query all models.
136
- # multi:: Specify additional model classes to search through. Each of
137
- # these, as well as this class, has to have the
138
- # :store_class_name option set to true. This option replaces the
139
- # multi_search method.
140
132
  #
141
133
  # +find_options+ is a hash passed on to active_record's find when
142
134
  # retrieving the data from db, useful to i.e. prefetch relationships with
@@ -153,61 +145,23 @@ module ActsAsFerret
153
145
  # +page+ and +per_page+ are supposed to work regardless of any
154
146
  # +conitions+ present in +find_options+.
155
147
  def find_with_ferret(q, options = {}, find_options = {})
156
- if options[:per_page]
157
- options[:page] = options[:page] ? options[:page].to_i : 1
158
- limit = options[:per_page]
159
- offset = (options[:page] - 1) * limit
160
- if find_options[:conditions] && !options[:multi]
161
- find_options[:limit] = limit
162
- find_options[:offset] = offset
163
- options[:limit] = :all
164
- options.delete :offset
165
- else
166
- # do pagination with ferret (or after everything is done in the case
167
- # of multi_search)
168
- options[:limit] = limit
169
- options[:offset] = offset
170
- end
171
- elsif find_options[:conditions]
172
- if options[:multi]
173
- # multisearch ignores find_options limit and offset
174
- options[:limit] ||= find_options.delete(:limit)
175
- options[:offset] ||= find_options.delete(:offset)
148
+ if respond_to?(:scope) && scope(:find, :conditions)
149
+ if find_options[:conditions]
150
+ find_options[:conditions] = "(#{find_options[:conditions]}) AND (#{scope(:find, :conditions)})"
176
151
  else
177
- # let the db do the limiting and offsetting for single-table searches
178
- find_options[:limit] ||= options.delete(:limit)
179
- find_options[:offset] ||= options.delete(:offset)
180
- options[:limit] = :all
152
+ find_options[:conditions] = scope(:find, :conditions)
181
153
  end
182
154
  end
183
-
184
- total_hits, result = if options[:multi].blank?
185
- find_records_lazy_or_not q, options, find_options
186
- else
187
- _multi_search q, options.delete(:multi), options, find_options
188
- end
189
- logger.debug "Query: #{q}\ntotal hits: #{total_hits}, results delivered: #{result.size}"
190
- SearchResults.new(result, total_hits, options[:page], options[:per_page])
155
+ return ActsAsFerret::find q, self, options, find_options
191
156
  end
192
- alias find_by_contents find_with_ferret
193
157
 
194
-
195
158
 
196
159
  # Returns the total number of hits for the given query
197
- # To count the results of a query across multiple models, specify an array of
198
- # class names with the :multi option.
199
160
  #
200
161
  # Note that since we don't query the database here, this method won't deliver
201
162
  # the expected results when used on an AR association.
163
+ #
202
164
  def total_hits(q, options={})
203
- if options[:models]
204
- # backwards compatibility
205
- logger.warn "the :models option of total_hits is deprecated, please use :multi instead"
206
- options[:multi] = options[:models]
207
- end
208
- if models = options[:multi]
209
- options[:multi] = add_self_to_model_list_if_necessary(models).map(&:to_s)
210
- end
211
165
  aaf_index.total_hits(q, options)
212
166
  end
213
167
 
@@ -215,243 +169,100 @@ module ActsAsFerret
215
169
  # Useful e.g. if you want to search across models or do not want to fetch
216
170
  # all result records (yet).
217
171
  #
218
- # Options are the same as for find_by_contents
172
+ # Options are the same as for find_with_ferret
219
173
  #
220
174
  # A block can be given too, it will be executed with every result:
221
- # find_id_by_contents(q, options) do |model, id, score|
175
+ # find_ids_with_ferret(q, options) do |model, id, score|
222
176
  # id_array << id
223
177
  # scores_by_id[id] = score
224
178
  # end
225
179
  # NOTE: in case a block is given, only the total_hits value will be returned
226
180
  # instead of the [total_hits, results] array!
227
181
  #
228
- def find_id_by_contents(q, options = {}, &block)
229
- deprecated_options_support(options)
230
- aaf_index.find_id_by_contents(q, options, &block)
182
+ def find_ids_with_ferret(q, options = {}, &block)
183
+ aaf_index.find_ids(q, options, &block)
231
184
  end
232
185
 
233
186
 
234
- # returns an array of hashes, each containing :class_name,
235
- # :id and :score for a hit.
236
- #
237
- # if a block is given, class_name, id and score of each hit will
238
- # be yielded, and the total number of hits is returned.
239
- def id_multi_search(query, additional_models = [], options = {}, &proc)
240
- deprecated_options_support(options)
241
- models = add_self_to_model_list_if_necessary(additional_models)
242
- aaf_index.id_multi_search(query, models.map(&:to_s), options, &proc)
243
- end
244
-
245
-
246
187
  protected
247
188
 
248
- def _multi_search(query, additional_models = [], options = {}, find_options = {})
249
- result = []
250
-
251
- if options[:lazy]
252
- logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?
253
- total_hits = id_multi_search(query, additional_models, options) do |model, id, score, data|
254
- result << FerretResult.new(model, id, score, data)
255
- end
256
- else
257
- id_arrays = {}
258
- rank = 0
259
-
260
- limit = options.delete(:limit)
261
- offset = options.delete(:offset) || 0
262
- options[:limit] = :all
263
- total_hits = id_multi_search(query, additional_models, options) do |model, id, score, data|
264
- id_arrays[model] ||= {}
265
- id_arrays[model][id] = [ rank += 1, score ]
266
- end
267
- result = retrieve_records(id_arrays, find_options)
268
- total_hits = result.size if find_options[:conditions]
269
- # total_hits += offset if offset
270
- if limit && limit != :all
271
- result = result[offset..limit+offset-1]
272
- end
273
- end
274
- [total_hits, result]
275
- end
276
-
277
- def add_self_to_model_list_if_necessary(models)
278
- models = [ models ] unless models.is_a? Array
279
- models << self unless models.include?(self)
280
- models
281
- end
282
-
283
- def find_records_lazy_or_not(q, options = {}, find_options = {})
284
- if options[:lazy]
285
- logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?
286
- lazy_find_by_contents q, options
287
- else
288
- ar_find_by_contents q, options, find_options
289
- end
290
- end
291
-
292
- def ar_find_by_contents(q, options = {}, find_options = {})
293
- result_ids = {}
294
- total_hits = find_id_by_contents(q, options) do |model, id, score, data|
295
- # stores ids, index and score of each hit for later ordering of
296
- # results
297
- result_ids[id] = [ result_ids.size + 1, score ]
298
- end
299
-
300
- result = retrieve_records( { self.name => result_ids }, find_options )
301
-
302
- # count total_hits via sql when using conditions or when we're called
303
- # from an ActiveRecord association.
304
- if find_options[:conditions] or caller.find{ |call| call =~ %r{active_record/associations} }
305
- # chances are the ferret result count is not our total_hits value, so
306
- # we correct this here.
307
- if options[:limit] != :all || options[:page] || options[:offset] || find_options[:limit] || find_options[:offset]
308
- # our ferret result has been limited, so we need to re-run that
309
- # search to get the full result set from ferret.
310
- result_ids = {}
311
- find_id_by_contents(q, options.update(:limit => :all, :offset => 0)) do |model, id, score, data|
312
- result_ids[id] = [ result_ids.size + 1, score ]
313
- end
314
- # Now ask the database for the total size of the final result set.
315
- total_hits = count_records( { self.name => result_ids }, find_options )
316
- else
317
- # what we got from the database is our full result set, so take
318
- # it's size
319
- total_hits = result.length
320
- end
321
- end
322
-
323
- [ total_hits, result ]
324
- end
325
-
326
- def lazy_find_by_contents(q, options = {})
327
- result = []
328
- total_hits = find_id_by_contents(q, options) do |model, id, score, data|
329
- result << FerretResult.new(model, id, score, data)
330
- end
331
- [ total_hits, result ]
332
- end
189
+ # def find_records_lazy_or_not(q, options = {}, find_options = {})
190
+ # if options[:lazy]
191
+ # logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?
192
+ # lazy_find_by_contents q, options
193
+ # else
194
+ # ar_find_by_contents q, options, find_options
195
+ # end
196
+ # end
197
+ #
198
+ # def ar_find_by_contents(q, options = {}, find_options = {})
199
+ # result_ids = {}
200
+ # total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
201
+ # # stores ids, index and score of each hit for later ordering of
202
+ # # results
203
+ # result_ids[id] = [ result_ids.size + 1, score ]
204
+ # end
205
+ #
206
+ # result = ActsAsFerret::retrieve_records( { self.name => result_ids }, find_options )
207
+ #
208
+ # # count total_hits via sql when using conditions or when we're called
209
+ # # from an ActiveRecord association.
210
+ # if find_options[:conditions] or caller.find{ |call| call =~ %r{active_record/associations} }
211
+ # # chances are the ferret result count is not our total_hits value, so
212
+ # # we correct this here.
213
+ # if options[:limit] != :all || options[:page] || options[:offset] || find_options[:limit] || find_options[:offset]
214
+ # # our ferret result has been limited, so we need to re-run that
215
+ # # search to get the full result set from ferret.
216
+ # result_ids = {}
217
+ # find_ids_with_ferret(q, options.update(:limit => :all, :offset => 0)) do |model, id, score, data|
218
+ # result_ids[id] = [ result_ids.size + 1, score ]
219
+ # end
220
+ # # Now ask the database for the total size of the final result set.
221
+ # total_hits = count_records( { self.name => result_ids }, find_options )
222
+ # else
223
+ # # what we got from the database is our full result set, so take
224
+ # # it's size
225
+ # total_hits = result.length
226
+ # end
227
+ # end
228
+ #
229
+ # [ total_hits, result ]
230
+ # end
231
+ #
232
+ # def lazy_find_by_contents(q, options = {})
233
+ # logger.debug "lazy_find_by_contents: #{q}"
234
+ # result = []
235
+ # rank = 0
236
+ # total_hits = find_ids_with_ferret(q, options) do |model, id, score, data|
237
+ # logger.debug "model: #{model}, id: #{id}, data: #{data}"
238
+ # result << FerretResult.new(model, id, score, rank += 1, data)
239
+ # end
240
+ # [ total_hits, result ]
241
+ # end
333
242
 
334
243
 
335
244
  def model_find(model, id, find_options = {})
336
245
  model.constantize.find(id, find_options)
337
246
  end
338
247
 
339
- # retrieves search result records from a data structure like this:
340
- # { 'Model1' => { '1' => [ rank, score ], '2' => [ rank, score ] }
341
- #
342
- # TODO: in case of STI AR will filter out hits from other
343
- # classes for us, but this
344
- # will lead to less results retrieved --> scoping of ferret query
345
- # to self.class is still needed.
346
- # from the ferret ML (thanks Curtis Hatter)
347
- # > I created a method in my base STI class so I can scope my query. For scoping
348
- # > I used something like the following line:
349
- # >
350
- # > query << " role:#{self.class.eql?(Contents) '*' : self.class}"
351
- # >
352
- # > Though you could make it more generic by simply asking
353
- # > "self.descends_from_active_record?" which is how rails decides if it should
354
- # > scope your "find" query for STI models. You can check out "base.rb" in
355
- # > activerecord to see that.
356
- # but maybe better do the scoping in find_id_by_contents...
357
- def retrieve_records(id_arrays, find_options = {})
358
- result = []
359
- # get objects for each model
360
- id_arrays.each do |model, id_array|
361
- next if id_array.empty?
362
- begin
363
- model = model.constantize
364
- rescue
365
- raise "Please use ':store_class_name => true' if you want to use multi_search.\n#{$!}"
366
- end
367
248
 
368
- # merge conditions
369
- conditions = combine_conditions([ "#{model.table_name}.#{model.primary_key} in (?)",
370
- id_array.keys ],
371
- find_options[:conditions])
372
-
373
- # check for include association that might only exist on some models in case of multi_search
374
- filtered_include_options = []
375
- if include_options = find_options[:include]
376
- include_options = [ include_options ] unless include_options.respond_to?(:each)
377
- include_options.each do |include_option|
378
- filtered_include_options << include_option if model.reflections.has_key?(include_option.is_a?(Hash) ? include_option.keys[0].to_sym : include_option.to_sym)
379
- end
380
- end
381
- filtered_include_options = nil if filtered_include_options.empty?
382
-
383
- # fetch
384
- tmp_result = model.find(:all, find_options.merge(:conditions => conditions,
385
- :include => filtered_include_options))
386
-
387
- # set scores and rank
388
- tmp_result.each do |record|
389
- record.ferret_rank, record.ferret_score = id_array[record.id.to_s]
390
- end
391
- # merge with result array
392
- result.concat tmp_result
393
- end
394
-
395
- # order results as they were found by ferret, unless an AR :order
396
- # option was given
397
- result.sort! { |a, b| a.ferret_rank <=> b.ferret_rank } unless find_options[:order]
398
- return result
399
- end
400
-
401
- def count_records(id_arrays, find_options = {})
402
- count_options = find_options.dup
403
- count_options.delete :limit
404
- count_options.delete :offset
405
- count = 0
406
- id_arrays.each do |model, id_array|
407
- next if id_array.empty?
408
- begin
409
- model = model.constantize
410
- # merge conditions
411
- conditions = combine_conditions([ "#{model.table_name}.#{model.primary_key} in (?)", id_array.keys ],
412
- find_options[:conditions])
413
- opts = find_options.merge :conditions => conditions
414
- opts.delete :limit; opts.delete :offset
415
- count += model.count opts
416
- rescue TypeError
417
- raise "#{model} must use :store_class_name option if you want to use multi_search against it.\n#{$!}"
418
- end
419
- end
420
- count
421
- end
422
-
423
- def deprecated_options_support(options)
424
- if options[:num_docs]
425
- logger.warn ":num_docs is deprecated, use :limit instead!"
426
- options[:limit] ||= options[:num_docs]
427
- end
428
- if options[:first_doc]
429
- logger.warn ":first_doc is deprecated, use :offset instead!"
430
- options[:offset] ||= options[:first_doc]
431
- end
432
- end
433
-
434
- # creates a new Index instance.
435
- def create_index_instance
436
- if aaf_configuration[:remote]
437
- RemoteIndex
438
- elsif aaf_configuration[:single_index]
439
- SharedIndex
440
- else
441
- LocalIndex
442
- end.new(aaf_configuration)
443
- end
444
-
445
- # combine our conditions with those given by user, if any
446
- def combine_conditions(conditions, additional_conditions = [])
447
- returning conditions do
448
- if additional_conditions && additional_conditions.any?
449
- cust_opts = additional_conditions.respond_to?(:shift) ? additional_conditions.dup : [ additional_conditions ]
450
- conditions.first << " and " << cust_opts.shift
451
- conditions.concat(cust_opts)
452
- end
453
- end
454
- end
249
+ # def count_records(id_arrays, find_options = {})
250
+ # count_options = find_options.dup
251
+ # count_options.delete :limit
252
+ # count_options.delete :offset
253
+ # count = 0
254
+ # id_arrays.each do |model, id_array|
255
+ # next if id_array.empty?
256
+ # model = model.constantize
257
+ # # merge conditions
258
+ # conditions = ActsAsFerret::combine_conditions([ "#{model.table_name}.#{model.primary_key} in (?)", id_array.keys ],
259
+ # find_options[:conditions])
260
+ # opts = find_options.merge :conditions => conditions
261
+ # opts.delete :limit; opts.delete :offset
262
+ # count += model.count opts
263
+ # end
264
+ # count
265
+ # end
455
266
 
456
267
  end
457
268