From f7940b1d20c3a176f8d6178dad8bf8aebbaf850c Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Mon, 28 Sep 2020 21:34:16 +0530 Subject: [PATCH] FEATURE: advanced search option for max posts count (#10761) This commit adds an option to search for max posts count and updates the UI for posts count search to show a min/max range in single line. --- .../app/components/search-advanced-options.js | 35 +++++++++++++++ .../components/search-advanced-options.hbs | 43 +++++++++++++------ .../stylesheets/common/base/search.scss | 8 ++-- config/locales/client.en.yml | 6 ++- lib/search.rb | 8 +++- spec/components/search_spec.rb | 1 + .../acceptance/search-full-test.js | 20 +++++++++ 7 files changed, 101 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/search-advanced-options.js b/app/assets/javascripts/discourse/app/components/search-advanced-options.js index b2ddfc879d1..b4c40f833a9 100644 --- a/app/assets/javascripts/discourse/app/components/search-advanced-options.js +++ b/app/assets/javascripts/discourse/app/components/search-advanced-options.js @@ -12,6 +12,7 @@ const REGEXP_TAGS_PREFIX = /^(tags?:|#(?=[a-z0-9\-]+::tag))/gi; const REGEXP_IN_PREFIX = /^(in|with):/gi; const REGEXP_STATUS_PREFIX = /^status:/gi; const REGEXP_MIN_POSTS_PREFIX = /^min_posts:/gi; +const REGEXP_MAX_POSTS_PREFIX = /^max_posts:/gi; const REGEXP_MIN_VIEWS_PREFIX = /^min_views:/gi; const REGEXP_MAX_VIEWS_PREFIX = /^max_views:/gi; const REGEXP_POST_TIME_PREFIX = /^(before|after):/gi; @@ -95,6 +96,7 @@ export default Component.extend({ }, status: null, min_posts: null, + max_posts: null, min_views: null, max_views: null, time: { @@ -166,6 +168,11 @@ export default Component.extend({ REGEXP_MIN_POSTS_PREFIX ); + this.setSearchedTermValue( + "searchedTerms.max_posts", + REGEXP_MAX_POSTS_PREFIX + ); + this.setSearchedTermValue( "searchedTerms.min_views", REGEXP_MIN_VIEWS_PREFIX @@ -359,6 +366,12 @@ export default Component.extend({ this._updateSearchTermForMinPostCount(); }, + @action + onChangeSearchTermMaxPostCount(value) { + this.set("searchedTerms.max_posts", value.length ? value : null); + this._updateSearchTermForMaxPostCount(); + }, + @action onChangeSearchTermMinViews(value) { this.set("searchedTerms.min_views", value.length ? value : null); @@ -653,6 +666,28 @@ export default Component.extend({ } }, + _updateSearchTermForMaxPostCount() { + const match = this.filterBlocks(REGEXP_MAX_POSTS_PREFIX); + const postsCountFilter = this.get("searchedTerms.max_posts"); + let searchTerm = this.searchTerm || ""; + + if (postsCountFilter) { + if (match.length !== 0) { + searchTerm = searchTerm.replace( + match[0], + `max_posts:${postsCountFilter}` + ); + } else { + searchTerm += ` max_posts:${postsCountFilter}`; + } + + this._updateSearchTerm(searchTerm); + } else if (match.length !== 0) { + searchTerm = searchTerm.replace(match[0], ""); + this._updateSearchTerm(searchTerm); + } + }, + _updateSearchTermForMinViews() { const match = this.filterBlocks(REGEXP_MIN_VIEWS_PREFIX); const viewsCountFilter = this.get("searchedTerms.min_views"); diff --git a/app/assets/javascripts/discourse/app/templates/components/search-advanced-options.hbs b/app/assets/javascripts/discourse/app/templates/components/search-advanced-options.hbs index 82a474440d3..c717b704d2a 100644 --- a/app/assets/javascripts/discourse/app/templates/components/search-advanced-options.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/search-advanced-options.hbs @@ -148,22 +148,39 @@ }} -
+ +
-
- {{input - type="number" - value=(readonly searchedTerms.min_posts) - class="input-small" - id="search-min-post-count" - input=(action "onChangeSearchTermMinPostCount" value="target.value") - }} +
+
+ {{input + type="number" + value=(readonly searchedTerms.min_posts) + class="input-small" + id="search-min-post-count" + input=(action "onChangeSearchTermMinPostCount" value="target.value") + placeholder=(i18n "search.advanced.post.min.placeholder") + }} +
+
+ +
+
+ {{input + type="number" + value=(readonly searchedTerms.max_posts) + class="input-small" + id="search-max-post-count" + input=(action "onChangeSearchTermMaxPostCount" value="target.value") + placeholder=(i18n "search.advanced.post.max.placeholder") + }} +
-
+
-
+
{{input type="number" @@ -175,8 +192,8 @@ }}
- -
+ +
{{input type="number" diff --git a/app/assets/stylesheets/common/base/search.scss b/app/assets/stylesheets/common/base/search.scss index 729c6179f11..2cbab66792a 100644 --- a/app/assets/stylesheets/common/base/search.scss +++ b/app/assets/stylesheets/common/base/search.scss @@ -164,13 +164,13 @@ } } - .views { - .views-count { + .count-group { + .count { margin-bottom: 15px; width: 45%; } - .views-count-dash { - padding-left: 10px; + .count-dash { + padding-left: 6px; vertical-align: middle; } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 595f456d2ed..2396307e2bb 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2119,7 +2119,11 @@ en: single_user: contain a single user post: count: - label: Minimum Posts + label: Posts + min: + placeholder: minimum + max: + placeholder: maximum time: label: Posted before: before diff --git a/lib/search.rb b/lib/search.rb index 9284ac95c59..1ea88a44ce9 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -369,12 +369,16 @@ class Search posts.where("topics.posts_count = ?", match.to_i) end + advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match| + posts.where("topics.posts_count >= ?", match.to_i) + end + advanced_filter(/^min_posts:(\d+)$/i) do |posts, match| posts.where("topics.posts_count >= ?", match.to_i) end - advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match| - posts.where("topics.posts_count >= ?", match.to_i) + advanced_filter(/^max_posts:(\d+)$/i) do |posts, match| + posts.where("topics.posts_count <= ?", match.to_i) end advanced_filter(/^in:first|^f$/i) do |posts| diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 113276085fd..5f05a2b28c5 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -1279,6 +1279,7 @@ describe Search do 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) expect(Search.execute('test min_posts:1').posts.length).to eq(1) + expect(Search.execute('test max_posts:2').posts.length).to eq(1) topic.update(closed: true) second_topic.update(category: public_category) diff --git a/test/javascripts/acceptance/search-full-test.js b/test/javascripts/acceptance/search-full-test.js index 9c2f2ed43ba..964296d6a15 100644 --- a/test/javascripts/acceptance/search-full-test.js +++ b/test/javascripts/acceptance/search-full-test.js @@ -396,6 +396,26 @@ QUnit.test( } ); +QUnit.test( + "update max post count through advanced search ui", + async (assert) => { + await visit("/search"); + await fillIn(".search-query", "none"); + await fillIn("#search-max-post-count", "5"); + + assert.equal( + find(".search-advanced-options #search-max-post-count").val(), + "5", + 'has "5" populated' + ); + assert.equal( + find(".search-query").val(), + "none max_posts:5", + 'has updated search term to "none max_posts:5"' + ); + } +); + QUnit.test("validate advanced search when initially empty", async (assert) => { await visit("/search?expanded=true"); await click(".search-advanced-options .in-likes");