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:foo | user:foo | group:foo | badge:foo | |
in:likes | in:posted | in:watching | in:tracking | in:private |
in:bookmarks | in:first | |
- posts_count:num | min_age:days | max_age:days | |
+ posts_count:num | before:days or date | after: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