Merge pull request #4896 from dmacjam/multiple-tags-logical-and-search
FEATURE: Advanced search supports logical AND of multiple tags
This commit is contained in:
commit
b98a930364
|
@ -77,7 +77,8 @@ export default Em.Component.extend({
|
|||
likes: false,
|
||||
private: false,
|
||||
seen: false
|
||||
}
|
||||
},
|
||||
all_tags: false
|
||||
},
|
||||
status: '',
|
||||
min_post_count: '',
|
||||
|
@ -230,13 +231,15 @@ export default Em.Component.extend({
|
|||
|
||||
const match = this.filterBlocks(REGEXP_TAGS_PREFIX);
|
||||
const tags = this.get('searchedTerms.tags');
|
||||
const contain_all_tags = this.get('searchedTerms.special.all_tags');
|
||||
|
||||
if (match.length !== 0) {
|
||||
const existingInput = _.isArray(tags) ? tags.join(',') : tags;
|
||||
const join_char = contain_all_tags ? '+' : ',';
|
||||
const existingInput = _.isArray(tags) ? tags.join(join_char) : tags;
|
||||
const userInput = match[0].replace(REGEXP_TAGS_REPLACE, '');
|
||||
|
||||
if (existingInput !== userInput) {
|
||||
this.set('searchedTerms.tags', (userInput.length !== 0) ? userInput.split(',') : []);
|
||||
this.set('searchedTerms.tags', (userInput.length !== 0) ? userInput.split(join_char) : []);
|
||||
}
|
||||
} else if (tags.length !== 0) {
|
||||
this.set('searchedTerms.tags', []);
|
||||
|
@ -365,14 +368,16 @@ export default Em.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
@observes('searchedTerms.tags')
|
||||
@observes('searchedTerms.tags', 'searchedTerms.special.all_tags')
|
||||
updateSearchTermForTags() {
|
||||
const match = this.filterBlocks(REGEXP_TAGS_PREFIX);
|
||||
const tagFilter = this.get('searchedTerms.tags');
|
||||
let searchTerm = this.get('searchTerm') || '';
|
||||
const contain_all_tags = this.get('searchedTerms.special.all_tags');
|
||||
|
||||
if (tagFilter && tagFilter.length !== 0) {
|
||||
const tags = tagFilter.join(',');
|
||||
const join_char = contain_all_tags ? '+' : ',';
|
||||
const tags = tagFilter.join(join_char);
|
||||
|
||||
if (match.length !== 0) {
|
||||
searchTerm = searchTerm.replace(match[0], `tags:${tags}`);
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
<label class="control-label" for="search-with-tags">{{i18n "search.advanced.with_tags.label"}}</label>
|
||||
<div class="controls">
|
||||
{{tag-chooser tags=searchedTerms.tags blacklist=searchedTerms.tags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true" width="70%"}}
|
||||
<section class="field">
|
||||
<label>{{ input type="checkbox" class="all-tags" checked=searchedTerms.special.all_tags}} {{i18n "search.advanced.filters.all_tags"}} </label>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1338,6 +1338,7 @@ en:
|
|||
seen: I've read
|
||||
unseen: I've not read
|
||||
wiki: are wiki
|
||||
all_tags: Contains all tags
|
||||
statuses:
|
||||
label: Where topics
|
||||
open: are open
|
||||
|
|
|
@ -447,15 +447,27 @@ class Search
|
|||
end
|
||||
end
|
||||
|
||||
advanced_filter(/tags?:([a-zA-Z0-9,\-_]+)/) do |posts, match|
|
||||
tags = match.split(",")
|
||||
advanced_filter(/tags?:([a-zA-Z0-9,\-_+]+)/) do |posts, match|
|
||||
if match.include?('+')
|
||||
tags = match.split('+')
|
||||
|
||||
posts.where("topics.id IN (
|
||||
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(#{query_locale}, array_to_string(array_agg(tags.name), ' ')) @@ to_tsquery(#{query_locale}, ?)
|
||||
)", 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
|
||||
|
||||
private
|
||||
|
|
|
@ -706,15 +706,35 @@ describe Search do
|
|||
expect(Search.execute('this is a test #beta').posts.size).to eq(0)
|
||||
end
|
||||
|
||||
it "can find with tag" do
|
||||
topic1 = Fabricate(:topic, title: 'Could not, would not, on a boat')
|
||||
topic1.tags = [Fabricate(:tag, name: 'eggs'), Fabricate(:tag, name: 'ham')]
|
||||
Fabricate(:post, topic: topic1)
|
||||
post2 = Fabricate(:post, topic: topic1, raw: "It probably doesn't help that they're green...")
|
||||
context 'tags' do
|
||||
let(:tag1) { Fabricate(:tag, name: 'lunch') }
|
||||
let(:tag2) { Fabricate(:tag, name: 'eggs') }
|
||||
let(:topic1) { Fabricate(:topic, tags: [tag2, Fabricate(:tag)]) }
|
||||
let(:topic2) { Fabricate(:topic, tags: [tag2]) }
|
||||
let(:topic3) { Fabricate(:topic, tags: [tag1, tag2]) }
|
||||
let!(:post1) { Fabricate(:post, topic: topic1)}
|
||||
let!(:post2) { Fabricate(:post, topic: topic2)}
|
||||
let!(:post3) { Fabricate(:post, topic: topic3)}
|
||||
|
||||
expect(Search.execute('green tags:eggs').posts.map(&:id)).to eq([post2.id])
|
||||
expect(Search.execute('green tags:plants').posts.size).to eq(0)
|
||||
it 'can find posts with tag' do
|
||||
post4 = Fabricate(:post, topic: topic3, raw: "It probably doesn't help that they're green...")
|
||||
|
||||
expect(Search.execute('green tags:eggs').posts.map(&:id)).to eq([post4.id])
|
||||
expect(Search.execute('tags:plants').posts.size).to eq(0)
|
||||
end
|
||||
|
||||
it 'can find posts with any tag from multiple tags' do
|
||||
Fabricate(:post)
|
||||
|
||||
expect(Search.execute('tags:eggs,lunch').posts.map(&:id).sort).to eq([post1.id, post2.id, post3.id].sort)
|
||||
end
|
||||
|
||||
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:eggs+lunch').posts.map(&:id)).to eq([post3.id])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it 'can parse complex strings using ts_query helper' do
|
||||
|
|
Loading…
Reference in New Issue