FEATURE: advanced search option for max posts count (#10761)

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.

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 b2ddfc8..b4c40f8 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: {
@@ -167,6 +169,11 @@ export default Component.extend({
     );
 
     this.setSearchedTermValue(
+      "searchedTerms.max_posts",
+      REGEXP_MAX_POSTS_PREFIX
+    );
+
+    this.setSearchedTermValue(
       "searchedTerms.min_views",
       REGEXP_MIN_VIEWS_PREFIX
     );
@@ -360,6 +367,12 @@ export default Component.extend({
   },
 
   @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);
     this._updateSearchTermForMinViews();
@@ -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 82a4744..c717b70 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 @@
       }}
     </div>
   </div>
-  <div class="control-group pull-left">
+
+  <div class="count-group control-group pull-left">
     <label class="control-label" for="search-min-post-count">{{i18n "search.advanced.post.count.label"}}</label>
-    <div class="controls">
-      {{input
-        type="number"
-        value=(readonly searchedTerms.min_posts)
-        class="input-small"
-        id="search-min-post-count"
-        input=(action "onChangeSearchTermMinPostCount" value="target.value")
-      }}
+    <div class="count pull-left">
+      <div class="controls">
+        {{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")
+        }}
+      </div>
+    </div>
+    <span class="count-dash">&mdash;</span>
+    <div class="count pull-right">
+      <div class="controls">
+        {{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")
+        }}
+      </div>
     </div>
   </div>
 
-  <div class="views control-group pull-left">
+  <div class="count-group control-group pull-left">
     <label class="control-label">{{i18n "search.advanced.views.label"}}</label>
-    <div class="views-count pull-left">
+    <div class="count pull-left">
       <div class="controls">
         {{input
           type="number"
@@ -175,8 +192,8 @@
         }}
       </div>
     </div>
-    <span class="views-count-dash">–</span>
-    <div class="views-count pull-right">
+    <span class="count-dash">&mdash;</span>
+    <div class="count pull-right">
       <div class="controls">
         {{input
           type="number"
diff --git a/app/assets/stylesheets/common/base/search.scss b/app/assets/stylesheets/common/base/search.scss
index 729c617..2cbab66 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 595f456..2396307 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 9284ac9..1ea88a4 100644
--- a/lib/search.rb
+++ b/lib/search.rb
@@ -369,14 +369,18 @@ class Search
     posts.where("topics.posts_count = ?", match.to_i)
   end
 
-  advanced_filter(/^min_posts:(\d+)$/i) do |posts, match|
+  advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match|
     posts.where("topics.posts_count >= ?", match.to_i)
   end
 
-  advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match|
+  advanced_filter(/^min_posts:(\d+)$/i) do |posts, match|
     posts.where("topics.posts_count >= ?", match.to_i)
   end
 
+  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|
     posts.where("posts.post_number = 1")
   end
diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb
index 1132760..5f05a2b 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 9c2f2ed..964296d 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");

[... diff too long, it was truncated ...]

GitHub sha: f7940b1d

This commit appears in #10761 which was approved by eviltrout. It was merged by techAPJ.