FEATURE: Make search filters case insensitive (#10715)

This commit is contained in:
Bianca Nenciu 2020-09-23 11:59:42 +03:00 committed by GitHub
parent 18f0cef72e
commit 4abbe3d361
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 64 deletions

View File

@ -114,7 +114,7 @@ export default Component.extend({
this.setSearchedTermValueForTags();
let regExpInMatch = this.inOptions.map((option) => option.value).join("|");
const REGEXP_IN_MATCH = new RegExp(`(in|with):(${regExpInMatch})`);
const REGEXP_IN_MATCH = new RegExp(`(in|with):(${regExpInMatch})`, "i");
this.setSearchedTermValue(
"searchedTerms.in",
@ -145,7 +145,10 @@ export default Component.extend({
let regExpStatusMatch = this.statusOptions
.map((status) => status.value)
.join("|");
const REGEXP_STATUS_MATCH = new RegExp(`status:(${regExpStatusMatch})`);
const REGEXP_STATUS_MATCH = new RegExp(
`status:(${regExpStatusMatch})`,
"i"
);
this.setSearchedTermValue(
"searchedTerms.status",
@ -203,7 +206,7 @@ export default Component.extend({
let val = this.get(key);
if (match.length !== 0) {
const userInput = match[0].replace(replaceRegEx, "");
const userInput = match[0].replace(replaceRegEx, "").toLowerCase();
if (val !== userInput && userInput.length) {
this.set(key, userInput);
@ -298,7 +301,9 @@ export default Component.extend({
if (match.length !== 0) {
const existingInputWhen = this.get("searchedTerms.time.when");
const userInputWhen = match[0].match(REGEXP_POST_TIME_WHEN)[0];
const userInputWhen = match[0]
.match(REGEXP_POST_TIME_WHEN)[0]
.toLowerCase();
const existingInputDays = this.get("searchedTerms.time.days");
const userInputDays = match[0].replace(REGEXP_POST_TIME_PREFIX, "");
const properties = {};
@ -549,7 +554,7 @@ export default Component.extend({
_updateSearchTermForIn() {
let regExpInMatch = this.inOptions.map((option) => option.value).join("|");
const REGEXP_IN_MATCH = new RegExp(`(in|with):(${regExpInMatch})`);
const REGEXP_IN_MATCH = new RegExp(`(in|with):(${regExpInMatch})`, "i");
const match = this.filterBlocks(REGEXP_IN_MATCH);
const inFilter = this.get("searchedTerms.in");
@ -577,7 +582,10 @@ export default Component.extend({
let regExpStatusMatch = this.statusOptions
.map((status) => status.value)
.join("|");
const REGEXP_STATUS_MATCH = new RegExp(`status:(${regExpStatusMatch})`);
const REGEXP_STATUS_MATCH = new RegExp(
`status:(${regExpStatusMatch})`,
"i"
);
const match = this.filterBlocks(REGEXP_STATUS_MATCH);
const statusFilter = this.get("searchedTerms.status");

View File

@ -305,7 +305,7 @@ class Search
Array.wrap(@custom_topic_eager_loads)
end
advanced_filter(/^in:personal-direct$/) do |posts|
advanced_filter(/^in:personal-direct$/i) do |posts|
if @guardian.user
posts
.joins("LEFT JOIN topic_allowed_groups tg ON posts.topic_id = tg.topic_id")
@ -325,27 +325,27 @@ class Search
end
end
advanced_filter(/^in:tagged$/) do |posts|
advanced_filter(/^in:tagged$/i) do |posts|
posts
.where('EXISTS (SELECT 1 FROM topic_tags WHERE topic_tags.topic_id = posts.topic_id)')
end
advanced_filter(/^in:untagged$/) do |posts|
advanced_filter(/^in:untagged$/i) do |posts|
posts
.joins("LEFT JOIN topic_tags ON
topic_tags.topic_id = posts.topic_id")
.where("topic_tags.id IS NULL")
end
advanced_filter(/^status:open$/) do |posts|
advanced_filter(/^status:open$/i) do |posts|
posts.where('NOT topics.closed AND NOT topics.archived')
end
advanced_filter(/^status:closed$/) do |posts|
advanced_filter(/^status:closed$/i) do |posts|
posts.where('topics.closed')
end
advanced_filter(/^status:public$/) do |posts|
advanced_filter(/^status:public$/i) do |posts|
category_ids = Category
.where(read_restricted: false)
.pluck(:id)
@ -353,39 +353,39 @@ class Search
posts.where("topics.category_id in (?)", category_ids)
end
advanced_filter(/^status:archived$/) do |posts|
advanced_filter(/^status:archived$/i) do |posts|
posts.where('topics.archived')
end
advanced_filter(/^status:noreplies$/) do |posts|
advanced_filter(/^status:noreplies$/i) do |posts|
posts.where("topics.posts_count = 1")
end
advanced_filter(/^status:single_user$/) do |posts|
advanced_filter(/^status:single_user$/i) do |posts|
posts.where("topics.participant_count = 1")
end
advanced_filter(/^posts_count:(\d+)$/) do |posts, match|
advanced_filter(/^posts_count:(\d+)$/i) do |posts, match|
posts.where("topics.posts_count = ?", match.to_i)
end
advanced_filter(/^min_post_count:(\d+)$/) do |posts, match|
advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match|
posts.where("topics.posts_count >= ?", match.to_i)
end
advanced_filter(/^in:first|^f$/) do |posts|
advanced_filter(/^in:first|^f$/i) do |posts|
posts.where("posts.post_number = 1")
end
advanced_filter(/^in:pinned$/) do |posts|
advanced_filter(/^in:pinned$/i) do |posts|
posts.where("topics.pinned_at IS NOT NULL")
end
advanced_filter(/^in:wiki$/) do |posts, match|
advanced_filter(/^in:wiki$/i) do |posts, match|
posts.where(wiki: true)
end
advanced_filter(/^badge:(.*)$/) do |posts, match|
advanced_filter(/^badge:(.*)$/i) do |posts, match|
badge_id = Badge.where('name ilike ? OR id = ?', match, match.to_i).pluck_first(:id)
if badge_id
posts.where('posts.user_id IN (SELECT ub.user_id FROM user_badges ub WHERE ub.badge_id = ?)', badge_id)
@ -403,34 +403,34 @@ class Search
)")
end
advanced_filter(/^in:(likes)$/) do |posts, match|
advanced_filter(/^in:(likes)$/i) do |posts, match|
if @guardian.user
post_action_type_filter(posts, PostActionType.types[:like])
end
end
advanced_filter(/^in:(bookmarks)$/) do |posts, match|
advanced_filter(/^in:(bookmarks)$/i) do |posts, match|
if @guardian.user
posts.where("posts.id IN (SELECT post_id FROM bookmarks WHERE bookmarks.user_id = #{@guardian.user.id})")
end
end
advanced_filter(/^in:posted$/) do |posts|
advanced_filter(/^in:posted$/i) do |posts|
posts.where("posts.user_id = #{@guardian.user.id}") if @guardian.user
end
advanced_filter(/^in:created$/) do |posts|
advanced_filter(/^in:created$/i) do |posts|
posts.where(user_id: @guardian.user.id, post_number: 1) if @guardian.user
end
advanced_filter(/^created:@(.*)$/) do |posts, match|
advanced_filter(/^created:@(.*)$/i) do |posts, match|
user_id = User.where(username: match.downcase).pluck_first(:id)
posts.where(user_id: user_id, post_number: 1)
end
advanced_filter(/^in:(watching|tracking)$/) do |posts, match|
advanced_filter(/^in:(watching|tracking)$/i) do |posts, match|
if @guardian.user
level = TopicUser.notification_levels[match.to_sym]
level = TopicUser.notification_levels[match.downcase.to_sym]
posts.where("posts.topic_id IN (
SELECT tu.topic_id FROM topic_users tu
WHERE tu.user_id = :user_id AND
@ -440,7 +440,7 @@ class Search
end
end
advanced_filter(/^in:seen$/) do |posts|
advanced_filter(/^in:seen$/i) do |posts|
if @guardian.user
posts
.joins("INNER JOIN post_timings ON
@ -451,7 +451,7 @@ class Search
end
end
advanced_filter(/^in:unseen$/) do |posts|
advanced_filter(/^in:unseen$/i) do |posts|
if @guardian.user
posts
.joins("LEFT JOIN post_timings ON
@ -463,11 +463,11 @@ class Search
end
end
advanced_filter(/^with:images$/) do |posts|
advanced_filter(/^with:images$/i) do |posts|
posts.where("posts.image_upload_id IS NOT NULL")
end
advanced_filter(/^category:(.+)$/) do |posts, match|
advanced_filter(/^category:(.+)$/i) do |posts, match|
exact = false
if match[0] == "="
@ -491,7 +491,7 @@ class Search
end
end
advanced_filter(/^\#([\p{L}\p{M}0-9\-:=]+)$/) do |posts, match|
advanced_filter(/^\#([\p{L}\p{M}0-9\-:=]+)$/i) do |posts, match|
exact = true
@ -570,7 +570,7 @@ class Search
end
end
advanced_filter(/^group:(.+)$/) do |posts, match|
advanced_filter(/^group:(.+)$/i) do |posts, match|
group_id = Group.where('name ilike ? OR (id = ? AND id > 0)', match, match.to_i).pluck_first(:id)
if group_id
posts.where("posts.user_id IN (select gu.user_id from group_users gu where gu.group_id = ?)", group_id)
@ -579,7 +579,7 @@ class Search
end
end
advanced_filter(/^user:(.+)$/) do |posts, match|
advanced_filter(/^user:(.+)$/i) do |posts, match|
user_id = User.where(staged: false).where('username_lower = ? OR id = ?', match.downcase, match.to_i).pluck_first(:id)
if user_id
posts.where("posts.user_id = #{user_id}")
@ -588,7 +588,7 @@ class Search
end
end
advanced_filter(/^\@([a-zA-Z0-9_\-.]+)$/) do |posts, match|
advanced_filter(/^\@([a-zA-Z0-9_\-.]+)$/i) do |posts, match|
user_id = User.where(staged: false).where(username_lower: match.downcase).pluck_first(:id)
if user_id
posts.where("posts.user_id = #{user_id}")
@ -597,7 +597,7 @@ class Search
end
end
advanced_filter(/^before:(.*)$/) do |posts, match|
advanced_filter(/^before:(.*)$/i) do |posts, match|
if date = Search.word_to_date(match)
posts.where("posts.created_at < ?", date)
else
@ -605,7 +605,7 @@ class Search
end
end
advanced_filter(/^after:(.*)$/) do |posts, match|
advanced_filter(/^after:(.*)$/i) do |posts, match|
if date = Search.word_to_date(match)
posts.where("posts.created_at > ?", date)
else
@ -613,15 +613,15 @@ class Search
end
end
advanced_filter(/^tags?:([\p{L}\p{M}0-9,\-_+]+)$/) do |posts, match|
advanced_filter(/^tags?:([\p{L}\p{M}0-9,\-_+]+)$/i) do |posts, match|
search_tags(posts, match, positive: true)
end
advanced_filter(/^\-tags?:([\p{L}\p{M}0-9,\-_+]+)$/) do |posts, match|
advanced_filter(/^\-tags?:([\p{L}\p{M}0-9,\-_+]+)$/i) do |posts, match|
search_tags(posts, match, positive: false)
end
advanced_filter(/^filetypes?:([a-zA-Z0-9,\-_]+)$/) do |posts, match|
advanced_filter(/^filetypes?:([a-zA-Z0-9,\-_]+)$/i) do |posts, match|
file_extensions = match.split(",").map(&:downcase)
posts.where("posts.id IN (
SELECT post_id
@ -682,13 +682,13 @@ class Search
if word == 'l'
@order = :latest
nil
elsif word =~ /order:\w+/
@order = word.gsub('order:', '').to_sym
elsif word =~ /^order:\w+$/i
@order = word.downcase.gsub('order:', '').to_sym
nil
elsif word == 'in:title' || word == 't'
elsif word =~ /^in:title$/i || word == 't'
@in_title = true
nil
elsif word =~ /topic:(\d+)/
elsif word =~ /^topic:(\d+)$/i
topic_id = $1.to_i
if topic_id > 1
topic = Topic.find_by(id: topic_id)
@ -697,16 +697,16 @@ class Search
end
end
nil
elsif word == 'in:all'
elsif word =~ /^in:all$/i
@search_all_topics = true
nil
elsif word == 'in:personal'
elsif word =~ /^in:personal$/i
@search_pms = true
nil
elsif word == "in:personal-direct"
elsif word =~ /^in:personal-direct$/i
@search_pms = true
nil
elsif word =~ /^personal_messages:(.+)$/
elsif word =~ /^personal_messages:(.+)$/i
if user = User.find_by_username($1)
@search_pms = true
@search_context = user

View File

@ -290,12 +290,10 @@ describe Search do
TopicAllowedGroup.create!(group_id: group.id, topic_id: topic.id)
results = Search.execute(
'mars in:personal',
guardian: Guardian.new(user)
)
expect(results.posts).to contain_exactly(reply)
["mars in:personal", "mars IN:PERSONAL"].each do |query|
results = Search.execute(query, guardian: Guardian.new(user))
expect(results.posts).to contain_exactly(reply)
end
end
context 'personal_messages filter' do
@ -359,19 +357,27 @@ describe Search do
_pm_2 = create_pm(users: [participant_2, participant])
pm_3 = create_pm(users: [participant, current])
pm_4 = create_pm(users: [participant_2, current])
results = Search.execute("in:personal-direct", guardian: Guardian.new(current))
expect(results.posts.size).to eq(3)
expect(results.posts.map(&:topic_id)).to eq([pm_4.id, pm_3.id, pm.id])
["in:personal-direct", "In:PeRsOnAl-DiReCt"].each do |query|
results = Search.execute(query, guardian: Guardian.new(current))
expect(results.posts.size).to eq(3)
expect(results.posts.map(&:topic_id)).to eq([pm_4.id, pm_3.id, pm.id])
end
end
it 'can filter direct PMs by @username' do
pm = create_pm(users: [current, participant])
pm_2 = create_pm(users: [participant, current])
_pm_3 = create_pm(users: [participant_2, current])
results = Search.execute("@#{participant.username} in:personal-direct", guardian: Guardian.new(current))
expect(results.posts.size).to eq(2)
expect(results.posts.map(&:topic_id)).to eq([pm_2.id, pm.id])
expect(results.posts.map(&:user_id).uniq).to eq([participant.id])
[
"@#{participant.username} in:personal-direct",
"@#{participant.username} iN:pErSoNaL-dIrEcT"
].each do |query|
results = Search.execute(query, guardian: Guardian.new(current))
expect(results.posts.size).to eq(2)
expect(results.posts.map(&:topic_id)).to eq([pm_2.id, pm.id])
expect(results.posts.map(&:user_id).uniq).to eq([participant.id])
end
end
it "doesn't include PMs that have more than 2 participants" do
@ -406,6 +412,10 @@ describe Search do
TopicAllowedUser.create!(user_id: u1.id, topic_id: private_topic.id)
TopicAllowedUser.create!(user_id: u2.id, topic_id: private_topic.id)
# case insensitive only
results = Search.execute('iN:aLL cheese', guardian: Guardian.new(u1))
expect(results.posts).to contain_exactly(private_post1)
# private only
results = Search.execute('in:all cheese', guardian: Guardian.new(u1))
expect(results.posts).to contain_exactly(private_post1)
@ -1110,6 +1120,7 @@ describe Search do
guardian = Guardian.new(user)
expect(Search.execute('boom in:pinned').posts.length).to eq(1)
expect(Search.execute('boom IN:PINNED').posts.length).to eq(1)
end
it 'supports wiki' do
@ -1120,6 +1131,7 @@ describe Search do
expect(Search.execute('test 248').posts.length).to eq(2)
expect(Search.execute('test 248 in:wiki').posts.first).to eq(post)
expect(Search.execute('test 248 IN:WIKI').posts.first).to eq(post)
end
it 'supports searching for posts that the user has seen/unseen' do
@ -1144,6 +1156,9 @@ describe Search do
expect(Search.execute('longan in:seen', guardian: Guardian.new(post.user)).posts)
.to eq([post])
expect(Search.execute('longan IN:SEEN', guardian: Guardian.new(post.user)).posts)
.to eq([post])
expect(Search.execute('longan in:seen').posts.sort).to eq([post, post_2])
expect(Search.execute('longan in:seen', guardian: Guardian.new(post_2.user)).posts)
@ -1157,6 +1172,9 @@ describe Search do
expect(Search.execute('longan in:unseen', guardian: Guardian.new(post.user)).posts)
.to eq([post_2])
expect(Search.execute('longan IN:UNSEEN', guardian: Guardian.new(post.user)).posts)
.to eq([post_2])
end
it 'supports before and after filters' do
@ -1180,6 +1198,7 @@ describe Search do
post_2 = Fabricate(:post, raw: 'boom boom shake the room test', topic: topic)
expect(Search.execute('test in:first').posts).to contain_exactly(post_1)
expect(Search.execute('test IN:FIRST').posts).to contain_exactly(post_1)
expect(Search.execute('boom').posts).to contain_exactly(post_2)
@ -1216,6 +1235,7 @@ describe Search do
UserBadge.create!(user_id: post.user_id, badge_id: badge.id, granted_at: 1.minute.ago, granted_by_id: -1)
expect(Search.execute('badge:"like a boss"').posts.length).to eq(1)
expect(Search.execute('BADGE:"LIKE A BOSS"').posts.length).to eq(1)
expect(Search.execute('badge:"test"').posts.length).to eq(0)
end
@ -1255,6 +1275,7 @@ describe Search do
expect(Search.execute('test status:public').posts.length).to eq(1)
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)
expect(Search.execute('test min_post_count:1').posts.length).to eq(1)
@ -1277,6 +1298,7 @@ describe Search do
expect(Search.execute('test in:likes', guardian: Guardian.new(topic.user)).posts.length).to eq(0)
expect(Search.execute('test in:posted', guardian: Guardian.new(topic.user)).posts.length).to eq(2)
expect(Search.execute('test In:PoStEd', guardian: Guardian.new(topic.user)).posts.length).to eq(2)
in_created = Search.execute('test in:created', guardian: Guardian.new(topic.user)).posts
created_by_user = Search.execute("test created:@#{topic.user.username}", guardian: Guardian.new(topic.user)).posts
@ -1309,7 +1331,7 @@ describe Search do
post2 = Fabricate(:post, raw: 'that Sam I am, that Sam I am', created_at: 5.minutes.ago)
expect(Search.execute('sam').posts.map(&:id)).to eq([post1.id, post2.id])
expect(Search.execute('sam order:latest').posts.map(&:id)).to eq([post2.id, post1.id])
expect(Search.execute('sam ORDER:LATEST').posts.map(&:id)).to eq([post2.id, post1.id])
expect(Search.execute('sam l').posts.map(&:id)).to eq([post2.id, post1.id])
expect(Search.execute('l sam').posts.map(&:id)).to eq([post2.id, post1.id])
end
@ -1662,6 +1684,9 @@ describe Search do
results = Search.execute('title in:title')
expect(results.posts.map(&:id)).to eq([post.id])
results = Search.execute('title iN:tItLe')
expect(results.posts.map(&:id)).to eq([post.id])
results = Search.execute('first in:title')
expect(results.posts).to eq([])
end
@ -1770,6 +1795,9 @@ describe Search do
results = Search.execute('in:tagged')
expect(results.posts.length).to eq(1)
results = Search.execute('In:TaGgEd')
expect(results.posts.length).to eq(1)
end
end
@ -1778,7 +1806,7 @@ describe Search do
topic = Fabricate(:topic, title: 'I am testing a untagged search')
_post = Fabricate(:post, topic: topic, raw: 'this is the first post')
results = Search.execute('in:untagged')
results = Search.execute('iN:uNtAgGeD')
expect(results.posts.length).to eq(1)
results = Search.execute('in:tagged')