REFACTOR: search improved so filters are extensible
- added posts_count filter - fixed it so you can search with a filter only
This commit is contained in:
parent
b593a8db92
commit
13f1f90c67
202
lib/search.rb
202
lib/search.rb
|
@ -125,7 +125,9 @@ class Search
|
||||||
|
|
||||||
# Query a term
|
# Query a term
|
||||||
def execute
|
def execute
|
||||||
return nil if @term.blank? || @term.length < (@opts[:min_search_term_length] || SiteSetting.min_search_term_length)
|
if @term.blank? || @term.length < (@opts[:min_search_term_length] || SiteSetting.min_search_term_length)
|
||||||
|
return nil unless @filters.present?
|
||||||
|
end
|
||||||
|
|
||||||
# If the term is a number or url to a topic, just include that topic
|
# If the term is a number or url to a topic, just include that topic
|
||||||
if @opts[:search_for_id] && @results.type_filter == 'topic'
|
if @opts[:search_for_id] && @results.type_filter == 'topic'
|
||||||
|
@ -145,58 +147,105 @@ class Search
|
||||||
@results
|
@results
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.advanced_filter(trigger,&block)
|
||||||
|
(@advanced_filters ||= {})[trigger] = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.advanced_filters
|
||||||
|
@advanced_filters
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/status:open/) do |posts|
|
||||||
|
posts.where('NOT topics.closed AND NOT topics.archived')
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/status:closed/) do |posts|
|
||||||
|
posts.where('topics.closed')
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/status:archived/) do |posts|
|
||||||
|
posts.where('topics.archived')
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/status:noreplies/) do |posts|
|
||||||
|
posts.where("topics.posts_count = 1")
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/status:single_user/) do |posts|
|
||||||
|
posts.where("topics.participant_count = 1")
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/posts_count:(\d+)/) do |posts, match|
|
||||||
|
posts.where("topics.posts_count = ?", match.to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
advanced_filter(/in:(likes|bookmarks)/) do |posts, match|
|
||||||
|
if @guardian.user
|
||||||
|
post_action_type = PostActionType.types[:like] if match == "likes"
|
||||||
|
post_action_type = PostActionType.types[:bookmark] if match == "bookmarks"
|
||||||
|
|
||||||
|
posts.where("posts.id IN (
|
||||||
|
SELECT pa.post_id FROM post_actions pa
|
||||||
|
WHERE pa.user_id = #{@guardian.user.id} AND
|
||||||
|
pa.post_action_type_id = #{post_action_type}
|
||||||
|
)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/in:posted/) do |posts|
|
||||||
|
posts.where("posts.user_id = #{@guardian.user.id}") if @guardian.user
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/in:(watching|tracking)/) do |posts,match|
|
||||||
|
if @guardian.user
|
||||||
|
level = TopicUser.notification_levels[match.to_sym]
|
||||||
|
posts.where("posts.topic_id IN (
|
||||||
|
SELECT tu.topic_id FROM topic_users tu
|
||||||
|
WHERE tu.user_id = #{@guardian.user.id} AND
|
||||||
|
tu.notification_level >= #{level}
|
||||||
|
)")
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/category:(.+)/) do |posts,match|
|
||||||
|
category_id = Category.find_by('name ilike ?', match).try(:id)
|
||||||
|
posts.where("topics.category_id = ?", category_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
advanced_filter(/user:(.+)/) do |posts,match|
|
||||||
|
user_id = User.find_by('username_lower = ?', match.downcase).try(:id)
|
||||||
|
posts.where("posts.user_id = #{user_id}")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
||||||
def process_advanced_search!(term)
|
def process_advanced_search!(term)
|
||||||
|
|
||||||
term.to_s.split(/\s+/).map do |word|
|
term.to_s.split(/\s+/).map do |word|
|
||||||
if word == 'status:open'
|
|
||||||
@status = :open
|
found = false
|
||||||
nil
|
|
||||||
elsif word == 'status:closed'
|
Search.advanced_filters.each do |matcher, block|
|
||||||
@status = :closed
|
if word =~ matcher
|
||||||
nil
|
(@filters ||= []) << [block, $1]
|
||||||
elsif word == 'status:archived'
|
found = true
|
||||||
@status = :archived
|
end
|
||||||
nil
|
end
|
||||||
elsif word == 'status:noreplies'
|
|
||||||
@posts_count = 1
|
if word == 'order:latest'
|
||||||
nil
|
|
||||||
elsif word == 'status:singleuser'
|
|
||||||
@single_user = true
|
|
||||||
nil
|
|
||||||
elsif word == 'order:latest'
|
|
||||||
@order = :latest
|
@order = :latest
|
||||||
nil
|
nil
|
||||||
elsif word == 'order:views'
|
elsif word == 'order:views'
|
||||||
@order = :views
|
@order = :views
|
||||||
nil
|
nil
|
||||||
elsif word =~ /category:(.+)/
|
|
||||||
@category_id = Category.find_by('name ilike ?', $1).try(:id)
|
|
||||||
nil
|
|
||||||
elsif word =~ /user:(.+)/
|
|
||||||
@user_id = User.find_by('username_lower = ?', $1.downcase).try(:id)
|
|
||||||
nil
|
|
||||||
elsif word == 'in:likes'
|
|
||||||
@liked_only = true
|
|
||||||
nil
|
|
||||||
elsif word == 'in:posted'
|
|
||||||
@posted_only = true
|
|
||||||
nil
|
|
||||||
elsif word == 'in:watching'
|
|
||||||
@notification_level = TopicUser.notification_levels[:watching]
|
|
||||||
nil
|
|
||||||
elsif word == 'in:tracking'
|
|
||||||
@notification_level = TopicUser.notification_levels[:tracking]
|
|
||||||
nil
|
|
||||||
elsif word == 'in:private'
|
elsif word == 'in:private'
|
||||||
@search_pms = true
|
@search_pms = true
|
||||||
nil
|
nil
|
||||||
elsif word == 'in:bookmarks'
|
|
||||||
@bookmarked_only = true
|
|
||||||
nil
|
|
||||||
else
|
else
|
||||||
word
|
found ? nil : word
|
||||||
end
|
end
|
||||||
end.compact.join(' ')
|
end.compact.join(' ')
|
||||||
end
|
end
|
||||||
|
@ -210,8 +259,8 @@ class Search
|
||||||
else
|
else
|
||||||
@limit = Search.per_facet + 1
|
@limit = Search.per_facet + 1
|
||||||
unless @search_context
|
unless @search_context
|
||||||
user_search
|
user_search if @term.present?
|
||||||
category_search
|
category_search if @term.present?
|
||||||
end
|
end
|
||||||
topic_search
|
topic_search
|
||||||
end
|
end
|
||||||
|
@ -306,60 +355,23 @@ class Search
|
||||||
posts = posts.where("topics.archetype <> ?", Archetype.private_message)
|
posts = posts.where("topics.archetype <> ?", Archetype.private_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_topic_search
|
if @term.present?
|
||||||
posts = posts.joins('JOIN users u ON u.id = posts.user_id')
|
if is_topic_search
|
||||||
posts = posts.where("posts.raw || ' ' || u.username || ' ' || u.name ilike ?", "%#{@term}%")
|
posts = posts.joins('JOIN users u ON u.id = posts.user_id')
|
||||||
else
|
posts = posts.where("posts.raw || ' ' || u.username || ' ' || u.name ilike ?", "%#{@term}%")
|
||||||
posts = posts.where("post_search_data.search_data @@ #{ts_query}")
|
else
|
||||||
end
|
posts = posts.where("post_search_data.search_data @@ #{ts_query}")
|
||||||
|
|
||||||
if @status == :open
|
|
||||||
posts = posts.where('NOT topics.closed AND NOT topics.archived')
|
|
||||||
elsif @status == :archived
|
|
||||||
posts = posts.where('topics.archived')
|
|
||||||
elsif @status == :closed
|
|
||||||
posts = posts.where('topics.closed')
|
|
||||||
end
|
|
||||||
|
|
||||||
if @single_user
|
|
||||||
posts = posts.where("topics.featured_user1_id IS NULL AND topics.last_post_user_id = topics.user_id")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @posts_count
|
|
||||||
posts = posts.where("topics.posts_count = #{@posts_count}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @user_id
|
|
||||||
posts = posts.where("posts.user_id = #{@user_id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @guardian.user
|
|
||||||
if @liked_only || @bookmarked_only
|
|
||||||
|
|
||||||
post_action_type = PostActionType.types[:like] if @liked_only
|
|
||||||
post_action_type = PostActionType.types[:bookmark] if @bookmarked_only
|
|
||||||
|
|
||||||
posts = posts.where("posts.id IN (
|
|
||||||
SELECT pa.post_id FROM post_actions pa
|
|
||||||
WHERE pa.user_id = #{@guardian.user.id} AND
|
|
||||||
pa.post_action_type_id = #{post_action_type}
|
|
||||||
)")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @posted_only
|
|
||||||
posts = posts.where("posts.user_id = #{@guardian.user.id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @notification_level
|
|
||||||
posts = posts.where("posts.topic_id IN (
|
|
||||||
SELECT tu.topic_id FROM topic_users tu
|
|
||||||
WHERE tu.user_id = #{@guardian.user.id} AND
|
|
||||||
tu.notification_level >= #{@notification_level}
|
|
||||||
)")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@filters.each do |block, match|
|
||||||
|
if block.arity == 1
|
||||||
|
posts = instance_exec(posts, &block) || posts
|
||||||
|
else
|
||||||
|
posts = instance_exec(posts, match, &block) || posts
|
||||||
|
end
|
||||||
|
end if @filters
|
||||||
|
|
||||||
# If we have a search context, prioritize those posts first
|
# If we have a search context, prioritize those posts first
|
||||||
if @search_context.present?
|
if @search_context.present?
|
||||||
|
|
||||||
|
@ -380,11 +392,7 @@ class Search
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @category_id
|
if @order == :latest || @term.blank?
|
||||||
posts = posts.where("topics.category_id = ?", @category_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @order == :latest
|
|
||||||
if opts[:aggregate_search]
|
if opts[:aggregate_search]
|
||||||
posts = posts.order("MAX(posts.created_at) DESC")
|
posts = posts.order("MAX(posts.created_at) DESC")
|
||||||
else
|
else
|
||||||
|
|
|
@ -378,11 +378,13 @@ describe Search do
|
||||||
|
|
||||||
expect(Search.execute('test status:closed').posts.length).to eq(0)
|
expect(Search.execute('test status:closed').posts.length).to eq(0)
|
||||||
expect(Search.execute('test status:open').posts.length).to eq(1)
|
expect(Search.execute('test status:open').posts.length).to eq(1)
|
||||||
|
expect(Search.execute('test posts_count:1').posts.length).to eq(1)
|
||||||
|
|
||||||
topic.closed = true
|
topic.closed = true
|
||||||
topic.save
|
topic.save
|
||||||
|
|
||||||
expect(Search.execute('test status:closed').posts.length).to eq(1)
|
expect(Search.execute('test status:closed').posts.length).to eq(1)
|
||||||
|
expect(Search.execute('status:closed').posts.length).to eq(1)
|
||||||
expect(Search.execute('test status:open').posts.length).to eq(0)
|
expect(Search.execute('test status:open').posts.length).to eq(0)
|
||||||
|
|
||||||
topic.archived = true
|
topic.archived = true
|
||||||
|
|
Loading…
Reference in New Issue