diff --git a/Gemfile b/Gemfile index 68acce42..7aea5b1f 100644 --- a/Gemfile +++ b/Gemfile @@ -47,4 +47,5 @@ group :test, :development do gem "sqlite3" gem "faker" gem "byebug" + gem "rb-readline" end diff --git a/Gemfile.lock b/Gemfile.lock index d5bb75f1..542f7daf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -153,6 +153,7 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + rb-readline (0.5.5) rotp (3.3.1) rqrcode (0.10.1) chunky_png (~> 1.0) @@ -241,6 +242,7 @@ DEPENDENCIES nokogiri (>= 1.7.2) oauth rails (~> 5.2.0) + rb-readline rotp rqrcode rspec-rails diff --git a/app/models/search.rb b/app/models/search.rb index 0a0c8ea9..2512a212 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -61,6 +61,23 @@ class Search .group("stories.id") end + def with_stories_in_domain(base, domain) + begin + reg = Regexp.new("//([^/]*\.)?#{domain}/") + base.where("`stories`.`url` REGEXP '" + + ActiveRecord::Base.connection.quote_string(reg.source) + "'") + rescue RegexpError + return base + end + end + + def with_stories_matching_tags(base, tag_scopes) + story_ids_matching_tags = with_tags( + Story.unmerged.where(:is_expired => false), tag_scopes + ).select(:id).map(&:id) + base.where(story_id: story_ids_matching_tags) + end + def search_for_user!(user) self.results = [] self.total_results = 0 @@ -84,13 +101,7 @@ class Search when "stories" base = Story.unmerged.where(:is_expired => false) if domain.present? - begin - reg = Regexp.new("//([^/]*\.)?#{domain}/") - base = base.where("`url` REGEXP '" + - ActiveRecord::Base.connection.quote_string(reg.source) + "'") - rescue RegexpError - return false - end + base = with_stories_in_domain(base, domain) end title_match_sql = Arel.sql("MATCH(stories.title) AGAINST('#{qwords}' IN BOOLEAN MODE)") @@ -138,17 +149,20 @@ class Search end when "comments" - base = Comment.active.where(Arel.sql("MATCH(comment) AGAINST('#{qwords}' IN BOOLEAN MODE)")) - .includes(:user, :story) - + base = Comment.active + if domain.present? + base = with_stories_in_domain(base.joins(:story), domain) + end + if tag_scopes.present? + base = with_stories_matching_tags(base, tag_scopes) + end + if qwords.present? + base = base.where(Arel.sql("MATCH(comment) AGAINST('#{qwords}' IN BOOLEAN MODE)")) + end self.results = base.select( "comments.*, " + "MATCH(comment) AGAINST('#{qwords}' IN BOOLEAN MODE) AS rel_comment" - ) - - if tag_scopes.present? - self.results = with_tags(base, tag_scopes) - end + ).includes(:user, :story) case self.order when "relevance" @@ -158,12 +172,8 @@ class Search when "points" self.results.order!("#{Comment.score_sql} DESC") end - end - if tag_scopes.present? self.total_results = self.results.length - else - self.total_results = base.count end if self.page > self.page_count diff --git a/spec/models/search_spec.rb b/spec/models/search_spec.rb index 2b4c9aab..1a41320e 100644 --- a/spec/models/search_spec.rb +++ b/spec/models/search_spec.rb @@ -31,11 +31,29 @@ describe Search do :user_id => @user.id, :tags_a => ["tag1"]), ] + @comments = [ + create(:comment, :comment => "comment0", + :story_id => @multi_tag.id, + :user_id => @user.id), + create(:comment, :comment => "comment1", + :story_id => @stories[0].id, + :user_id => @user.id), + create(:comment, :comment => "comment2", + :story_id => @stories[1].id, + :user_id => @user.id), + create(:comment, :comment => "comment3", + :story_id => @stories[2].id, + :user_id => @user.id), + create(:comment, :comment => "comment4", + :story_id => @stories[4].id, + :user_id => @user.id), + ] end after(:all) do - @user.destroy! + @comments.each(&:destroy!) @stories.each(&:destroy!) + @user.destroy! if @user end it "can search for stories" do @@ -103,4 +121,54 @@ describe Search do expect(search.results.length).to eq(2) end + + it "can search for comments" do + search = Search.new + search.q = "comment1" + search.what = "comments" + + search.search_for_user!(@user) + + expect(search.results).to include(@comments[1]) + end + it "can search for comments by tag" do + search = Search.new + search.q = "comment2 comment3 tag:tag1" + search.what = "comments" + + search.search_for_user!(@user) + + expect(search.results).to include(@comments[2]) + expect(search.results).not_to include(@comments[3]) + end + it "can search for comments with only tags" do + search = Search.new + search.q = "tag:tag1" + search.what = "comments" + + search.search_for_user!(@user) + + expect(search.results).to include(@comments[2]) + expect(search.results).not_to include(@comments[3]) + end + it "should only return comments matching all tags if multiple are present" do + search = Search.new + search.q = "tag:tag1 tag:tag2" + search.what = "comments" + + search.search_for_user!(@user) + + expect(search.results).to eq([@comments[0]]) + end + + it "should only return comments with stories in domain if domain present" do + search = Search.new + search.q = "comment3 comment4 domain:lobste.rs" + search.what = "comments" + + search.search_for_user!(@user) + + expect(search.results).to include(@comments[4]) + expect(search.results).not_to include(@comments[3]) + end end