| 94 | | # declare the class level helper methods |
|---|
| 95 | | # which will load the relevant instance methods defined below when invoked |
|---|
| 96 | | module ClassMethods |
|---|
| 97 | | |
|---|
| 98 | | # helper that defines a method that adds the given field to a lucene |
|---|
| 99 | | # document instance |
|---|
| 100 | | def define_to_field_method(field, options = {}) |
|---|
| 101 | | options = { |
|---|
| 102 | | :store => :no, |
|---|
| 103 | | :index => :yes, |
|---|
| 104 | | :term_vector => :with_positions_offsets, |
|---|
| 105 | | :boost => 1.0 }.update(options) |
|---|
| 106 | | fields_for_ferret[field] = options |
|---|
| 107 | | define_method("#{field}_to_ferret".to_sym) do |
|---|
| 108 | | begin |
|---|
| 109 | | val = content_for_field_name(field) |
|---|
| 110 | | rescue |
|---|
| 111 | | logger.warn("Error retrieving value for field #{field}: #{$!}") |
|---|
| 112 | | val = '' |
|---|
| 113 | | end |
|---|
| 114 | | logger.debug("Adding field #{field} with value '#{val}' to index") |
|---|
| 115 | | val |
|---|
| 116 | | end |
|---|
| 117 | | end |
|---|
| 118 | | |
|---|
| 119 | | def add_fields(field_config) |
|---|
| 120 | | if field_config.respond_to?(:each_pair) |
|---|
| 121 | | field_config.each_pair do |key,val| |
|---|
| 122 | | define_to_field_method(key,val) |
|---|
| 123 | | end |
|---|
| 124 | | elsif field_config.respond_to?(:each) |
|---|
| 125 | | field_config.each do |field| |
|---|
| 126 | | define_to_field_method(field) |
|---|
| 127 | | end |
|---|
| 128 | | end |
|---|
| 129 | | end |
|---|
| 130 | | |
|---|
| 131 | | def reloadable?; false end |
|---|
| 132 | | |
|---|
| 133 | | @@ferret_indexes = Hash.new |
|---|
| 134 | | def ferret_indexes; @@ferret_indexes end |
|---|
| 135 | | |
|---|
| 136 | | @@multi_indexes = Hash.new |
|---|
| 137 | | def multi_indexes; @@multi_indexes end |
|---|
| 138 | | |
|---|
| 139 | | # declares a class as ferret-searchable. |
|---|
| 140 | | # |
|---|
| 141 | | # options are: |
|---|
| 142 | | # |
|---|
| 143 | | # fields:: names all fields to include in the index. If not given, |
|---|
| 144 | | # all attributes of the class will be indexed. You may also give |
|---|
| 145 | | # symbols pointing to instance methods of your model here, i.e. |
|---|
| 146 | | # to retrieve and index data from a related model. |
|---|
| 147 | | # |
|---|
| 148 | | # additional_fields:: names fields to include in the index, in addition |
|---|
| 149 | | # to those derived from the db scheme. use if you want to add |
|---|
| 150 | | # custom fields derived from methods to the db fields (which will be picked |
|---|
| 151 | | # by aaf). This option will be ignored when the fields option is given, in |
|---|
| 152 | | # that case additional fields get specified there. |
|---|
| 153 | | # |
|---|
| 154 | | # index_dir:: declares the directory where to put the index for this class. |
|---|
| 155 | | # The default is RAILS_ROOT/index/RAILS_ENV/CLASSNAME. |
|---|
| 156 | | # The index directory will be created if it doesn't exist. |
|---|
| 157 | | # |
|---|
| 158 | | # single_index:: set this to true to let this class use a Ferret |
|---|
| 159 | | # index that is shared by all classes having :single_index set to true. |
|---|
| 160 | | # :store_class_name is set to true implicitly, as well as index_dir, so |
|---|
| 161 | | # don't bother setting these when using this option. the shared index |
|---|
| 162 | | # will be located in index/<RAILS_ENV>/shared . |
|---|
| 163 | | # |
|---|
| 164 | | # store_class_name:: to make search across multiple models useful, set |
|---|
| 165 | | # this to true. the model class name will be stored in a keyword field |
|---|
| 166 | | # named class_name |
|---|
| 167 | | # |
|---|
| 168 | | # max_results:: number of results to retrieve for :num_docs => :all, |
|---|
| 169 | | # default value is 1000 |
|---|
| 170 | | # |
|---|
| 171 | | # ferret_options may be: |
|---|
| 172 | | # or_default:: - whether query terms are required by |
|---|
| 173 | | # default (the default, false), or not (true) |
|---|
| 174 | | # |
|---|
| 175 | | # analyzer:: the analyzer to use for query parsing (default: nil, |
|---|
| 176 | | # wihch means the ferret StandardAnalyzer gets used) |
|---|
| 177 | | # |
|---|
| 178 | | def acts_as_ferret(options={}, ferret_options={}) |
|---|
| 179 | | configuration = { |
|---|
| 180 | | :index_dir => "#{FerretMixin::Acts::ARFerret::index_dir}/#{self.name.underscore}", |
|---|
| 181 | | :store_class_name => false, |
|---|
| 182 | | :single_index => false, |
|---|
| 183 | | :max_results => 1000 |
|---|
| 184 | | } |
|---|
| 185 | | ferret_configuration = { |
|---|
| 186 | | :or_default => false, |
|---|
| 187 | | :handle_parser_errors => true |
|---|
| 188 | | #:max_clauses => 512, |
|---|
| 189 | | #:default_field => '*', |
|---|
| 190 | | #:analyzer => Ferret::Analysis::StandardAnalyzer.new, |
|---|
| 191 | | # :wild_card_downcase => true |
|---|
| 192 | | } |
|---|
| 193 | | configuration.update(options) if options.is_a?(Hash) |
|---|
| 194 | | |
|---|
| 195 | | # apply appropriate settings for shared index |
|---|
| 196 | | if configuration[:single_index] |
|---|
| 197 | | configuration[:index_dir] = "#{FerretMixin::Acts::ARFerret::index_dir}/shared" |
|---|
| 198 | | configuration[:store_class_name] = true |
|---|
| 199 | | end |
|---|
| 200 | | ferret_configuration.update(ferret_options) if ferret_options.is_a?(Hash) |
|---|
| 201 | | # these properties are somewhat vital to the plugin and shouldn't |
|---|
| 202 | | # be overwritten by the user: |
|---|
| 203 | | ferret_configuration.update( |
|---|
| 204 | | |
|---|
| 205 | | :key => (configuration[:single_index] ? [:id, :class_name] : :id), |
|---|
| 206 | | :path => configuration[:index_dir], |
|---|
| 207 | | :auto_flush => true, |
|---|
| 208 | | :create_if_missing => true |
|---|
| 209 | | ) |
|---|
| 210 | | |
|---|
| 211 | | class_eval <<-EOV |
|---|
| 212 | | include FerretMixin::Acts::ARFerret::InstanceMethods |
|---|
| 213 | | |
|---|
| 214 | | |
|---|
| 215 | | after_create :ferret_create |
|---|
| 216 | | after_update :ferret_update |
|---|
| 217 | | after_destroy :ferret_destroy |
|---|
| 218 | | |
|---|
| 219 | | cattr_accessor :fields_for_ferret |
|---|
| 220 | | cattr_accessor :configuration |
|---|
| 221 | | cattr_accessor :ferret_configuration |
|---|
| 222 | | |
|---|
| 223 | | @@fields_for_ferret = Hash.new |
|---|
| 224 | | @@configuration = configuration |
|---|
| 225 | | @@ferret_configuration = ferret_configuration |
|---|
| 226 | | |
|---|
| 227 | | if configuration[:fields] |
|---|
| 228 | | add_fields(configuration[:fields]) |
|---|
| 229 | | else |
|---|
| 230 | | add_fields(self.new.attributes.keys.map { |k| k.to_sym }) |
|---|
| 231 | | add_fields(configuration[:additional_fields]) |
|---|
| 232 | | end |
|---|
| 233 | | |
|---|
| 234 | | EOV |
|---|
| 235 | | FerretMixin::Acts::ARFerret::ensure_directory configuration[:index_dir] |
|---|
| 236 | | end |
|---|
| 237 | | |
|---|
| 238 | | def class_index_dir |
|---|
| 239 | | configuration[:index_dir] |
|---|
| 240 | | end |
|---|
| 241 | | |
|---|
| 242 | | # rebuild the index from all data stored for this model. |
|---|
| 243 | | # This is called automatically when no index exists yet. |
|---|
| 244 | | # |
|---|
| 245 | | # TODO: the automatic index initialization only works if |
|---|
| 246 | | # every model class has it's |
|---|
| 247 | | # own index, otherwise the index will get populated only |
|---|
| 248 | | # with instances from the first model loaded |
|---|
| 249 | | # |
|---|
| 250 | | # When calling this method manually, you can give any additional |
|---|
| 251 | | # model classes that should also go into this index as parameters. |
|---|
| 252 | | # Useful when using the :single_index option. |
|---|
| 253 | | # Note that attributes named the same in different models will share |
|---|
| 254 | | # the same field options in the shared index. |
|---|
| 255 | | def rebuild_index(*models) |
|---|
| 256 | | models << self |
|---|
| 257 | | # default attributes for fields |
|---|
| 258 | | fi = Ferret::Index::FieldInfos.new(:store => :no, |
|---|
| 259 | | :index => :yes, |
|---|
| 260 | | :term_vector => :no, |
|---|
| 261 | | :boost => 1.0) |
|---|
| 262 | | # primary key |
|---|
| 263 | | fi.add_field(:id, :store => :yes, :index => :untokenized) |
|---|
| 264 | | # class_name |
|---|
| 265 | | if configuration[:store_class_name] |
|---|
| 266 | | fi.add_field(:class_name, :store => :yes, :index => :untokenized) |
|---|
| 267 | | end |
|---|
| 268 | | # collect field options from all models |
|---|
| 269 | | fields = {} |
|---|
| 270 | | models.each do |model| |
|---|
| 271 | | fields.update(model.fields_for_ferret) |
|---|
| 272 | | end |
|---|
| 273 | | logger.debug("class #{self.name}: fields for index: #{fields.keys.join(',')}") |
|---|
| 274 | | fields.each_pair do |field, options| |
|---|
| 275 | | fi.add_field(field, { :store => :no, |
|---|
| 276 | | :index => :yes }.update(options)) |
|---|
| 277 | | end |
|---|
| 278 | | fi.create_index(ferret_configuration[:path]) |
|---|
| 279 | | |
|---|
| 280 | | index = Ferret::Index::Index.new(ferret_configuration.dup.update(:auto_flush => false)) |
|---|
| 281 | | batch_size = 1000 |
|---|
| 282 | | models.each do |model| |
|---|
| 283 | | # index in batches of 1000 to limit memory consumption (fixes #24) |
|---|
| 284 | | model.transaction do |
|---|
| 285 | | 0.step(model.count, batch_size) do |i| |
|---|
| 286 | | model.find(:all, :limit => batch_size, :offset => i).each do |rec| |
|---|
| 287 | | index << rec.to_doc |
|---|
| 288 | | end |
|---|
| 289 | | end |
|---|
| 290 | | end |
|---|
| 291 | | end |
|---|
| 292 | | logger.debug("Created Ferret index in: #{class_index_dir}") |
|---|
| 293 | | index.flush |
|---|
| 294 | | index.optimize |
|---|
| 295 | | index.close |
|---|
| 296 | | end |
|---|
| 297 | | |
|---|
| 298 | | # Retrieve the Ferret::Index::Index instance for this model class. |
|---|
| 299 | | # |
|---|
| 300 | | # Index instances are stored in a hash, using the index directory |
|---|
| 301 | | # as the key. So model classes sharing a single index will share their |
|---|
| 302 | | # Index object, too. |
|---|
| 303 | | def ferret_index |
|---|
| 304 | | ferret_indexes[class_index_dir] ||= create_index_instance |
|---|
| 305 | | end |
|---|
| 306 | | |
|---|
| 307 | | # creates a new Index::Index instance. Before that, a check is done |
|---|
| 308 | | # to see if the index exists in the file system. If not, index rebuild |
|---|
| 309 | | # from all model data retrieved by find(:all) is triggered. |
|---|
| 310 | | def create_index_instance |
|---|
| 311 | | rebuild_index unless File.file? "#{class_index_dir}/segments" |
|---|
| 312 | | Ferret::Index::Index.new(ferret_configuration) |
|---|
| 313 | | end |
|---|
| 314 | | |
|---|
| 315 | | # Finds instances by contents. Terms are ANDed by default, can be circumvented |
|---|
| 316 | | # by using OR between terms. |
|---|
| 317 | | # options: |
|---|
| 318 | | # :first_doc - first hit to retrieve (useful for paging) |
|---|
| 319 | | # :num_docs - number of hits to retrieve, or :all to retrieve |
|---|
| 320 | | # max_results results, which by default is 1000 and can be changed in |
|---|
| 321 | | # the call to acts_as_ferret or on demand like this: |
|---|
| 322 | | # Model.configuration[:max_results] = 1000000 |
|---|
| 323 | | # |
|---|
| 324 | | # find_options is a hash passed on to active_record's find when |
|---|
| 325 | | # retrieving the data from db, useful to i.e. prefetch relationships. |
|---|
| 326 | | # |
|---|
| 327 | | # this method returns a SearchResults instance, which really is an Array that has |
|---|
| 328 | | # been decorated with a total_hits accessor that delivers the total |
|---|
| 329 | | # number of hits (including those not fetched because of a low num_docs |
|---|
| 330 | | # value). |
|---|
| 331 | | def find_by_contents(q, options = {}, find_options = {}) |
|---|
| 332 | | # handle shared index |
|---|
| 333 | | return single_index_find_by_contents(q, options, find_options) if configuration[:single_index] |
|---|
| 334 | | id_array = [] |
|---|
| 335 | | id_positions = {} |
|---|
| 336 | | total_hits = find_id_by_contents(q, options) do |model, id, score| |
|---|
| 337 | | id_array << id |
|---|
| 338 | | # store index of this id for later ordering of results |
|---|
| 339 | | id_positions[id] = id_array.size |
|---|
| 340 | | end |
|---|
| 341 | | begin |
|---|
| 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 | | if id_array.empty? |
|---|
| 347 | | result = [] |
|---|
| 348 | | else |
|---|
| 349 | | conditions = [ "#{self.table_name}.id in (?)", id_array ] |
|---|
| 350 | | # combine our conditions with those given by user, if any |
|---|
| 351 | | if find_options[:conditions] |
|---|
| 352 | | cust_opts = find_options[:conditions].dup |
|---|
| 353 | | conditions.first << " and " << cust_opts.shift |
|---|
| 354 | | conditions.concat(cust_opts) |
|---|
| 355 | | end |
|---|
| 356 | | result = self.find(:all, |
|---|
| 357 | | find_options.merge(:conditions => conditions)) |
|---|
| 358 | | end |
|---|
| 359 | | rescue |
|---|
| 360 | | logger.debug "REBUILD YOUR INDEX! One of the id's didn't have an associated record: #{id_array}" |
|---|
| 361 | | end |
|---|
| 362 | | |
|---|
| 363 | | # order results as they were found by ferret, unless an AR :order |
|---|
| 364 | | # option was given |
|---|
| 365 | | unless find_options[:order] |
|---|
| 366 | | result.sort! { |a, b| id_positions[a.id] <=> id_positions[b.id] } |
|---|
| 367 | | end |
|---|
| 368 | | |
|---|
| 369 | | logger.debug "Query: #{q}\nResult id_array: #{id_array.inspect},\nresult: #{result}" |
|---|
| 370 | | return SearchResults.new(result, total_hits) |
|---|
| 371 | | end |
|---|
| 372 | | |
|---|
| 373 | | # determine all field names in the shared index |
|---|
| 374 | | def single_index_field_names(models) |
|---|
| 375 | | @single_index_field_names ||= ( |
|---|
| 376 | | searcher = Ferret::Search::Searcher.new(class_index_dir) |
|---|
| 377 | | if searcher.reader.respond_to?(:get_field_names) |
|---|
| 378 | | (searcher.reader.send(:get_field_names) - ['id', 'class_name']).to_a |
|---|
| 379 | | else |
|---|
| 380 | | puts <<-END |
|---|
| 381 | | unable to retrieve field names for class #{self.name}, please |
|---|
| 382 | | consider naming all indexed fields in your call to acts_as_ferret! |
|---|
| 383 | | END |
|---|
| 384 | | models.map { |m| m.content_columns.map { |col| col.name } }.flatten |
|---|
| 385 | | end |
|---|
| 386 | | ) |
|---|
| 387 | | |
|---|
| 388 | | end |
|---|
| 389 | | |
|---|
| 390 | | # weiter: checken ob ferret-bug, dass wir die queries so selber bauen |
|---|
| 391 | | # muessen - liegt am downcasen des qparsers ? - gucken ob jetzt mit |
|---|
| 392 | | # ferret geht (content_cols) und dave um zugriff auf qp bitten, oder |
|---|
| 393 | | # auf reader |
|---|
| 394 | | def single_index_find_by_contents(q, options = {}, find_options = {}) |
|---|
| 395 | | result = [] |
|---|
| 396 | | |
|---|
| 397 | | unless options[:models] == :all # search needs to be restricted by one or more class names |
|---|
| 398 | | options[:models] ||= [] |
|---|
| 399 | | # add this class to the list of given models |
|---|
| 400 | | options[:models] << self unless options[:models].include?(self) |
|---|
| 401 | | # build query parser TODO: cache these somehow |
|---|
| 402 | | original_query = q |
|---|
| 403 | | if q.is_a? String |
|---|
| 404 | | #class_clauses = [] |
|---|
| 405 | | #options[:models].each do |model| |
|---|
| 406 | | # class_clauses << "class_name:#{model}" |
|---|
| 407 | | #end |
|---|
| 408 | | #q << " AND (#{class_clauses.join(' OR ')})" |
|---|
| 409 | | |
|---|
| 410 | | qp = Ferret::QueryParser.new (ferret_configuration) |
|---|
| 411 | | qp.fields = ferret_index.send(:reader).field_names |
|---|
| 412 | | original_query = qp.parse(q) |
|---|
| 413 | | end |
|---|
| 414 | | #else |
|---|
| 415 | | q = Ferret::Search::BooleanQuery.new |
|---|
| 416 | | q.add_query(original_query, :must) |
|---|
| 417 | | model_query = Ferret::Search::BooleanQuery.new |
|---|
| 418 | | options[:models].each do |model| |
|---|
| 419 | | model_query.add_query(Ferret::Search::TermQuery.new(:class_name, model.name), :should) |
|---|
| 420 | | end |
|---|
| 421 | | q.add_query(model_query, :must) |
|---|
| 422 | | #end |
|---|
| 423 | | end |
|---|
| 424 | | #puts q.to_s |
|---|
| 425 | | total_hits = find_id_by_contents(q, options) do |model, id, score| |
|---|
| 426 | | result << Object.const_get(model).find(id, find_options.dup) |
|---|
| 427 | | end |
|---|
| 428 | | return SearchResults.new(result, total_hits) |
|---|
| 429 | | end |
|---|
| 430 | | protected :single_index_find_by_contents |
|---|
| 431 | | |
|---|
| 432 | | # Finds instance model name, ids and scores by contents. |
|---|
| 433 | | # Useful if you want to search across models |
|---|
| 434 | | # Terms are ANDed by default, can be circumvented by using OR between terms. |
|---|
| 435 | | # |
|---|
| 436 | | # Example controller code (not tested): |
|---|
| 437 | | # def multi_search(query) |
|---|
| 438 | | # result = [] |
|---|
| 439 | | # result << (Model1.find_id_by_contents query) |
|---|
| 440 | | # result << (Model2.find_id_by_contents query) |
|---|
| 441 | | # result << (Model3.find_id_by_contents query) |
|---|
| 442 | | # result.flatten! |
|---|
| 443 | | # result.sort! {|element| element[:score]} |
|---|
| 444 | | # # Figure out for yourself how to retreive and present the data from modelname and id |
|---|
| 445 | | # end |
|---|
| 446 | | # |
|---|
| 447 | | # Note that the scores retrieved this way aren't normalized across |
|---|
| 448 | | # indexes, so that the order of results after sorting by score will |
|---|
| 449 | | # differ from the order you would get when running the same query |
|---|
| 450 | | # on a single index containing all the data from Model1, Model2 |
|---|
| 451 | | # and Model |
|---|
| 452 | | # |
|---|
| 453 | | # options: |
|---|
| 454 | | # :first_doc - first hit to retrieve (useful for paging) |
|---|
| 455 | | # :num_docs - number of hits to retrieve, or :all to retrieve |
|---|
| 456 | | # max_results results, which by default is 1000 and can be changed in |
|---|
| 457 | | # the call to acts_as_ferret or on demand like this: |
|---|
| 458 | | # Model.configuration[:max_results] = 1000000 |
|---|
| 459 | | # |
|---|
| 460 | | # a block can be given too, it will be executed with every result: |
|---|
| 461 | | # find_id_by_contents(q, options) do |model, id, score| |
|---|
| 462 | | # id_array << id |
|---|
| 463 | | # scores_by_id[id] = score |
|---|
| 464 | | # end |
|---|
| 465 | | # NOTE: in case a block is given, the total_hits value will be returned |
|---|
| 466 | | # instead of the result list! |
|---|
| 467 | | # |
|---|
| 468 | | def find_id_by_contents(q, options = {}) |
|---|
| 469 | | deprecated_options_support(options) |
|---|
| 470 | | options[:limit] = configuration[:max_results] if options[:limit] == :all |
|---|
| 471 | | |
|---|
| 472 | | result = [] |
|---|
| 473 | | index = self.ferret_index |
|---|
| 474 | | #hits = index.search(q, options) |
|---|
| 475 | | #hits.each do |hit, score| |
|---|
| 476 | | total_hits = index.search_each(q, options) do |hit, score| |
|---|
| 477 | | # only collect result data if we intend to return it |
|---|
| 478 | | doc = index[hit] |
|---|
| 479 | | model = configuration[:store_class_name] ? doc[:class_name] : self.name |
|---|
| 480 | | if block_given? |
|---|
| 481 | | yield model, doc[:id].to_i, score |
|---|
| 482 | | else |
|---|
| 483 | | result << { :model => model, :id => doc[:id], :score => score } |
|---|
| 484 | | end |
|---|
| 485 | | end |
|---|
| 486 | | logger.debug "id_score_model array: #{result.inspect}" |
|---|
| 487 | | return block_given? ? total_hits : result |
|---|
| 488 | | end |
|---|
| 489 | | |
|---|
| 490 | | # requires the store_class_name option of acts_as_ferret to be true |
|---|
| 491 | | # for all models queried this way. |
|---|
| 492 | | # |
|---|
| 493 | | # TODO: not optimal as each instance is fetched in a db call for it's |
|---|
| 494 | | # own. |
|---|
| 495 | | def multi_search(query, additional_models = [], options = {}) |
|---|
| 496 | | result = [] |
|---|
| 497 | | total_hits = id_multi_search(query, additional_models, options) do |model, id, score| |
|---|
| 498 | | result << Object.const_get(model).find(id) |
|---|
| 499 | | end |
|---|
| 500 | | SearchResults.new(result, total_hits) |
|---|
| 501 | | end |
|---|
| 502 | | |
|---|
| 503 | | # returns an array of hashes, each containing :class_name, |
|---|
| 504 | | # :id and :score for a hit. |
|---|
| 505 | | # |
|---|
| 506 | | # if a block is given, class_name, id and score of each hit will |
|---|
| 507 | | # be yielded, and the total number of hits is returned. |
|---|
| 508 | | # |
|---|
| 509 | | def id_multi_search(query, additional_models = [], options = {}) |
|---|
| 510 | | deprecated_options_support(options) |
|---|
| 511 | | options[:limit] = configuration[:max_results] if options[:limit] == :all |
|---|
| 512 | | additional_models << self |
|---|
| 513 | | searcher = multi_index(additional_models) |
|---|
| 514 | | result = [] |
|---|
| 515 | | total_hits = searcher.search_each (query, options) do |hit, score| |
|---|
| 516 | | doc = searcher[hit] |
|---|
| 517 | | if block_given? |
|---|
| 518 | | yield doc[:class_name], doc[:id].to_i, score |
|---|
| 519 | | else |
|---|
| 520 | | result << { :model => doc[:class_name], :id => doc[:id], :score => score } |
|---|
| 521 | | end |
|---|
| 522 | | end |
|---|
| 523 | | return block_given? ? total_hits : result |
|---|
| 524 | | end |
|---|
| 525 | | |
|---|
| 526 | | # returns a MultiIndex instance operating on a MultiReader |
|---|
| 527 | | def multi_index(model_classes) |
|---|
| 528 | | model_classes.sort! { |a, b| a.name <=> b.name } |
|---|
| 529 | | key = model_classes.inject("") { |s, clazz| s << clazz.name } |
|---|
| 530 | | @@multi_indexes[key] ||= MultiIndex.new(model_classes, ferret_configuration) |
|---|
| 531 | | end |
|---|
| 532 | | |
|---|
| 533 | | def deprecated_options_support(options) |
|---|
| 534 | | if options[:num_docs] |
|---|
| 535 | | logger.warn ":num_docs is deprecated, use :limit instead!" |
|---|
| 536 | | options[:limit] ||= options[:num_docs] |
|---|
| 537 | | end |
|---|
| 538 | | if options[:first_doc] |
|---|
| 539 | | logger.warn ":first_doc is deprecated, use :offset instead!" |
|---|
| 540 | | options[:offset] ||= options[:first_doc] |
|---|
| 541 | | end |
|---|
| 542 | | end |
|---|
| 543 | | end |
|---|