From e00850a1abe67c8721a340dd37c2008e1c56a3d4 Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Mon, 14 Mar 2016 23:27:02 +1100 Subject: [PATCH] FEATURE: implement before and after filters in search remove max_age and min_age supports - before:monday - after:june - before:2001 - before:2001-01-22 --- config/locales/server.en.yml | 2 +- lib/search.rb | 52 ++++++++++++++++++++++++++++++---- spec/components/search_spec.rb | 49 +++++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index d3a05ffa9..7831d5e74 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2723,7 +2723,7 @@ en: category:foouser:foogroup:foobadge:foo in:likesin:postedin:watchingin:trackingin:private in:bookmarksin:first - posts_count:nummin_age:daysmax_age:days + posts_count:numbefore:days or dateafter:days or date

Examples

diff --git a/lib/search.rb b/lib/search.rb index a7c5c1212..fb8b989a4 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -94,6 +94,40 @@ class Search data end + def self.word_to_date(str) + + if str =~ /^[0-9]{1,3}$/ + return Time.zone.now.beginning_of_day.days_ago(str.to_i) + end + + if str =~ /^([12][0-9]{3})(-([0-1]?[0-9]))?(-([0-3]?[0-9]))?$/ + year = $1.to_i + month = $2 ? $3.to_i : 1 + day = $4 ? $5.to_i : 1 + + return if day==0 || month==0 || day > 31 || month > 12 + + return Time.zone.parse("#{year}-#{month}-#{day}") rescue nil + end + + if str.downcase == "yesterday" + return Time.zone.now.beginning_of_day.yesterday + end + + titlecase = str.downcase.titlecase + + if Date::DAYNAMES.include?(titlecase) + return Time.zone.now.beginning_of_week(str.downcase.to_sym) + end + + if idx = (Date::MONTHNAMES.find_index(titlecase) || + Date::ABBR_MONTHNAMES.find_index(titlecase)) + delta = Time.zone.now.month - idx + delta += 12 if delta < 0 + Time.zone.now.beginning_of_month.months_ago(delta) + end + end + def initialize(term, opts=nil) @opts = opts || {} @guardian = @opts[:guardian] || Guardian.new @@ -251,14 +285,20 @@ class Search end end - advanced_filter(/min_age:(\d+)/) do |posts,match| - n = match.to_i - posts.where("topics.created_at > ?", n.days.ago) + advanced_filter(/before:(.*)/) do |posts,match| + if date = Search.word_to_date(match) + posts.where("posts.created_at < ?", date) + else + posts + end end - advanced_filter(/max_age:(\d+)/) do |posts,match| - n = match.to_i - posts.where("topics.created_at < ?", n.days.ago) + advanced_filter(/after:(.*)/) do |posts,match| + if date = Search.word_to_date(match) + posts.where("posts.created_at > ?", date) + else + posts + end end private diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 71d5da7cd..42a4f1866 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -408,15 +408,21 @@ describe Search do describe 'Advanced search' do - it 'supports min_age and max_age in:first user:' do - topic = Fabricate(:topic, created_at: 3.months.ago) - Fabricate(:post, raw: 'hi this is a test 123 123', topic: topic) + it 'supports before and after in:first user:' do + + time = Time.zone.parse('2001-05-20 2:55') + freeze_time(time) + + topic = Fabricate(:topic) + Fabricate(:post, raw: 'hi this is a test 123 123', topic: topic, created_at: time.months_ago(2)) _post = Fabricate(:post, raw: 'boom boom shake the room', topic: topic) - expect(Search.execute('test min_age:100').posts.length).to eq(1) - expect(Search.execute('test min_age:10').posts.length).to eq(0) - expect(Search.execute('test max_age:10').posts.length).to eq(1) - expect(Search.execute('test max_age:100').posts.length).to eq(0) + expect(Search.execute('test before:1').posts.length).to eq(1) + expect(Search.execute('test before:2001-04-20').posts.length).to eq(1) + expect(Search.execute('test before:2001').posts.length).to eq(0) + expect(Search.execute('test before:monday').posts.length).to eq(1) + + expect(Search.execute('test after:jan').posts.length).to eq(1) expect(Search.execute('test in:first').posts.length).to eq(1) expect(Search.execute('boom').posts.length).to eq(1) @@ -512,5 +518,34 @@ describe Search do Post.exec_sql("SELECT to_tsvector('bbb') @@ " << ts_query) end + context '#word_to_date' do + it 'parses relative dates correctly' do + time = Time.zone.parse('2001-02-20 2:55') + freeze_time(time) + + expect(Search.word_to_date('yesterday')).to eq(time.beginning_of_day.yesterday) + expect(Search.word_to_date('suNday')).to eq(Time.zone.parse('2001-02-18')) + expect(Search.word_to_date('thursday')).to eq(Time.zone.parse('2001-02-15')) + expect(Search.word_to_date('deCember')).to eq(Time.zone.parse('2000-12-01')) + expect(Search.word_to_date('deC')).to eq(Time.zone.parse('2000-12-01')) + expect(Search.word_to_date('january')).to eq(Time.zone.parse('2001-01-01')) + expect(Search.word_to_date('jan')).to eq(Time.zone.parse('2001-01-01')) + + + expect(Search.word_to_date('100')).to eq(time.beginning_of_day.days_ago(100)) + + expect(Search.word_to_date('invalid')).to eq(nil) + end + + it 'parses absolute dates correctly' do + expect(Search.word_to_date('2001-1-20')).to eq(Time.zone.parse('2001-01-20')) + expect(Search.word_to_date('2030-10-2')).to eq(Time.zone.parse('2030-10-02')) + expect(Search.word_to_date('2030-10')).to eq(Time.zone.parse('2030-10-01')) + expect(Search.word_to_date('2030')).to eq(Time.zone.parse('2030-01-01')) + expect(Search.word_to_date('2030-01-32')).to eq(nil) + expect(Search.word_to_date('10000')).to eq(nil) + end + end + end