Changeset 319
- Timestamp:
- 02/18/08 20:36:04 (8 months ago)
- Files:
-
- trunk/demo/app/controllers/admin/backend_controller.rb (modified) (1 diff)
- trunk/demo/test/unit/comment_test.rb (modified) (11 diffs)
- trunk/demo/test/unit/content_test.rb (modified) (9 diffs)
- trunk/demo/test/unit/shared_index1_test.rb (modified) (6 diffs)
- trunk/demo/test/unit/special_content_test.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb (modified) (4 diffs)
- trunk/plugin/acts_as_ferret/lib/class_methods.rb (modified) (14 diffs)
- trunk/plugin/acts_as_ferret/lib/index.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/local_index.rb (modified) (4 diffs)
- trunk/plugin/acts_as_ferret/lib/more_like_this.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/multi_index.rb (modified) (6 diffs)
- trunk/plugin/acts_as_ferret/lib/remote_index.rb (modified) (1 diff)
- trunk/plugin/acts_as_ferret/lib/shared_index_class_methods.rb (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/demo/app/controllers/admin/backend_controller.rb
r84 r319 4 4 @query = params[:query] || '' 5 5 unless @query.blank? 6 @results = Content.find_ by_contents@query6 @results = Content.find_with_ferret @query 7 7 end 8 8 end trunk/demo/test/unit/comment_test.rb
r307 r319 31 31 def test_search_for_id 32 32 # don't search the id field by default: 33 assert Comment.find_ by_contents('3').empty?33 assert Comment.find_with_ferret('3').empty? 34 34 # explicit query for id field works: 35 assert_equal 3, Comment.find_ by_contents('id:3').first.id35 assert_equal 3, Comment.find_with_ferret('id:3').first.id 36 36 end 37 37 … … 45 45 # TODO: check why this fails, but querying for 'comment fixture' works. 46 46 # maybe different analyzers at index creation and searching time ? 47 #comments_from_ferret = Comment.find_ by_contents('"comment from fixture"')48 comments_from_ferret = Comment.find_ by_contents('comment fixture')47 #comments_from_ferret = Comment.find_with_ferret('"comment from fixture"') 48 comments_from_ferret = Comment.find_with_ferret('comment fixture') 49 49 assert_equal 2, comments_from_ferret.size 50 50 assert comments_from_ferret.include?(comments(:first)) … … 54 54 def test_rebuild_index 55 55 Comment.aaf_index.ferret_index.query_delete('comment') 56 comments_from_ferret = Comment.find_ by_contents('comment AND fixture')56 comments_from_ferret = Comment.find_with_ferret('comment AND fixture') 57 57 assert comments_from_ferret.empty? 58 58 Comment.rebuild_index 59 comments_from_ferret = Comment.find_ by_contents('comment AND fixture')59 comments_from_ferret = Comment.find_with_ferret('comment AND fixture') 60 60 assert_equal 2, comments_from_ferret.size 61 61 end 62 62 63 63 def test_total_hits 64 comments_from_ferret = Comment.find_ by_contents('comment AND fixture', :limit => 1)64 comments_from_ferret = Comment.find_with_ferret('comment AND fixture', :limit => 1) 65 65 assert_equal 1, comments_from_ferret.size 66 66 assert_equal 2, comments_from_ferret.total_hits 67 67 68 comments_from_ferret = Comment.find_ by_contents('comment AND fixture', {}, :conditions => 'id != 1')68 comments_from_ferret = Comment.find_with_ferret('comment AND fixture', {}, :conditions => 'id != 1') 69 69 assert_equal 1, comments_from_ferret.size 70 70 assert_equal 1, comments_from_ferret.total_hits … … 72 72 73 73 def test_score 74 comments_from_ferret = Comment.find_ by_contents('comment AND fixture', :limit => 1)74 comments_from_ferret = Comment.find_with_ferret('comment AND fixture', :limit => 1) 75 75 assert comments_from_ferret.first 76 76 assert comments_from_ferret.first.ferret_score > 0 … … 81 81 Comment.create( :author => 'multi-commenter', :content => "This is multicomment no #{i}" ) 82 82 end 83 assert_equal 10, (res = Comment.find_ by_contents('multicomment')).size83 assert_equal 10, (res = Comment.find_with_ferret('multicomment')).size 84 84 assert_equal 20, res.total_hits 85 assert_equal 15, (res = Comment.find_ by_contents('multicomment', :limit => 15)).size85 assert_equal 15, (res = Comment.find_with_ferret('multicomment', :limit => 15)).size 86 86 assert_equal 20, res.total_hits 87 assert_equal 20, (res = Comment.find_ by_contents('multicomment', :limit => :all)).size87 assert_equal 20, (res = Comment.find_with_ferret('multicomment', :limit => :all)).size 88 88 assert_equal 20, res.total_hits 89 89 end … … 101 101 end 102 102 103 def test_find_ by_contents103 def test_find_with_ferret 104 104 comment = Comment.create( :author => 'john doe', :content => 'This is a useless comment' ) 105 105 comment2 = Comment.create( :author => 'another', :content => 'content' ) 106 106 107 comments_from_ferret = Comment.find_ by_contents('anoth* OR jo*')107 comments_from_ferret = Comment.find_with_ferret('anoth* OR jo*') 108 108 assert_equal 2, comments_from_ferret.size 109 109 assert comments_from_ferret.include?(comment) … … 111 111 112 112 # find options 113 comments_from_ferret = Comment.find_ by_contents('anoth* OR jo*', {}, :conditions => ["id=?",comment2.id])113 comments_from_ferret = Comment.find_with_ferret('anoth* OR jo*', {}, :conditions => ["id=?",comment2.id]) 114 114 assert_equal 1, comments_from_ferret.size 115 115 assert comments_from_ferret.include?(comment2) 116 116 117 comments_from_ferret = Comment.find_ by_contents('lorem ipsum not here')118 assert comments_from_ferret.empty? 119 120 comments_from_ferret = Comment.find_ by_contents('another')117 comments_from_ferret = Comment.find_with_ferret('lorem ipsum not here') 118 assert comments_from_ferret.empty? 119 120 comments_from_ferret = Comment.find_with_ferret('another') 121 121 assert_equal 1, comments_from_ferret.size 122 122 assert_equal comment2.id, comments_from_ferret.first.id 123 123 124 comments_from_ferret = Comment.find_ by_contents('doe')125 assert_equal 1, comments_from_ferret.size 126 assert_equal comment.id, comments_from_ferret.first.id 127 128 comments_from_ferret = Comment.find_ by_contents('useless')124 comments_from_ferret = Comment.find_with_ferret('doe') 125 assert_equal 1, comments_from_ferret.size 126 assert_equal comment.id, comments_from_ferret.first.id 127 128 comments_from_ferret = Comment.find_with_ferret('useless') 129 129 assert_equal 1, comments_from_ferret.size 130 130 assert_equal comment.id, comments_from_ferret.first.id 131 131 132 132 # no monkeys here 133 comments_from_ferret = Comment.find_ by_contents('monkey')133 comments_from_ferret = Comment.find_with_ferret('monkey') 134 134 assert comments_from_ferret.empty? 135 135 136 136 # multiple terms are ANDed by default... 137 comments_from_ferret = Comment.find_ by_contents('monkey comment')137 comments_from_ferret = Comment.find_with_ferret('monkey comment') 138 138 assert comments_from_ferret.empty? 139 139 # ...unless you connect them by OR 140 comments_from_ferret = Comment.find_ by_contents('monkey OR comment')140 comments_from_ferret = Comment.find_with_ferret('monkey OR comment') 141 141 assert_equal 3, comments_from_ferret.size 142 142 assert comments_from_ferret.include?(comment) … … 146 146 # multiple terms, each term has to occur in a document to be found, 147 147 # but they may occur in different fields 148 comments_from_ferret = Comment.find_ by_contents('useless john')148 comments_from_ferret = Comment.find_with_ferret('useless john') 149 149 assert_equal 1, comments_from_ferret.size 150 150 assert_equal comment.id, comments_from_ferret.first.id … … 152 152 153 153 # search for an exact string by enclosing it in " 154 comments_from_ferret = Comment.find_ by_contents('"useless john"')155 assert comments_from_ferret.empty? 156 comments_from_ferret = Comment.find_ by_contents('"useless comment"')154 comments_from_ferret = Comment.find_with_ferret('"useless john"') 155 assert comments_from_ferret.empty? 156 comments_from_ferret = Comment.find_with_ferret('"useless comment"') 157 157 assert_equal 1, comments_from_ferret.size 158 158 assert_equal comment.id, comments_from_ferret.first.id … … 187 187 comment = Comment.create( :author => 'john doe', :content => 'Move or shake' ) 188 188 ['move shake', 'Move shake', 'move Shake', 'move or shake', 'move the shake'].each do |q| 189 comments_from_ferret = Comment.find_ by_contents(q)189 comments_from_ferret = Comment.find_with_ferret(q) 190 190 assert_equal comment, comments_from_ferret.first, "query #{q} failed" 191 191 end … … 194 194 195 195 def test_array_conditions_combining 196 comments_from_ferret = Comment.find_ by_contents('comment AND fixture', {}, :conditions => [ 'id IN (?)', [ 2, 3 ] ])196 comments_from_ferret = Comment.find_with_ferret('comment AND fixture', {}, :conditions => [ 'id IN (?)', [ 2, 3 ] ]) 197 197 assert_equal 1, comments_from_ferret.size 198 198 assert_equal 1, comments_from_ferret.total_hits trunk/demo/test/unit/content_test.rb
r318 r319 29 29 30 30 def test_limit_all 31 res = Content.find_ by_contents'*', { :limit => :all }, :conditions => ['lower(title) like ?', 'content'], :order => 'contents.description'31 res = Content.find_with_ferret '*', { :limit => :all }, :conditions => ['lower(title) like ?', 'content'], :order => 'contents.description' 32 32 end 33 33 … … 49 49 end 50 50 51 # weiter: single index / multisearch lazy loading52 51 def test_lazy_loading 53 52 results = Content.find_with_ferret 'description', :lazy => true … … 356 355 end 357 356 358 def test_total_hits_multi359 q = '*:title OR *:comment'360 assert_equal 3, Comment.total_hits(q)361 assert_equal 2, Content.total_hits(q)362 assert_equal 5, ActsAsFerret::total_hits(q, [ Comment, Content ])363 end364 365 def test_multi_search_sorting366 sorting = [ Ferret::Search::SortField.new(:id) ]367 result = Content.find_with_ferret('*:title OR *:comment', :multi => [Comment], :sort => sorting)368 assert_equal result.map(&:id).sort, result.map(&:id)369 370 sorting = [ Ferret::Search::SortField.new(:title) ]371 result = Content.find_with_ferret('*:title OR *:comment', :multi => [Comment], :sort => sorting)372 sorting = [ Ferret::Search::SortField.new(:title, :reverse => true) ]373 result2 = Content.find_with_ferret('*:title OR *:comment', :multi => [Comment], :sort => sorting)374 assert result.any?375 assert result.map(&:id) != result2.map(&:id)376 377 sorting = [ Ferret::Search::SortField::SCORE ]378 result = Content.find_with_ferret('*:title OR *:comment', :multi => Comment, :sort => sorting)379 assert result.any?380 assert_equal result.map(&:ferret_score).sort.reverse, result.map(&:ferret_score)381 382 sorting = [ Ferret::Search::SortField::SCORE_REV ]383 result2 = Content.find_with_ferret('*:title OR *:comment', :multi => Comment, :sort => sorting)384 assert_equal result2.map(&:ferret_score).sort, result2.map(&:ferret_score)385 assert_equal result.map(&:ferret_score), result2.map(&:ferret_score).reverse386 end387 388 357 def test_sort_class 389 358 sorting = Ferret::Search::Sort.new(Ferret::Search::SortField.new(:id, :reverse => true)) … … 414 383 end 415 384 416 # remote index rebuilds will create an index in a directory with a timestamped name.417 # the local MultiIndex instance doesn't know about this (because it's running in418 # another interpreter instance than the server) and therefore fails to use the419 # correct index directories.420 # TODO strange, still doesn't work but it should now...421 unless Content.aaf_configuration[:remote]422 def test_multi_index423 i = ActsAsFerret::MultiIndex.new([Content, Comment])424 hits = i.search(TermQuery.new(:title,"title"))425 assert_equal 1, hits.total_hits426 427 qp = Ferret::QueryParser.new(:default_field => "title",428 :analyzer => Ferret::Analysis::WhiteSpaceAnalyzer.new)429 hits = i.search(qp.parse("title"))430 assert_equal 1, hits.total_hits431 432 qp = Ferret::QueryParser.new(:fields => ['title', 'content', 'description'],433 :analyzer => Ferret::Analysis::WhiteSpaceAnalyzer.new)434 hits = i.search(qp.parse("title"))435 assert_equal 2, hits.total_hits436 hits = i.search(qp.parse("title:title OR description:title"))437 assert_equal 2, hits.total_hits438 439 hits = i.search("title:title OR description:title OR title:comment OR description:comment OR content:comment")440 assert_equal 5, hits.total_hits441 442 hits = i.search("title OR comment")443 assert_equal 5, hits.total_hits444 445 hits = i.search("title OR comment", :limit => 2)446 count = 0447 hits.hits.each { |hit, score| count += 1 }448 assert_equal 2, count449 450 hits = i.search("title OR comment", :offset => 2)451 count = 0452 hits.hits.each { |hit, score| count += 1 }453 assert_equal 3, count454 end455 end456 385 457 386 def test_add_rebuilds_index … … 467 396 end 468 397 469 def test_multi_search_rebuilds_index470 remove_index Content471 contents_from_ferret = Content.find_with_ferret('description:title', :multi => Comment)472 assert_equal 1, contents_from_ferret.size473 end474 475 # remote index rebuilds will create an index in a directory with a timestamped name...476 unless Content.aaf_configuration[:remote]477 def test_multi_index_rebuilds_index478 remove_index Content479 i = ActsAsFerret::MultiIndex.new([Content])480 assert File.exists?("#{ActsAsFerret::index_definition(Content)[:index_dir]}/segments")481 hits = i.search("description:title")482 assert_equal 1, hits.total_hits, hits.inspect483 end484 end485 486 def test_multi_search_find_options487 contents_from_ferret = Content.find_with_ferret('title', { :multi => Comment }, :order => 'id desc')488 assert_equal 2, contents_from_ferret.size489 assert contents_from_ferret.first.id > contents_from_ferret.last.id490 contents_from_ferret = Content.find_with_ferret('title', { :multi => Comment }, :order => 'id asc')491 assert contents_from_ferret.first.id < contents_from_ferret.last.id492 493 contents_from_ferret = Content.find_with_ferret('title', :multi => Comment, :limit => 1)494 assert_equal 1, contents_from_ferret.size495 contents_from_ferret = Content.find_with_ferret('title', { :multi => Comment }, :limit => 1)496 assert_equal 1, contents_from_ferret.size497 498 499 more_contents(true)500 r = Content.find_with_ferret('title OR comment', { :multi => Comment, :limit => :all } )501 assert_equal 60, r.size502 assert_equal 60, r.total_hits503 504 id = Content.find_with_ferret('title').first.id505 r = Content.find_with_ferret('title OR comment', { :multi => Comment, :limit => :all },506 { :conditions => { :content => ["id != ?", id] }})507 assert_equal 59, r.size508 assert_equal 59, r.total_hits509 510 r = Content.find_with_ferret('title OR comment', { :multi => Comment, :limit => 20 },511 { :conditions => { :content => ["id != ?", id] }})512 assert_equal 20, r.size513 assert_equal 59, r.total_hits514 515 r = Content.find_with_ferret('title OR comment', { :multi => Comment, :limit => 20 },516 { :conditions => { :comment => 'content is null',517 :content => ["id != ?", id] }})518 assert_equal 20, r.size519 assert_equal 29, r.total_hits520 521 r = Content.find_with_ferret('title OR comment', { :multi => Comment },522 { :conditions => { :content => ["id != ?", id] }, :limit => 20 })523 assert_equal 20, r.size524 assert_equal 59, r.total_hits525 end526 527 def test_multi_search528 assert_equal 4, ContentBase.find(:all).size529 530 Content.aaf_index.ferret_index.flush531 contents_from_ferret = Content.find_with_ferret('description:title', :multi => [])532 assert_equal 1, contents_from_ferret.size533 contents_from_ferret = Content.find_with_ferret('title:title OR description:title', :multi => [])534 assert_equal 2, contents_from_ferret.size535 contents_from_ferret = Content.find_with_ferret('title:title', :multi => [])536 assert_equal 1, contents_from_ferret.size537 contents_from_ferret = Content.find_with_ferret('*:title', :multi => [])538 assert_equal 2, contents_from_ferret.size539 contents_from_ferret = Content.find_with_ferret('title', :multi => [])540 assert_equal 2, contents_from_ferret.size541 542 assert_equal contents(:first).id, contents_from_ferret.first.id543 assert_equal @another_content.id, contents_from_ferret.last.id544 545 contents_from_ferret = Content.find_with_ferret('title', :multi => [])546 assert_equal 2, contents_from_ferret.size547 contents_from_ferret = Content.find_with_ferret('title', :multi => [], :limit => 1)548 assert_equal 1, contents_from_ferret.size549 contents_from_ferret = Content.find_with_ferret('title', :multi => [], :offset => 1)550 assert_equal 1, contents_from_ferret.size551 552 contents_from_ferret = Content.find_with_ferret('title:title OR content:comment OR description:title', :multi => [Comment])553 assert_equal 5, contents_from_ferret.size554 contents_from_ferret = Content.find_with_ferret('title:title OR content:comment OR description:title', :multi => [Comment], :limit => 2)555 assert_equal 2, contents_from_ferret.size556 557 contents_from_ferret = Content.find_with_ferret('*:title OR *:comment', :multi => Comment)558 assert_equal 5, contents_from_ferret.size559 contents_from_ferret = Content.find_with_ferret('*:title OR *:comment', :multi => [Comment])560 assert_equal 5, contents_from_ferret.size561 contents_from_ferret = Content.find_with_ferret('title:(title OR comment) OR description:(title OR comment) OR content:(title OR comment)', :multi => [Comment])562 assert_equal 5, contents_from_ferret.size563 end564 565 def test_multi_search_lazy566 contents_from_ferret = Content.find_with_ferret('title', :multi => Comment, :lazy => true)567 assert_equal 2, contents_from_ferret.size568 contents_from_ferret.each do |record|569 assert ActsAsFerret::FerretResult === record, record.inspect570 assert !record.description.blank?571 assert_nil record.instance_variable_get(:"@ar_record")572 end573 end574 575 def test_id_multi_search576 assert_equal 4, ContentBase.find(:all).size577 578 [ 'title:title OR description:title OR content:title', 'title', '*:title'].each do |query|579 total_hits, contents_from_ferret = Content.id_multi_search(query)580 assert_equal 2, contents_from_ferret.size, query581 assert_equal 2, total_hits, query582 assert_equal contents(:first).id, contents_from_ferret.first[:id].to_i583 assert_equal @another_content.id, contents_from_ferret.last[:id].to_i584 end585 586 ContentBase.rebuild_index587 Comment.rebuild_index588 ['title OR comment', 'title:(title OR comment) OR description:(title OR comment) OR content:(title OR comment)'].each do |query|589 total_hits, contents_from_ferret = Content.id_multi_search(query, [Comment])590 assert_equal 5, contents_from_ferret.size, query591 assert_equal 5, total_hits592 end593 end594 595 def test_id_multi_search_lazy596 total_hits, contents_from_ferret = Content.id_multi_search('title', [Comment], :lazy => true)597 assert_equal 2, contents_from_ferret.size598 assert_equal 2, total_hits599 found = 0600 contents_from_ferret.each do |data|601 next if data[:model] != 'Content'602 found += 1603 assert !data[:data][:description].blank?604 end605 assert_equal 2, found606 end607 608 398 def test_total_hits 609 399 assert_equal 2, Content.total_hits('title:title OR description:title') … … 611 401 end 612 402 613 def test_find_id _by_contents614 total_hits, contents_from_ferret = Content.find_id _by_contents('title:title OR description:title')403 def test_find_ids_with_ferret 404 total_hits, contents_from_ferret = Content.find_ids_with_ferret('title:title OR description:title') 615 405 assert_equal 2, contents_from_ferret.size 616 406 assert_equal 2, total_hits … … 622 412 623 413 # give description field higher boost: 624 total_hits, contents_from_ferret = Content.find_id _by_contents('title:title OR description:title^200')414 total_hits, contents_from_ferret = Content.find_ids_with_ferret('title:title OR description:title^200') 625 415 assert_equal 2, contents_from_ferret.size 626 416 assert_equal 2, total_hits … … 732 522 end 733 523 734 def test_multi_pagination735 more_contents(true)736 737 r = Content.find_with_ferret 'title OR comment', :multi => Comment, :per_page => 10, :sort => 'title'738 assert_equal 60, r.total_hits739 assert_equal 10, r.size740 assert_equal "0", r.first.description741 assert_equal "9", r.last.description742 assert_equal 1, r.current_page743 assert_equal 6, r.page_count744 745 r = Content.find_with_ferret 'title OR comment', :multi => Comment, :page => '2', :per_page => 10, :sort => 'title'746 assert_equal 60, r.total_hits747 assert_equal 10, r.size748 assert_equal "10", r.first.description749 assert_equal "19", r.last.description750 assert_equal 2, r.current_page751 assert_equal 6, r.page_count752 753 r = Content.find_with_ferret 'title OR comment', :multi => Comment, :page => 7, :per_page => 10, :sort => 'title'754 assert_equal 60, r.total_hits755 assert_equal 0, r.size756 end757 758 524 def test_pagination 759 525 more_contents … … 845 611 assert_equal 3, r.current_page 846 612 assert_equal 3, r.page_count 847 end848 849 def test_multi_pagination_with_ar_conditions850 more_contents(true)851 id = Content.find_with_ferret('title').first.id852 r = Content.find_with_ferret 'title OR comment', { :page => 1, :per_page => 10, :multi => Comment },853 { :conditions => { :content => ["id != ?", id] }, :order => 'id ASC' }854 assert_equal 59, r.total_hits855 assert_equal 10, r.size856 assert_equal "Comment for content 00", r.first.content857 assert_equal "Comment for content 09", r.last.content858 assert_equal 1, r.current_page859 assert_equal 6, r.page_count860 861 r = Content.find_with_ferret 'title OR comment', { :page => 6, :per_page => 10, :multi => Comment },862 { :conditions => { :content => [ "id != ?", id ] }, :order => 'id ASC' }863 assert_equal 59, r.total_hits864 assert_equal 9, r.size865 assert_equal "21", r.first.description866 assert_equal "29", r.last.description867 assert_equal 6, r.current_page868 assert_equal 6, r.page_count869 613 end 870 614 trunk/demo/test/unit/shared_index1_test.rb
r317 r319 9 9 10 10 def test_lazy_loading_shared_index 11 results = SharedIndex1.find_ by_contents'first', :lazy => [ :name ], :models => :all11 results = SharedIndex1.find_with_ferret 'first', :lazy => [ :name ], :models => :all 12 12 assert_equal 2, results.size 13 13 found_lazy_result = false … … 24 24 end 25 25 26 def test_find_id _by_contents27 result = SharedIndex1.find_id _by_contents("first")26 def test_find_ids_with_ferret 27 result = SharedIndex1.find_ids_with_ferret("first") 28 28 assert_equal 2, result.size 29 29 end 30 30 31 def test_find_ by_contents_one_class32 result = SharedIndex1.find_ by_contents("first")31 def test_find_with_ferret_one_class 32 result = SharedIndex1.find_with_ferret("first") 33 33 assert_equal 1, result.size, result.inspect 34 34 assert_equal shared_index1s(:first), result.first 35 35 36 result = SharedIndex1.find_ by_contents("name:first", :models => [SharedIndex1])36 result = SharedIndex1.find_with_ferret("name:first", :models => [SharedIndex1]) 37 37 assert_equal 1, result.size 38 38 assert_equal shared_index1s(:first), result.first … … 40 40 41 41 def test_custom_query 42 result = SharedIndex1.find_ by_contents("name:first class_name:SharedIndex1")42 result = SharedIndex1.find_with_ferret("name:first class_name:SharedIndex1") 43 43 assert_equal 1, result.size 44 44 assert_equal shared_index1s(:first), result.first 45 45 end 46 46 47 def test_find_ by_contents_all_classes48 result = SharedIndex1.find_ by_contents("first", :models => :all)47 def test_find_with_ferret_all_classes 48 result = SharedIndex1.find_with_ferret("first", :models => :all) 49 49 assert_equal 2, result.size 50 50 assert result.include?(shared_index1s(:first)) 51 51 assert result.include?(shared_index2s(:first)) 52 52 53 result = SharedIndex1.find_ by_contents("name:first", :models => [SharedIndex2])53 result = SharedIndex1.find_with_ferret("name:first", :models => [SharedIndex2]) 54 54 assert_equal 2, result.size 55 55 assert result.include?(shared_index1s(:first)) … … 63 63 64 64 def test_destroy 65 result = SharedIndex1.find_ by_contents("first OR another", :models => :all)65 result = SharedIndex1.find_with_ferret("first OR another", :models => :all) 66 66 assert_equal 4, result.size 67 67 SharedIndex1.destroy(shared_index1s(:first)) 68 result = SharedIndex1.find_ by_contents("first OR another", :models => :all)68 result = SharedIndex1.find_with_ferret("first OR another", :models => :all) 69 69 assert_equal 3, result.size 70 70 shared_index2s(:first).destroy 71 result = SharedIndex1.find_ by_contents("first OR another", :models => :all)71 result = SharedIndex1.find_with_ferret("first OR another", :models => :all) 72 72 assert_equal 2, result.size 73 73 end … … 75 75 def test_ferret_destroy 76 76 SharedIndex1.rebuild_index 77 result = SharedIndex1.find_id _by_contents("first OR another", :models => :all)77 result = SharedIndex1.find_ids_with_ferret("first OR another", :models => :all) 78 78 assert_equal 4, result.first 79 79 shared_index1s(:first).ferret_destroy 80 result = SharedIndex1.find_id _by_contents("first OR another", :models => :all)80 result = SharedIndex1.find_ids_with_ferret("first OR another", :models => :all) 81 81 assert_equal 3, result.first 82 82 end … … 84 84 def test_ferret_destroy_ticket_88 85 85 SharedIndex1.rebuild_index 86 result = SharedIndex1.find_id _by_contents("first OR another", :models => :all)86 result = SharedIndex1.find_ids_with_ferret("first OR another", :models => :all) 87 87 assert_equal 4, result.first 88 result = SharedIndex2.find_id _by_contents("first OR another", :models => :all)88 result = SharedIndex2.find_ids_with_ferret("first OR another", :models => :all) 89 89 assert_equal 4, result.first 90 90 SharedIndex1.destroy(shared_index1s(:first)) 91 result = SharedIndex1.find_id _by_contents("first OR another", :models => :all)91 result = SharedIndex1.find_ids_with_ferret("first OR another", :models => :all) 92 92 assert_equal 3, result.first 93 result = SharedIndex2.find_id _by_contents("first OR another", :models => :all)93 result = SharedIndex2.find_ids_with_ferret("first OR another", :models => :all) 94 94 assert_equal 3, result.first 95 95 shared_index2s(:first).destroy 96 result = SharedIndex1.find_id _by_contents("first OR another", :models => :all)96 result = SharedIndex1.find_ids_with_ferret("first OR another", :models => :all) 97 97 assert_equal 2, result.first 98 result = SharedIndex2.find_id _by_contents("first OR another", :models => :all)98 result = SharedIndex2.find_ids_with_ferret("first OR another", :models => :all) 99 99 assert_equal 2, result.first 100 100 end 101 101 102 102 def test_update 103 assert SharedIndex1.find_ by_contents("new").empty?103 assert SharedIndex1.find_with_ferret("new").empty? 104 104 shared_index1s(:first).name = "new name" 105 105 shared_index1s(:first).save 106 assert_equal 1, SharedIndex1.find_ by_contents("new").size107 assert_equal 1, SharedIndex1.find_ by_contents("new").size108 assert_equal 1, SharedIndex1.find_ by_contents("new", :models => [SharedIndex2]).size109 assert_equal 0, SharedIndex2.find_ by_contents("new").size106 assert_equal 1, SharedIndex1.find_with_ferret("new").size 107 assert_equal 1, SharedIndex1.find_with_ferret("new").size 108 assert_equal 1, SharedIndex1.find_with_ferret("new", :models => [SharedIndex2]).size 109 assert_equal 0, SharedIndex2.find_with_ferret("new").size 110 110 end 111 111 end trunk/demo/test/unit/special_content_test.rb
r191 r319 15 15 end 16 16 17 def test_find_ by_contents18 contents_from_ferret = SpecialContent.find_ by_contents('single table')17 def test_find_with_ferret 18 contents_from_ferret = SpecialContent.find_with_ferret('single table') 19 19 assert_equal 1, contents_from_ferret.size 20 20 assert_equal ContentBase.find(3), contents_from_ferret.first 21 contents_from_ferret = SpecialContent.find_ by_contents('title')21 contents_from_ferret = SpecialContent.find_with_ferret('title') 22 22 assert contents_from_ferret.empty? 23 23 trunk/plugin/acts_as_ferret/lib/acts_as_ferret.rb
r318 r319 195 195 end 196 196 197 # count hits for a query acrossmultiple models197 # count hits for a query with multiple models 198 198 def self.total_hits(query, models, options = {}) 199 models = [models] unless Array === models200 199 get_index_for(*models).total_hits query, options.merge( :models => models ) 200 end 201 202 # find ids of records with multiple models 203 def self.find_ids(query, models, options = {}) 204 get_index_for(*models).find_ids query, options.merge( :models => models ) 205 end 206 207 def self.find(query, models, options = {}, ar_options = {}) 208 # TODO generalize local/remote index so we can remove the workaround below 209 # (replace logic in class_methods#find_with_ferret) 210 if Class === models or models.size == 1 211 return (Class === models ? models : models.shift).find_with_ferret query, options, ar_options 212 end 213 index = get_index_for(*models) 214 multi = (MultiIndex === index or index.shared?) 215 if options[:per_page] 216 options[:page] = options[:page] ? options[:page].to_i : 1 217 limit = options[:per_page] 218 offset = (options[:page] - 1) * limit 219 if ar_options[:conditions] && !multi 220 ar_options[:limit] = limit 221 ar_options[:offset] = offset 222 options[:limit] = :all 223 options.delete :offset 224 else 225 # do pagination with ferret (or after everything is done in the case 226 # of multi_search) 227 options[:limit] = limit 228 options[:offset] = offset 229 end 230 elsif ar_options[:conditions] 231 if multi 232 # multisearch ignores find_options limit and offset 233 options[:limit] ||= ar_options.delete(:limit) 234 options[:offset] ||= ar_options.delete(:offset) 235 else 236 # let the db do the limiting and offsetting for single-table searches 237 unless options[:limit] == :all 238 ar_options[:limit] ||= options.delete(:limit) 239 end 240 ar_options[:offset] ||= options.delete(:offset) 241 options[:limit] = :all 242 end 243 end 244 245 total_hits, result = index.find_records query, options.merge(:models => models), ar_options 246 logger.debug "Query: #{query}\ntotal hits: #{total_hits}, results delivered: #{result.size}" 247 SearchResults.new(result, total_hits, options[:page], options[:per_page]) 201 248 end 202 249 … … 258 305 end 259 306 307 # retrieves search result records from a data structure like this: 308 # { 'Model1' => { '1' => [ rank, score ], '2' => [ rank, score ] } 309 # 310 # TODO: in case of STI AR will filter out hits from other 311 # classes for us, but this 312 # will lead to less results retrieved --> scoping of ferret query 313 # to self.class is still needed. 314 # from the ferret ML (thanks Curtis Hatter) 315 # > I created a method in my base STI class so I can scope my query. For scoping 316 # > I used something like the following line: 317 # > 318 # > query << " role:#{self.class.eql?(Contents) '*' : self.class}" 319 # > 320 # > Though you could make it more generic by simply asking 321 # > "self.descends_from_active_record?" which is how rails decides if it should 322 # > scope your "find" query for STI models. You can check out "base.rb" in 323 # > activerecord to see that. 324 # but maybe better do the scoping in find_ids_with_ferret... 325 def self.retrieve_records(id_arrays, find_options = {}) 326 result = [] 327 # get objects for each model 328 id_arrays.each do |model, id_array| 329 next if id_array.empty? 330 model_class = begin 331 model.constantize 332 rescue 333 raise "Please use ':store_class_name => true' if you want to use multi_search.\n#{$!}" 334 end 335 336 # check for per-model conditions and take these if provided 337 if conditions = find_options[:conditions] 338 key = model.underscore.to_sym 339 conditions = conditions[key] if Hash === conditions 340 end 341 342 # merge conditions 343 conditions = combine_conditions([ "#{model_class.table_name}.#{model_class.primary_key} in (?)", 344 id_array.keys ], 345 conditions) 346 347 348 # check for include association that might only exist on some models in case of multi_search 349 filtered_include_options = [] 350 if include_options = find_options[:include] 351 include_options = [ include_options ] unless include_options.respond_to?(:each) 352 include_options.each do |include_option| 353 filtered_include_options << include_option if model_class.reflections.has_key?(include_option.is_a?(Hash) ? include_option.keys[0].to_sym : include_option.to_sym) 354 end 355 end 356 filtered_include_options = nil if filtered_include_options.empty? 357 358 # fetch 359 tmp_result = model_class.find(:all, find_options.merge(:conditions => conditions, 360 :include => filtered_include_options)) 361 362 # set scores and rank 363 tmp_result.each do |record| 364 record.ferret_rank, record.ferret_score = id_array[record.id.to_s] 365 end 366 # merge with result array 367 result.concat tmp_result 368 end 369 370 # order results as they were found by ferret, unless an AR :order 371 # option was given 372 result.sort! { |a, b| a.ferret_rank <=> b.ferret_rank } unless find_options[:order] 373 return result 374 end 375 376 # combine our conditions with those given by user, if any 377 def self.combine_conditions(conditions, additional_conditions = []) 378 returning conditions do 379 if additional_conditions && additional_conditions.any? 380 cust_opts = (Array === additional_conditions) ? additional_conditions.dup : [ additional_conditions ] 381 logger.debug "cust_opts: #{cust_opts.inspect}" 382 conditions.first << " and " << cust_opts.shift 383 conditions.concat(cust_opts) 384 end 385 end 386 end 387 260 388 def self.build_field_config(fields) 261 389 field_config = {} … … 304 432 # other fields 305 433 index_definition[:ferret_fields].each_pair do |field, options| 434 options = options.dup 435 options.delete :via 436 options.delete :boost if options[:boost].is_a?(Symbol) # dynamic boost 306 437 fi.add_field(field, options) 307 438 end … … 326 457 def self.field_config_for(fieldname, options = {}) 327 458 config = DEFAULT_FIELD_OPTIONS.merge options 459 config[:via] ||= fieldname 328 460 config[:term_vector] = :no if config[:index] == :no 329 config.delete :via330 config.delete :boost if config[:boost].is_a?(Symbol) # dynamic boosts aren't handled here331 461 return config 332 462 end trunk/plugin/acts_as_ferret/lib/class_methods.rb
r318 r319 141 141 # to explicitly state the fields you want to fetch, true won't 142 142 # work here) 143 # models:: only for single_index scenarios: an Array of other Model classes to144 # include in this search. Use :all to query all models.145 # multi:: Specify additional model classes to search through. Each of146 # these, as well as this class, has to have the147 # :store_class_name option set to true. This option replaces the148 # multi_search method.149 143 # 150 144 # +find_options+ is a hash passed on to active_record's find when … … 167 161 limit = options[:per_page] 168 162 offset = (options[:page] - 1) * limit 169 if find_options[:conditions] && !options[:multi]163 if find_options[:conditions] 170 164 find_options[:limit] = limit 171 165 find_options[:offset] = offset … … 173 167 options.delete :offset 174 168 else 175 # do pagination with ferret (or after everything is done in the case 176 # of multi_search) 169 # do pagination with ferret 177 170 options[:limit] = limit 178 171 options[:offset] = offset 179 172 end 180 173 elsif find_options[:conditions] 181 if options[:multi] 182 # multisearch ignores find_options limit and offset 183 options[:limit] ||= find_options.delete(:limit) 184 options[:offset] ||= find_options.delete(:offset) 185 else 186 # let the db do the limiting and offsetting for single-table searches 187 unless options[:limit] == :all 188 find_options[:limit] ||= options.delete(:limit) 189 end 190 find_options[:offset] ||= options.delete(:offset) 191 options[:limit] = :all 192 end 193 end 194 195 total_hits, result = if options[:multi].blank? 196 find_records_lazy_or_not q, options, find_options 197 else 198 _multi_search q, options.delete(:multi), options, find_options 199 end 174 find_options[:limit] ||= options.delete(:limit) unless options[:limit] == :all 175 find_options[:offset] ||= options.delete(:offset) 176 options[:limit] = :all 177 end 178 179 total_hits, result = find_records_lazy_or_not q, options, find_options 200 180 logger.debug "Query: #{q}\ntotal hits: #{total_hits}, results delivered: #{result.size}" 201 181 SearchResults.new(result, total_hits, options[:page], options[:per_page]) 202 182 end 203 alias find_by_contents find_with_ferret204 183 205 184 … … 211 190 # 212 191 def total_hits(q, options={}) 213 if options[:models]214 # backwards compatibility215 logger.warn "the :models option of total_hits is deprecated, please use :multi instead"216 options[:multi] = options[:models]217 end218 if models = options[:multi]219 options[:multi] = add_self_to_model_list_if_necessary(models).map(&:to_s)220 end221 192 aaf_index.total_hits(q, options) 222 193 end … … 229 200 # 230 201 # A block can be given too, it will be executed with every result: 231 # find_id _by_contents(q, options) do |model, id, score|202 # find_ids_with_ferret(q, options) do |model, id, score| 232 203 # id_array << id 233 204 # scores_by_id[id] = score … … 236 207 # instead of the [total_hits, results] array! 237 208 # 238 def find_id_by_contents(q, options = {}, &block) 239 deprecated_options_support(options) 240 aaf_index.find_id_by_contents(q, options, &block) 209 def find_ids_with_ferret(q, options = {}, &block) 210 aaf_index.find_ids(q, options, &block) 241 211 end 242 212 … … 248 218 # be yielded, and the total number of hits is returned. 249 219 def id_multi_search(query, additional_models = [], options = {}, &proc) 250 deprecated_options_support(options)220 logger.warn "Model.id_multi_search is deprecated, use ActsAsFerret::find_ids instead!" 251 221 models = add_self_to_model_list_if_necessary(additional_models) 252 aaf_index.id_multi_search(query, models.map(&:to_s), options, &proc)222 ActsAsFerret::find_ids(query, models, options, &proc) 253 223 end 254 224 255 225 256 226 protected 257 258 def _multi_search(query, additional_models = [], options = {}, find_options = {})259 result = []260 261 rank = 0262 if options[:lazy]263 logger.warn "find_options #{find_options} are ignored because :lazy => true" unless find_options.empty?264 total_hits = id_multi_search(query, additional_models, options) do |model, id, score, data|265 result << FerretResult.new(model, id, score, rank += 1, data)266 end267 else268 id_arrays = {}269 270 limit = options.delete(:limit)271
