# encoding: utf-8 require "#{File.dirname(File.expand_path(__FILE__))}/../test_helper" class ActsAsSolrTest < Test::Unit::TestCase fixtures :books, :movies, :electronics, :postings, :authors, :advertises Document.destroy_all DynamicAttribute.delete_all Advertise.first.dynamic_attributes.create! :name => 'Description', :value => 'A very cool bike' Advertise.first.dynamic_attributes.create! :name => 'Price', :value => '1000' Local.delete_all Local.create! :localizable => Advertise.find(1), :longitude => '-77.4027', :latitude => '39.36' Local.create! :localizable => Advertise.find(2), :longitude => '77.4027', :latitude => '-38.36' # Inserting new data into Solr and making sure it's getting indexed def test_insert_new_data assert_equal 2, Book.count_by_solr('ruby OR splinter OR bob') b = Book.create(:name => "Fuze in action", :author => "Bob Bobber", :category_id => 1) assert b.valid? assert_equal 3, Book.count_by_solr('ruby OR splinter OR bob') end # Check the type column stored in the index isn't stemmed by SOLR. If it is stemmed, # then both Post and Posting will be stored as type:Post, so a query for Posts will # return Postings and vice versa def test_insert_new_data_doesnt_stem_type assert_equal 0, Post.count_by_solr('aardvark') p = Posting.new :name => 'aardvark', :description => "An interesting animal" p.guid = '12AB' p.save! assert_equal 0, Post.count_by_solr('aardvark') end def test_type_determined_from_database_if_not_explicitly_set assert Post.configuration[:solr_fields][:posted_at][:type] == :date end def test_search_includes_subclasses Novel.create! :name => 'Wuthering Heights', :author => 'Emily Bronte' Book.create! :name => 'Jane Eyre', :author => 'Charlotte Bronte' assert_equal 1, Novel.find_by_solr('Bronte').total_hits assert_equal 2, Book.find_by_solr('Bronte').total_hits end # Testing basic solr search: # Model.find_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_by_solr_ruby ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter', 'author:peter AND ruby', 'peter dummy'].each do |term| records = Book.find_by_solr term assert_equal 1, records.total assert_equal "Peter McPeterson", records.docs.first.author assert_equal "Ruby for Dummies", records.docs.first.name assert_equal ({"id" => 2, "category_id" => 2, "name" => "Ruby for Dummies", "author" => "Peter McPeterson", "published_on" => (Date.today - 2.years), "type" => nil}), records.docs.first.attributes end end # Testing basic solr search: # Model.find_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_by_solr_splinter ['splinter', 'name:splinter', 'name:splinter AND author:clancy', 'author:clancy AND splinter', 'cell tom'].each do |term| records = Book.find_by_solr term assert_equal 1, records.total assert_equal "Splinter Cell", records.docs.first.name assert_equal "Tom Clancy", records.docs.first.author assert_equal ({"id" => 1, "category_id" => 1, "name" => "Splinter Cell", "author" => "Tom Clancy", "published_on" => (Date.today - 1.year), "type" => nil}), records.docs.first.attributes end end # Testing basic solr search: # Model.find_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_by_solr_ruby_or_splinter ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter', 'dummy OR cell'].each do |term| records = Book.find_by_solr term assert_equal 2, records.total end end # Testing search in indexed field methods: # # class Movie < ActiveRecord::Base # acts_as_solr :fields => [:name, :description, :current_time] # # def current_time # Time.now.to_s # end # # end # # The method current_time above gets indexed as being part of the # Movie model and it's available for search as well def test_find_with_dynamic_fields date = Time.now.strftime('%b %d %Y') ["dynamite AND #{date}", "description:goofy AND #{date}", "goofy napoleon #{date}", "goofiness #{date}"].each do |term| records = Movie.find_by_solr term assert_equal 1, records.total assert_equal ({"id" => 1, "name" => "Napoleon Dynamite", "description" => "Cool movie about a goofy guy"}), records.docs.first.attributes end end # Testing basic solr search that returns just the ids instead of the objects: # Model.find_id_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_id_by_solr_ruby ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter', 'author:peter AND ruby'].each do |term| records = Book.find_id_by_solr term assert_equal 1, records.docs.size assert_equal ['2'], records.docs end end # Testing basic solr search that returns just the ids instead of the objects: # Model.find_id_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_id_by_solr_splinter ['splinter', 'name:splinter', 'name:splinter AND author:clancy', 'author:clancy AND splinter'].each do |term| records = Book.find_id_by_solr term assert_equal 1, records.docs.size assert_equal ['1'], records.docs end end # Testing basic solr search that returns just the ids instead of the objects: # Model.find_id_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_find_id_by_solr_ruby_or_splinter ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter', 'dummy OR cell'].each do |term| records = Book.find_id_by_solr term assert_equal 2, records.docs.size assert_equal ['1','2'], records.docs end end # Testing basic solr search that returns the total number of records found: # Model.find_count_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_count_by_solr ['ruby', 'dummy', 'name:ruby', 'name:dummy', 'name:ruby AND author:peter', 'author:peter AND ruby'].each do |term| assert_equal 1, Book.count_by_solr(term), "there should only be 1 result for search: #{term}" end end # Testing basic solr search that returns the total number of records found: # Model.find_count_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_count_by_solr_splinter ['splinter', 'name:splinter', 'name:splinter AND author:clancy', 'author:clancy AND splinter', 'author:clancy cell'].each do |term| assert_equal 1, Book.count_by_solr(term) end end # Testing basic solr search that returns the total number of records found: # Model.find_count_by_solr 'term' # Note that you're able to mix free-search with fields and boolean operators def test_count_by_solr_ruby_or_splinter ['ruby OR splinter', 'ruby OR author:tom', 'name:cell OR author:peter', 'dummy OR cell'].each do |term| assert_equal 2, Book.count_by_solr(term) end end # Testing basic solr search with additional options: # Model.find_count_by_solr 'term', :limit => 10, :offset => 0 def test_find_with_options [1,2].each do |count| records = Book.find_by_solr 'ruby OR splinter', :limit => count assert_equal count, records.docs.size end end # Testing self.rebuild_solr_index # - It makes sure the index is rebuilt after a data has been lost def test_rebuild_solr_index assert_equal 1, Book.count_by_solr('splinter') Book.find(:first).solr_destroy assert_equal 0, Book.count_by_solr('splinter') Book.rebuild_solr_index assert_equal 1, Book.count_by_solr('splinter') end # Testing instance methods: # - solr_save # - solr_destroy def test_solr_save_and_solr_destroy assert_equal 1, Book.count_by_solr('splinter') Book.find(:first).solr_destroy assert_equal 0, Book.count_by_solr('splinter') Book.find(:first).solr_save assert_equal 1, Book.count_by_solr('splinter') end # Testing the order of results def test_find_returns_records_in_order records = Book.find_by_solr 'ruby^5 OR splinter' # we boosted ruby so ruby should come first assert_equal 2, records.total assert_equal 'Ruby for Dummies', records.docs.first.name assert_equal 'Splinter Cell', records.docs.last.name end # Testing solr search with optional :order argument def _test_with_order_option records = Movie.find_by_solr 'office^5 OR goofiness' assert_equal 'Hypnotized dude loves fishing but not working', records.docs.first.description assert_equal 'Cool movie about a goofy guy', records.docs.last.description records = Movie.find_by_solr 'office^5 OR goofiness', :order => 'description asc' assert_equal 'Cool movie about a goofy guy', records.docs.first.description assert_equal 'Hypnotized dude loves fishing but not working', records.docs.last.description end # Testing search with omitted :field_types should # return the same result set as if when we use it def test_omit_field_types_in_search records = Electronic.find_by_solr "price:[200 TO 599.99]" assert_match(/599/, records.docs.first.price) assert_match(/319/, records.docs.last.price) records = Electronic.find_by_solr "price:[200 TO 599.99]", :order => 'price asc' assert_match(/319/, records.docs.first.price) assert_match(/599/, records.docs.last.price) end # Test to make sure the result returned when no matches # are found has the same structure when there are results def test_returns_no_matches records = Book.find_by_solr 'rubyist' assert_equal [], records.docs assert_equal 0, records.total records = Book.find_id_by_solr 'rubyist' assert_equal [], records.docs assert_equal 0, records.total records = Book.find_by_solr 'rubyist', :facets => {} assert_equal [], records.docs assert_equal 0, records.total assert_equal({"facet_fields"=>[]}, records.facets) end # Testing the :exclude_fields option when set in the # model to make sure it doesn't get indexed def test_exclude_fields_option records = Electronic.find_by_solr 'audiobooks OR latency' assert records.docs.empty? assert_equal 0, records.total assert_nothing_raised{ records = Electronic.find_by_solr 'features:audiobooks' assert records.docs.empty? assert_equal 0, records.total } end # Testing the :auto_commit option set to false in the model # should not send the commit command to Solr def test_auto_commit_turned_off assert_equal 0, Author.count_by_solr('raymond chandler') original_count = Author.count Author.create(:name => 'Raymond Chandler', :biography => 'Writes noirish detective stories') assert_equal original_count + 1, Author.count assert_equal 0, Author.count_by_solr('raymond chandler') end # Testing models that use a different key as the primary key def test_search_on_model_with_string_id_field records = Posting.find_by_solr 'first^5 OR second' assert_equal 2, records.total assert_equal 'ABC-123', records.docs.first.guid assert_equal 'DEF-456', records.docs.last.guid end # Making sure the result set is ordered correctly even on # models that use a different key as the primary key def test_records_in_order_on_model_with_string_id_field records = Posting.find_by_solr 'first OR second^5' assert_equal 2, records.total assert_equal 'DEF-456', records.docs.first.guid assert_equal 'ABC-123', records.docs.last.guid end # Making sure the records are added when passing a batch size # to rebuild_solr_index def test_using_rebuild_solr_index_with_batch assert_equal 2, Movie.count_by_solr('office OR napoleon') Movie.find(:all).each(&:solr_destroy) assert_equal 0, Movie.count_by_solr('office OR napoleon') Movie.rebuild_solr_index 100 assert_equal 2, Movie.count_by_solr('office OR napoleon') end # Making sure find_by_solr with scores actually return the scores # for each individual record def test_find_by_solr_with_score books = Book.find_by_solr 'ruby^10 OR splinter', :scores => true assert_equal 2, books.total assert (books.max_score >= 0.3 && books.max_score <= 0.6) books.records.each { |book| assert_not_nil book.solr_score } assert (books.docs.first.solr_score >= 0.3 && books.docs.first.solr_score <= 0.6) assert (books.docs.last.solr_score < books.docs.first.solr_score) end # Making sure nothing breaks when html entities are inside # the content to be indexed; and on the search as well. def test_index_and_search_with_html_entities description = " inverted exclamation mark ¡ ¡ ¤ currency ¤ ¤ ¢ cent ¢ ¢ £ pound £ £ ¥ yen ¥ ¥ ¦ broken vertical bar ¦ ¦ § section § § ¨ spacing diaeresis ¨ ¨ © copyright © © ª feminine ordinal indicator ª ª « angle quotation mark (left) « « ¬ negation ¬ ¬ ­ soft hyphen ­ ­ ® registered trademark ® ® ™ trademark ™ ™ ¯ spacing macron ¯ ¯ ° degree ° ° ± plus-or-minus ± ± ² superscript 2 ² ² ³ superscript 3 ³ ³ ´ spacing acute ´ ´ µ micro µ µ ¶ paragraph ¶ ¶ · middle dot · · ¸ spacing cedilla ¸ ¸ ¹ superscript 1 ¹ ¹ º masculine ordinal indicator º º » angle quotation mark (right) » » ¼ fraction 1/4 ¼ ¼ ½ fraction 1/2 ½ ½ ¾ fraction 3/4 ¾ ¾ ¿ inverted question mark ¿ ¿ × multiplication × × ÷ division ÷ ÷ ♥ ♦ ♣ ♠" author = Author.create(:name => "Test in Action™ - Copyright © Bob", :biography => description) assert author.valid? author.solr_commit author = Author.find_by_solr 'trademark © ¾ ¡ £' assert_equal 1, author.total end def test_operator_search_option assert_nothing_raised { books = Movie.find_by_solr "office napoleon", :operator => :or assert_equal 2, books.total books = Movie.find_by_solr "office napoleon", :operator => :and assert_equal 0, books.total } assert_raise RuntimeError do Movie.find_by_solr "office napoleon", :operator => :bad end end # Making sure find_by_solr with scores actually return the scores # for each individual record and orders them accordingly def test_find_by_solr_order_by_score books = Book.find_by_solr 'ruby^10 OR splinter', {:scores => true, :order => 'score asc' } assert (books.docs.collect(&:solr_score).compact.size == books.docs.size), "Each book should have a score" assert (books.docs.last.solr_score >= 0.3 && books.docs.last.solr_score <= 0.6) books = Book.find_by_solr 'ruby^10 OR splinter', {:scores => true, :order => 'score desc' } assert (books.docs.first.solr_score >= 0.3 && books.docs.first.solr_score <= 0.6) assert (books.docs.last.solr_score < books.docs.first.solr_score) end # Search based on fields with the :date format def test_indexed_date_field_format movies = Movie.find_by_solr 'time_on_xml:[NOW-1DAY TO NOW]' assert_equal 2, movies.total end def test_query_time_is_returned results = Book.find_by_solr('ruby') assert_not_nil(results.query_time) assert_equal(results.query_time.class,Fixnum) end def test_should_not_index_the_record_when_offline_proc_returns_true Gadget.search_disabled = true gadget = Gadget.create(:name => "flipvideo mino") assert_equal 0, Gadget.find_id_by_solr('flipvideo').total end def test_should_find_filtering_a_dynamic_attribute records = Advertise.find_by_solr "description:bike" assert_equal 1, records.total end def test_dynamic_attributes_are_faceted records = Advertise.find_by_solr "Description:bike", :facets => { :fields => [:Description] } expected = { "A very cool bike" => 1 } assert_equal expected, records.facets['facet_fields']['Description_facet'] end def test_search_is_an_alias_for_find_by_solr assert_equal Advertise.find_by_solr("bike").docs, Advertise.search("bike").docs end def test_search_given_a_radius records = Advertise.search "bike", :around => {:latitude => '-39.36', :longitude => '77.4027', :radius => 1} assert_equal 0, records.total end def test_records_are_found_in_a_radius records = Advertise.search "bike", :around => {:latitude => '39.36', :longitude => '-77.4027', :radius => 1} assert_equal 1, records.total end def test_records_are_found_with_highlight records = Book.find_by_solr "ruby", :highlight => { :fields => "name" } assert_equal 1, records.total end def test_records_wiht_highlights_are_returned_properly records = Book.find_by_solr "ruby", :highlight => { :fields => "name" } expected = {"name"=>["Ruby for Dummies"]} assert_equal expected, records.highlights.values.first end def test_spellcheck assert_equal "ruby for dummies", Book.search("rubi for dumies").suggest end def test_mongo_mappers_documents_are_found Document.new(:name => "mapper").save assert_equal "mapper", Document.search("mapper").docs.first.name end def test_total_pages_is_returned_when_limit_specified assert_equal 2, Posting.search("test", :limit => 1).total_pages end def test_total_pages_is_returned_when_limit_not_specified assert_equal 1, Posting.search("test").total_pages end def test_current_page_is_returned assert_equal 2, Posting.search("test", :limit => 1, :offset => 1).current_page end def test_current_page_1_is_returned assert_equal 1, Posting.search("test").current_page end def test_current_page_1_is_returned_when_no_records_found assert_equal 1, Posting.search("nothing").current_page end def test_page_parameter_is_accepted assert_equal 2, Posting.search("test", :limit => 1, :page => 2).current_page end def test_per_page_parameter_is_accepted assert_equal 1, Posting.search("test", :per_page => 1).per_page end end