diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 1c57b9b0248..dfe8b9e9d86 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -8,7 +8,7 @@ class SearchQueryParser < Parslet::Parser rule(:operator) { (str('+') | str('-')).as(:operator) } rule(:prefix) { term >> colon } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } - rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } + rule(:phrase) { (quote >> (match('[^\s"]').repeat(1).as(:term) >> space.maybe).repeat >> quote).as(:phrase) } rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 870f34cae8e..a45ae3d09bc 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -225,17 +225,16 @@ class SearchQueryTransformer < Parslet::Transform rule(clause: subtree(:clause)) do prefix = clause[:prefix][:term].to_s if clause[:prefix] operator = clause[:operator]&.to_s + term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix) - PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) + PrefixClause.new(prefix, operator, term, current_account: current_account) elsif clause[:prefix] - TermClause.new(operator, "#{prefix} #{clause[:term]}") + TermClause.new(operator, "#{prefix} #{term}") elsif clause[:term] - TermClause.new(operator, clause[:term].to_s) - elsif clause[:shortcode] - TermClause.new(operator, ":#{clause[:term]}:") + TermClause.new(operator, term) elsif clause[:phrase] - PhraseClause.new(operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) + PhraseClause.new(operator, term) else raise "Unexpected clause type: #{clause}" end diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index e4b38a9dabe..e2a5f41b10a 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -8,6 +8,7 @@ class StatusesSearchService < BaseService @limit = options[:limit].to_i @offset = options[:offset].to_i + convert_deprecated_options! status_search_results end @@ -28,4 +29,25 @@ class StatusesSearchService < BaseService def parsed_query SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account) end + + def convert_deprecated_options! + syntax_options = [] + + if @options[:account_id] + username = Account.select(:username, :domain).find(@options[:account_id]).acct + syntax_options << "from:@#{username}" + end + + if @options[:min_id] + timestamp = Mastodon::Snowflake.to_time(@options[:min_id]) + syntax_options << "after:\"#{timestamp.iso8601}\"" + end + + if @options[:max_id] + timestamp = Mastodon::Snowflake.to_time(@options[:max_id]) + syntax_options << "before:\"#{timestamp.iso8601}\"" + end + + @query = "#{@query} #{syntax_options.join(' ')}".strip if syntax_options.any? + end end diff --git a/lib/mastodon/snowflake.rb b/lib/mastodon/snowflake.rb index 8b79541da27..0a596b29401 100644 --- a/lib/mastodon/snowflake.rb +++ b/lib/mastodon/snowflake.rb @@ -104,6 +104,10 @@ module Mastodon::Snowflake id end + def to_time(id) + Time.at((id >> 16) / 1000).utc + end + private def already_defined? diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 4b949b1b825..5817e3d1d20 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -57,4 +57,24 @@ describe SearchQueryTransformer do expect(subject.send(:filter_clauses)).to be_empty end end + + context 'with \'"hello world"\'' do + let(:query) { '"hello world"' } + + it 'transforms clauses' do + expect(subject.send(:must_clauses).map(&:phrase)).to contain_exactly('hello world') + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses)).to be_empty + end + end + + context 'with \'before:"2022-01-01 23:00"\'' do + let(:query) { 'before:"2022-01-01 23:00"' } + + it 'transforms clauses' do + expect(subject.send(:must_clauses)).to be_empty + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses).map(&:term)).to contain_exactly(lt: '2022-01-01 23:00', time_zone: 'UTC') + end + end end