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.
This commit is contained in:
Arpit Jalan 2020-09-28 21:34:16 +05:30 committed by GitHub
parent 4d1b18f4c6
commit f7940b1d20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 20 deletions

View File

@ -12,6 +12,7 @@ const REGEXP_TAGS_PREFIX = /^(tags?:|#(?=[a-z0-9\-]+::tag))/gi;
const REGEXP_IN_PREFIX = /^(in|with):/gi; const REGEXP_IN_PREFIX = /^(in|with):/gi;
const REGEXP_STATUS_PREFIX = /^status:/gi; const REGEXP_STATUS_PREFIX = /^status:/gi;
const REGEXP_MIN_POSTS_PREFIX = /^min_posts:/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_MIN_VIEWS_PREFIX = /^min_views:/gi;
const REGEXP_MAX_VIEWS_PREFIX = /^max_views:/gi; const REGEXP_MAX_VIEWS_PREFIX = /^max_views:/gi;
const REGEXP_POST_TIME_PREFIX = /^(before|after):/gi; const REGEXP_POST_TIME_PREFIX = /^(before|after):/gi;
@ -95,6 +96,7 @@ export default Component.extend({
}, },
status: null, status: null,
min_posts: null, min_posts: null,
max_posts: null,
min_views: null, min_views: null,
max_views: null, max_views: null,
time: { time: {
@ -166,6 +168,11 @@ export default Component.extend({
REGEXP_MIN_POSTS_PREFIX REGEXP_MIN_POSTS_PREFIX
); );
this.setSearchedTermValue(
"searchedTerms.max_posts",
REGEXP_MAX_POSTS_PREFIX
);
this.setSearchedTermValue( this.setSearchedTermValue(
"searchedTerms.min_views", "searchedTerms.min_views",
REGEXP_MIN_VIEWS_PREFIX REGEXP_MIN_VIEWS_PREFIX
@ -359,6 +366,12 @@ export default Component.extend({
this._updateSearchTermForMinPostCount(); this._updateSearchTermForMinPostCount();
}, },
@action
onChangeSearchTermMaxPostCount(value) {
this.set("searchedTerms.max_posts", value.length ? value : null);
this._updateSearchTermForMaxPostCount();
},
@action @action
onChangeSearchTermMinViews(value) { onChangeSearchTermMinViews(value) {
this.set("searchedTerms.min_views", value.length ? value : null); 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() { _updateSearchTermForMinViews() {
const match = this.filterBlocks(REGEXP_MIN_VIEWS_PREFIX); const match = this.filterBlocks(REGEXP_MIN_VIEWS_PREFIX);
const viewsCountFilter = this.get("searchedTerms.min_views"); const viewsCountFilter = this.get("searchedTerms.min_views");

View File

@ -148,22 +148,39 @@
}} }}
</div> </div>
</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> <label class="control-label" for="search-min-post-count">{{i18n "search.advanced.post.count.label"}}</label>
<div class="controls"> <div class="count pull-left">
{{input <div class="controls">
type="number" {{input
value=(readonly searchedTerms.min_posts) type="number"
class="input-small" value=(readonly searchedTerms.min_posts)
id="search-min-post-count" class="input-small"
input=(action "onChangeSearchTermMinPostCount" value="target.value") 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> </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> <label class="control-label">{{i18n "search.advanced.views.label"}}</label>
<div class="views-count pull-left"> <div class="count pull-left">
<div class="controls"> <div class="controls">
{{input {{input
type="number" type="number"
@ -175,8 +192,8 @@
}} }}
</div> </div>
</div> </div>
<span class="views-count-dash"></span> <span class="count-dash">&mdash;</span>
<div class="views-count pull-right"> <div class="count pull-right">
<div class="controls"> <div class="controls">
{{input {{input
type="number" type="number"

View File

@ -164,13 +164,13 @@
} }
} }
.views { .count-group {
.views-count { .count {
margin-bottom: 15px; margin-bottom: 15px;
width: 45%; width: 45%;
} }
.views-count-dash { .count-dash {
padding-left: 10px; padding-left: 6px;
vertical-align: middle; vertical-align: middle;
} }
} }

View File

@ -2119,7 +2119,11 @@ en:
single_user: contain a single user single_user: contain a single user
post: post:
count: count:
label: Minimum Posts label: Posts
min:
placeholder: minimum
max:
placeholder: maximum
time: time:
label: Posted label: Posted
before: before before: before

View File

@ -369,12 +369,16 @@ class Search
posts.where("topics.posts_count = ?", match.to_i) posts.where("topics.posts_count = ?", match.to_i)
end 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| advanced_filter(/^min_posts:(\d+)$/i) do |posts, match|
posts.where("topics.posts_count >= ?", match.to_i) posts.where("topics.posts_count >= ?", match.to_i)
end end
advanced_filter(/^min_post_count:(\d+)$/i) do |posts, match| advanced_filter(/^max_posts:(\d+)$/i) do |posts, match|
posts.where("topics.posts_count >= ?", match.to_i) posts.where("topics.posts_count <= ?", match.to_i)
end end
advanced_filter(/^in:first|^f$/i) do |posts| advanced_filter(/^in:first|^f$/i) do |posts|

View File

@ -1279,6 +1279,7 @@ describe Search do
expect(Search.execute('test posts_count:1').posts.length).to eq(1) 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_post_count:1').posts.length).to eq(1)
expect(Search.execute('test min_posts: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) topic.update(closed: true)
second_topic.update(category: public_category) second_topic.update(category: public_category)

View File

@ -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) => { QUnit.test("validate advanced search when initially empty", async (assert) => {
await visit("/search?expanded=true"); await visit("/search?expanded=true");
await click(".search-advanced-options .in-likes"); await click(".search-advanced-options .in-likes");