FEATURE: Add search not operator for tags.
This commit is contained in:
parent
3d2f3ef8ae
commit
b71cf6d422
|
@ -465,32 +465,16 @@ class Search
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
advanced_filter(/tags?:([a-zA-Z0-9,\-_+]+)/) do |posts, match|
|
advanced_filter(/^tags?:([a-zA-Z0-9,\-_+]+)/) do |posts, match|
|
||||||
if match.include?('+')
|
search_tags(posts, match, positive: true)
|
||||||
tags = match.split('+')
|
|
||||||
|
|
||||||
posts.where("topics.id IN (
|
|
||||||
SELECT tt.topic_id
|
|
||||||
FROM topic_tags tt, tags
|
|
||||||
WHERE tt.tag_id = tags.id
|
|
||||||
GROUP BY tt.topic_id
|
|
||||||
HAVING to_tsvector(#{default_ts_config}, array_to_string(array_agg(tags.name), ' ')) @@ to_tsquery(#{default_ts_config}, ?)
|
|
||||||
)", tags.join('&'))
|
|
||||||
else
|
|
||||||
tags = match.split(",")
|
|
||||||
|
|
||||||
posts.where("topics.id IN (
|
|
||||||
SELECT DISTINCT(tt.topic_id)
|
|
||||||
FROM topic_tags tt, tags
|
|
||||||
WHERE tt.tag_id = tags.id
|
|
||||||
AND tags.name in (?)
|
|
||||||
)", tags)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
advanced_filter(/\-tags?:([a-zA-Z0-9,\-_+]+)/) do |posts, match|
|
||||||
|
search_tags(posts, match, positive: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
advanced_filter(/filetypes?:([a-zA-Z0-9,\-_]+)/) do |posts, match|
|
advanced_filter(/filetypes?:([a-zA-Z0-9,\-_]+)/) do |posts, match|
|
||||||
file_extensions = match.split(",").map(&:downcase)
|
file_extensions = match.split(",").map(&:downcase)
|
||||||
|
|
||||||
posts.where("posts.id IN (
|
posts.where("posts.id IN (
|
||||||
SELECT post_id FROM topic_links
|
SELECT post_id FROM topic_links
|
||||||
WHERE extension IN (:file_extensions)
|
WHERE extension IN (:file_extensions)
|
||||||
|
@ -503,8 +487,33 @@ class Search
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def process_advanced_search!(term)
|
def search_tags(posts, match, positive:)
|
||||||
|
return if match.nil?
|
||||||
|
|
||||||
|
modifier = positive ? "" : "NOT"
|
||||||
|
|
||||||
|
if match.include?('+')
|
||||||
|
tags = match.split('+')
|
||||||
|
|
||||||
|
posts.where("topics.id #{modifier} IN (
|
||||||
|
SELECT tt.topic_id
|
||||||
|
FROM topic_tags tt, tags
|
||||||
|
WHERE tt.tag_id = tags.id
|
||||||
|
GROUP BY tt.topic_id
|
||||||
|
HAVING to_tsvector(#{default_ts_config}, array_to_string(array_agg(tags.name), ' ')) @@ to_tsquery(#{default_ts_config}, ?)
|
||||||
|
)", tags.join('&')).order("id")
|
||||||
|
else
|
||||||
|
tags = match.split(",")
|
||||||
|
|
||||||
|
posts.where("topics.id #{modifier} IN (
|
||||||
|
SELECT DISTINCT(tt.topic_id)
|
||||||
|
FROM topic_tags tt, tags
|
||||||
|
WHERE tt.tag_id = tags.id AND tags.name IN (?)
|
||||||
|
)", tags).order("id")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_advanced_search!(term)
|
||||||
term.to_s.scan(/(([^" \t\n\x0B\f\r]+)?(("[^"]+")?))/).to_a.map do |(word, _)|
|
term.to_s.scan(/(([^" \t\n\x0B\f\r]+)?(("[^"]+")?))/).to_a.map do |(word, _)|
|
||||||
next if word.blank?
|
next if word.blank?
|
||||||
|
|
||||||
|
|
|
@ -845,12 +845,17 @@ describe Search do
|
||||||
context 'tags' do
|
context 'tags' do
|
||||||
let(:tag1) { Fabricate(:tag, name: 'lunch') }
|
let(:tag1) { Fabricate(:tag, name: 'lunch') }
|
||||||
let(:tag2) { Fabricate(:tag, name: 'eggs') }
|
let(:tag2) { Fabricate(:tag, name: 'eggs') }
|
||||||
|
let(:tag3) { Fabricate(:tag, name: 'sandwiches') }
|
||||||
let(:topic1) { Fabricate(:topic, tags: [tag2, Fabricate(:tag)]) }
|
let(:topic1) { Fabricate(:topic, tags: [tag2, Fabricate(:tag)]) }
|
||||||
let(:topic2) { Fabricate(:topic, tags: [tag2]) }
|
let(:topic2) { Fabricate(:topic, tags: [tag2]) }
|
||||||
let(:topic3) { Fabricate(:topic, tags: [tag1, tag2]) }
|
let(:topic3) { Fabricate(:topic, tags: [tag1, tag2]) }
|
||||||
|
let(:topic4) { Fabricate(:topic, tags: [tag1, tag2, tag3]) }
|
||||||
|
let(:topic5) { Fabricate(:topic, tags: [tag2, tag3]) }
|
||||||
let!(:post1) { Fabricate(:post, topic: topic1) }
|
let!(:post1) { Fabricate(:post, topic: topic1) }
|
||||||
let!(:post2) { Fabricate(:post, topic: topic2) }
|
let!(:post2) { Fabricate(:post, topic: topic2) }
|
||||||
let!(:post3) { Fabricate(:post, topic: topic3) }
|
let!(:post3) { Fabricate(:post, topic: topic3) }
|
||||||
|
let!(:post4) { Fabricate(:post, topic: topic4) }
|
||||||
|
let!(:post5) { Fabricate(:post, topic: topic5) }
|
||||||
|
|
||||||
it 'can find posts with tag' do
|
it 'can find posts with tag' do
|
||||||
post4 = Fabricate(:post, topic: topic3, raw: "It probably doesn't help that they're green...")
|
post4 = Fabricate(:post, topic: topic3, raw: "It probably doesn't help that they're green...")
|
||||||
|
@ -862,12 +867,23 @@ describe Search do
|
||||||
it 'can find posts with any tag from multiple tags' do
|
it 'can find posts with any tag from multiple tags' do
|
||||||
Fabricate(:post)
|
Fabricate(:post)
|
||||||
|
|
||||||
expect(Search.execute('tags:eggs,lunch').posts.map(&:id).sort).to eq([post1.id, post2.id, post3.id].sort)
|
expect(Search.execute('tags:eggs,lunch').posts.map(&:id).sort).to eq([post1.id, post2.id, post3.id, post4.id, post5.id].sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can find posts which contains all provided tags' do
|
it 'can find posts which contains all provided tags' do
|
||||||
expect(Search.execute('tags:lunch+eggs').posts.map(&:id)).to eq([post3.id])
|
expect(Search.execute('tags:lunch+eggs+sandwiches').posts.map(&:id)).to eq([post4.id].sort)
|
||||||
expect(Search.execute('tags:eggs+lunch').posts.map(&:id)).to eq([post3.id])
|
expect(Search.execute('tags:eggs+lunch+sandwiches').posts.map(&:id)).to eq([post4.id].sort)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can find posts which contains provided tags and does not contain selected ones' do
|
||||||
|
expect(Search.execute('tags:eggs -tags:lunch').posts)
|
||||||
|
.to contain_exactly(post1, post2, post5)
|
||||||
|
|
||||||
|
expect(Search.execute('tags:eggs -tags:lunch+sandwiches').posts)
|
||||||
|
.to contain_exactly(post1, post2, post3, post5)
|
||||||
|
|
||||||
|
expect(Search.execute('tags:eggs -tags:lunch,sandwiches').posts)
|
||||||
|
.to contain_exactly(post1, post2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue