Can search by URL or ID
This commit is contained in:
parent
957d95c1cc
commit
fff46cf5aa
107
lib/search.rb
107
lib/search.rb
|
@ -118,48 +118,72 @@ SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# needs current user for secure categories
|
# If we're searching for a single topic
|
||||||
def self.query(term, guardian, type_filter=nil, min_search_term_length=3)
|
def self.single_topic(id, guardian)
|
||||||
|
topic = Topic.where(id: id).first
|
||||||
|
return nil unless guardian.can_see?(topic)
|
||||||
|
|
||||||
guardian ||= Guardian.new(nil)
|
results = [{'type' => 'topic',
|
||||||
|
'id' => topic.id,
|
||||||
|
'url' => topic.relative_url,
|
||||||
|
'title' => topic.title }]
|
||||||
|
group_db_result(results, 'topic')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Query a term
|
||||||
|
def self.query(term, guardian, type_filter=nil, min_search_term_length=3)
|
||||||
|
|
||||||
return nil if term.blank?
|
return nil if term.blank?
|
||||||
|
|
||||||
|
# If no guardian supplied, assume anonymous
|
||||||
|
guardian ||= Guardian.new(nil)
|
||||||
|
|
||||||
|
term = term.to_s
|
||||||
|
|
||||||
|
# If the term is a number or url to a topic, just include that topic
|
||||||
|
if type_filter == 'topic'
|
||||||
|
|
||||||
|
begin
|
||||||
|
route = Rails.application.routes.recognize_path(term)
|
||||||
|
return single_topic(route[:topic_id], guardian) if route[:topic_id].present?
|
||||||
|
rescue ActionController::RoutingError
|
||||||
|
end
|
||||||
|
|
||||||
|
return single_topic(term.to_i, guardian) if term =~ /^\d+$/
|
||||||
|
end
|
||||||
|
|
||||||
# We are stripping only symbols taking place in FTS and simply sanitizing the rest.
|
# We are stripping only symbols taking place in FTS and simply sanitizing the rest.
|
||||||
sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,''))
|
sanitized_term = PG::Connection.escape_string(term.gsub(/[:()&!]/,''))
|
||||||
|
query_string(sanitized_term, guardian, type_filter, min_search_term_length)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Search for a string term
|
||||||
|
def self.query_string(term, guardian, type_filter, min_search_term_length)
|
||||||
|
|
||||||
# really short terms are totally pointless
|
# really short terms are totally pointless
|
||||||
return nil if sanitized_term.blank? || sanitized_term.length < min_search_term_length
|
return nil if term.length < min_search_term_length
|
||||||
|
|
||||||
terms = sanitized_term.split
|
args = {orig: term,
|
||||||
terms.map! {|t| "#{t}:*"}
|
query: term.split.map {|t| "#{t}:*"}.join(" & "),
|
||||||
|
locale: current_locale_long}
|
||||||
args = {orig: sanitized_term, query: terms.join(" & "), locale: current_locale_long}
|
|
||||||
|
|
||||||
if type_filter.present?
|
if type_filter.present?
|
||||||
raise Discourse::InvalidAccess.new("invalid type filter") unless Search.facets.include?(type_filter)
|
raise Discourse::InvalidAccess.new("invalid type filter") unless Search.facets.include?(type_filter)
|
||||||
|
args.merge!(limit: Search.per_facet * Search.facets.size)
|
||||||
a = args.merge(limit: Search.per_facet * Search.facets.size)
|
db_result = case type_filter.to_s
|
||||||
|
when 'topic'
|
||||||
if type_filter.to_s == "post"
|
post_query(guardian, type_filter.to_sym, args)
|
||||||
db_result = post_query(guardian, :post, a)
|
when 'category'
|
||||||
elsif type_filter.to_s == "topic"
|
category_query(guardian, args)
|
||||||
db_result = post_query(guardian, :topic, a)
|
else
|
||||||
elsif type_filter.to_s == "category"
|
ActiveRecord::Base.exec_sql(Search.send("#{type_filter}_query_sql"), args)
|
||||||
db_result = category_query(guardian, a)
|
end
|
||||||
else
|
|
||||||
sql = Search.send("#{type_filter}_query_sql")
|
|
||||||
db_result = ActiveRecord::Base.exec_sql(sql , a)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
args.merge!(limit: (Search.per_facet + 1))
|
||||||
db_result = []
|
db_result = []
|
||||||
|
db_result += ActiveRecord::Base.exec_sql(user_query_sql, args).to_a
|
||||||
a = args.merge(limit: (Search.per_facet + 1))
|
db_result += category_query(guardian, args).to_a
|
||||||
db_result += ActiveRecord::Base.exec_sql(user_query_sql, a).to_a
|
db_result += post_query(guardian, :topic, args).to_a
|
||||||
db_result += category_query(guardian, a).to_a
|
|
||||||
db_result += post_query(guardian, :topic, a).to_a
|
|
||||||
end
|
end
|
||||||
|
|
||||||
db_result = db_result.to_a
|
db_result = db_result.to_a
|
||||||
|
@ -175,24 +199,29 @@ SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
if expected_topics > 0
|
if expected_topics > 0
|
||||||
tmp = post_query(guardian, :post, args.merge(limit: expected_topics * 3))
|
tmp = post_query(guardian, :post, args.merge(limit: expected_topics * 3)).to_a
|
||||||
|
|
||||||
topic_ids = Set.new db_result.map{|r| r["id"]}
|
topic_ids = Set.new db_result.map{|r| r["id"]}
|
||||||
|
|
||||||
tmp = tmp.to_a
|
tmp.reject! do |i|
|
||||||
tmp = tmp.reject{ |i|
|
if topic_ids.include?(i["id"])
|
||||||
if topic_ids.include? i["id"]
|
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
topic_ids << i["id"]
|
topic_ids << i["id"]
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
|
|
||||||
db_result += tmp[0..expected_topics-1]
|
db_result += tmp[0..expected_topics-1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Group the results by type
|
group_db_result(db_result, type_filter)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Group the results by type
|
||||||
|
def self.group_db_result(db_result, type_filter)
|
||||||
grouped = {}
|
grouped = {}
|
||||||
db_result.each do |row|
|
db_result.each do |row|
|
||||||
type = row.delete('type')
|
type = row.delete('type')
|
||||||
|
@ -204,10 +233,10 @@ SQL
|
||||||
row['url'].gsub!('slug', new_slug)
|
row['url'].gsub!('slug', new_slug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add avatars for users
|
||||||
|
row['avatar_template'] = User.avatar_template(row['email']) if type == 'user'
|
||||||
|
|
||||||
# Remove attributes when we know they don't matter
|
# Remove attributes when we know they don't matter
|
||||||
if type == 'user'
|
|
||||||
row['avatar_template'] = User.avatar_template(row['email'])
|
|
||||||
end
|
|
||||||
row.delete('email')
|
row.delete('email')
|
||||||
row.delete('color') unless type == 'category'
|
row.delete('color') unless type == 'category'
|
||||||
row.delete('text_color') unless type == 'category'
|
row.delete('text_color') unless type == 'category'
|
||||||
|
@ -216,7 +245,7 @@ SQL
|
||||||
grouped[type] << row
|
grouped[type] << row
|
||||||
end
|
end
|
||||||
|
|
||||||
result = grouped.map do |type, results|
|
grouped.map do |type, results|
|
||||||
more = type_filter.blank? && (results.size > Search.per_facet)
|
more = type_filter.blank? && (results.size > Search.per_facet)
|
||||||
results = results[0..([results.length, Search.per_facet].min - 1)] if type_filter.blank?
|
results = results[0..([results.length, Search.per_facet].min - 1)] if type_filter.blank?
|
||||||
{
|
{
|
||||||
|
@ -226,8 +255,6 @@ SQL
|
||||||
results: results
|
results: results
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,7 +110,6 @@ describe Search do
|
||||||
let(:topic) { Fabricate(:topic) }
|
let(:topic) { Fabricate(:topic) }
|
||||||
|
|
||||||
context 'searching the OP' do
|
context 'searching the OP' do
|
||||||
|
|
||||||
let!(:post) { Fabricate(:post, topic: topic, user: topic.user) }
|
let!(:post) { Fabricate(:post, topic: topic, user: topic.user) }
|
||||||
let(:result) { first_of_type(Search.query('hello', nil), 'topic') }
|
let(:result) { first_of_type(Search.query('hello', nil), 'topic') }
|
||||||
|
|
||||||
|
@ -119,7 +118,26 @@ describe Search do
|
||||||
result['title'].should == topic.title
|
result['title'].should == topic.title
|
||||||
result['url'].should == topic.relative_url
|
result['url'].should == topic.relative_url
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "search for a topic by id" do
|
||||||
|
let(:result) { first_of_type(Search.query(topic.id, nil, 'topic'), 'topic') }
|
||||||
|
|
||||||
|
it 'returns the topic' do
|
||||||
|
result.should be_present
|
||||||
|
result['title'].should == topic.title
|
||||||
|
result['url'].should == topic.relative_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "search for a topic by url" do
|
||||||
|
let(:result) { first_of_type(Search.query(topic.relative_url, nil, 'topic'), 'topic') }
|
||||||
|
|
||||||
|
it 'returns the topic' do
|
||||||
|
result.should be_present
|
||||||
|
result['title'].should == topic.title
|
||||||
|
result['url'].should == topic.relative_url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'security' do
|
context 'security' do
|
||||||
|
|
Loading…
Reference in New Issue