From 12a00d6dc5ae31fbcdcaa189ead7846877b3ea82 Mon Sep 17 00:00:00 2001 From: Krzysztof Kotlarek Date: Fri, 7 Aug 2020 12:47:00 +1000 Subject: [PATCH] FEATURE: add advanced order to search (#10385) Similar to `advanced_filter` I introduced `advanced_order`. I needed a new option because default orders are evaluated after advanced_filter so I couldn't use it. Also, that part is a little bit more generic ``` elsif word =~ /order:\w+/ @order = word.gsub('order:', '').to_sym nil ``` After those changes, I can use them in plugins in this way: ``` Search.advanced_order(:votes) do |posts| posts.reorder("COALESCE((SELECT dvvc.counter FROM discourse_voting_vote_counters dvvc WHERE dvvc.topic_id = subquery.topic_id), 0) DESC") end ``` --- lib/plugin/instance.rb | 16 ++++++++++++++++ lib/search.rb | 25 ++++++++++++++++--------- spec/components/search_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index f1de573cc8f..0ca72b1e6e3 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -190,6 +190,22 @@ class Plugin::Instance DiscoursePluginRegistry.register_editable_group_custom_field(field, self) end + # Allows to define custom search order. Example usage: + # Search.advanced_order(:chars) do |posts| + # posts.reorder("(SELECT LENGTH(raw) FROM posts WHERE posts.topic_id = subquery.topic_id) DESC") + # end + def register_search_advanced_order(trigger, &block) + Search.advanced_order(trigger, &block) + end + + # Allows to define custom search filters. Example usage: + # Search.advanced_filter(/^min_chars:(\d+)$/) do |posts, match| + # posts.where("(SELECT LENGTH(p2.raw) FROM posts p2 WHERE p2.id = posts.id) >= ?", match.to_i) + # end + def register_search_advanced_filter(trigger, &block) + Search.advanced_filter(trigger, &block) + end + # Request a new size for topic thumbnails # Will respect plugin enabled setting is enabled # Size should be an array with two elements [max_width, max_height] diff --git a/lib/search.rb b/lib/search.rb index 218aaac7247..d8f2e40bb3c 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -277,6 +277,14 @@ class Search @results end + def self.advanced_order(trigger, &block) + (@advanced_orders ||= {})[trigger] = block + end + + def self.advanced_orders + @advanced_orders + end + def self.advanced_filter(trigger, &block) (@advanced_filters ||= {})[trigger] = block end @@ -659,11 +667,11 @@ class Search end end - if word == 'order:latest' || word == 'l' + if word == 'l' @order = :latest nil - elsif word == 'order:latest_topic' - @order = :latest_topic + elsif word =~ /order:\w+/ + @order = word.gsub('order:', '').to_sym nil elsif word == 'in:title' || word == 't' @in_title = true @@ -677,12 +685,6 @@ class Search end end nil - elsif word == 'order:views' - @order = :views - nil - elsif word == 'order:likes' - @order = :likes - nil elsif word == 'in:all' @search_all_topics = true nil @@ -1011,6 +1013,11 @@ class Search posts = aggregate_relation.from(posts) end + if @order + advanced_order = Search.advanced_orders&.fetch(@order, nil) + posts = advanced_order.call(posts) if advanced_order + end + posts = posts.offset(offset) posts.limit(limit) end diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 5a7de3bf611..00ceb74a535 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -1690,4 +1690,24 @@ describe Search do end end + context 'plugin extensions' do + let!(:post0) { Fabricate(:post, raw: 'this is the first post about advanced filter with length more than 50 chars') } + let!(:post1) { Fabricate(:post, raw: 'this is the second post about advanced filter') } + + it 'allows to define custom filter' do + expect(Search.new("advanced").execute.posts).to eq([post1, post0]) + Search.advanced_filter(/^min_chars:(\d+)$/) do |posts, match| + posts.where("(SELECT LENGTH(p2.raw) FROM posts p2 WHERE p2.id = posts.id) >= ?", match.to_i) + end + expect(Search.new("advanced min_chars:50").execute.posts).to eq([post0]) + end + + it 'allows to define custom order' do + expect(Search.new("advanced").execute.posts).to eq([post1, post0]) + Search.advanced_order(:chars) do |posts| + posts.reorder("(SELECT LENGTH(raw) FROM posts WHERE posts.topic_id = subquery.topic_id) DESC") + end + expect(Search.new("advanced order:chars").execute.posts).to eq([post0, post1]) + end + end end