Commit 068b4127 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'search-title' into 'master'

Add 'in' filter that modifies scope of 'search' filter  to issues and merge requests API

See merge request gitlab-org/gitlab-ce!24350
parents a34d6abd 40198f81
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
# assignee_id: integer or 'None' or 'Any' # assignee_id: integer or 'None' or 'Any'
# assignee_username: string # assignee_username: string
# search: string # search: string
# in: 'title', 'description', or a string joining them with comma
# label_name: string # label_name: string
# sort: string # sort: string
# non_archived: boolean # non_archived: boolean
...@@ -56,6 +57,7 @@ class IssuableFinder ...@@ -56,6 +57,7 @@ class IssuableFinder
milestone_title milestone_title
my_reaction_emoji my_reaction_emoji
search search
in
] ]
end end
...@@ -408,7 +410,7 @@ class IssuableFinder ...@@ -408,7 +410,7 @@ class IssuableFinder
items = klass.with(cte.to_arel).from(klass.table_name) items = klass.with(cte.to_arel).from(klass.table_name)
end end
items.full_search(search) items.full_search(search, matched_columns: params[:in])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
# milestone_title: string # milestone_title: string
# assignee_id: integer # assignee_id: integer
# search: string # search: string
# in: 'title', 'description', or a string joining them with comma
# label_name: string # label_name: string
# sort: string # sort: string
# my_reaction_emoji: string # my_reaction_emoji: string
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# author_id: integer # author_id: integer
# assignee_id: integer # assignee_id: integer
# search: string # search: string
# in: 'title', 'description', or a string joining them with comma
# label_name: string # label_name: string
# sort: string # sort: string
# non_archived: boolean # non_archived: boolean
......
...@@ -136,10 +136,18 @@ module Issuable ...@@ -136,10 +136,18 @@ module Issuable
# This method uses ILIKE on PostgreSQL and LIKE on MySQL. # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
# #
# query - The search query as a String # query - The search query as a String
# matched_columns - Modify the scope of the query. 'title', 'description' or joining them with a comma.
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def full_search(query) def full_search(query, matched_columns: 'title,description')
fuzzy_search(query, [:title, :description]) allowed_columns = [:title, :description]
matched_columns = matched_columns.to_s.split(',').map(&:to_sym)
matched_columns &= allowed_columns
# Matching title or description if the matched_columns did not contain any allowed columns.
matched_columns = [:title, :description] if matched_columns.empty?
fuzzy_search(query, matched_columns)
end end
def sort_by_attribute(method, excluded_labels: []) def sort_by_attribute(method, excluded_labels: [])
......
---
title: Add 'in' filter that modifies scope of 'search' filter to issues and merge requests API
merge_request: 24350
author: Hiroyuki Sato
type: added
...@@ -31,6 +31,7 @@ GET /issues?iids[]=42&iids[]=43 ...@@ -31,6 +31,7 @@ GET /issues?iids[]=42&iids[]=43
GET /issues?author_id=5 GET /issues?author_id=5
GET /issues?assignee_id=5 GET /issues?assignee_id=5
GET /issues?my_reaction_emoji=star GET /issues?my_reaction_emoji=star
GET /issues?search=foo&in=title
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -46,6 +47,7 @@ GET /issues?my_reaction_emoji=star ...@@ -46,6 +47,7 @@ GET /issues?my_reaction_emoji=star
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search issues against their `title` and `description` | | `search` | string | no | Search issues against their `title` and `description` |
| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description` |
| `created_after` | datetime | no | Return issues created on or after the given time | | `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time | | `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time |
......
...@@ -24,6 +24,7 @@ GET /merge_requests?labels=bug,reproduced ...@@ -24,6 +24,7 @@ GET /merge_requests?labels=bug,reproduced
GET /merge_requests?author_id=5 GET /merge_requests?author_id=5
GET /merge_requests?my_reaction_emoji=star GET /merge_requests?my_reaction_emoji=star
GET /merge_requests?scope=assigned_to_me GET /merge_requests?scope=assigned_to_me
GET /merge_requests?search=foo&in=title
``` ```
Parameters: Parameters:
...@@ -47,6 +48,7 @@ Parameters: ...@@ -47,6 +48,7 @@ Parameters:
| `source_branch` | string | no | Return merge requests with the given source branch | | `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch | | `target_branch` | string | no | Return merge requests with the given target branch |
| `search` | string | no | Search merge requests against their `title` and `description` | | `search` | string | no | Search merge requests against their `title` and `description` |
| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description` |
| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests | | `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests |
```json ```json
......
...@@ -43,7 +43,8 @@ module API ...@@ -43,7 +43,8 @@ module API
desc: 'Return issues sorted in `asc` or `desc` order.' desc: 'Return issues sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return issues for a specific milestone' optional :milestone, type: String, desc: 'Return issues for a specific milestone'
optional :iids, type: Array[Integer], desc: 'The IID array of issues' optional :iids, type: Array[Integer], desc: 'The IID array of issues'
optional :search, type: String, desc: 'Search issues for text present in the title or description' optional :search, type: String, desc: 'Search issues for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
optional :created_after, type: DateTime, desc: 'Return issues created after the specified time' optional :created_after, type: DateTime, desc: 'Return issues created after the specified time'
optional :created_before, type: DateTime, desc: 'Return issues created before the specified time' optional :created_before, type: DateTime, desc: 'Return issues created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time' optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time'
......
...@@ -109,7 +109,8 @@ module API ...@@ -109,7 +109,8 @@ module API
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
optional :source_branch, type: String, desc: 'Return merge requests with the given source branch' optional :source_branch, type: String, desc: 'Return merge requests with the given source branch'
optional :target_branch, type: String, desc: 'Return merge requests with the given target branch' optional :target_branch, type: String, desc: 'Return merge requests with the given target branch'
optional :search, type: String, desc: 'Search merge requests for text present in the title or description' optional :search, type: String, desc: 'Search merge requests for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title' optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
use :pagination use :pagination
end end
......
...@@ -314,6 +314,14 @@ describe IssuesFinder do ...@@ -314,6 +314,14 @@ describe IssuesFinder do
end end
end end
context 'filtering by issue term in title' do
let(:params) { { search: 'git', in: 'title' } }
it 'returns issues with title match for search term' do
expect(issues).to contain_exactly(issue1)
end
end
context 'filtering by issues iids' do context 'filtering by issues iids' do
let(:params) { { iids: issue3.iid } } let(:params) { { iids: issue3.iid } }
......
...@@ -139,6 +139,78 @@ describe Issuable do ...@@ -139,6 +139,78 @@ describe Issuable do
it 'returns issues with a matching description for a query shorter than 3 chars' do it 'returns issues with a matching description for a query shorter than 3 chars' do
expect(issuable_class.full_search(searchable_issue2.description.downcase)).to eq([searchable_issue2]) expect(issuable_class.full_search(searchable_issue2.description.downcase)).to eq([searchable_issue2])
end end
context 'when matching columns is "title"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'title'))
.to eq([searchable_issue])
end
it 'returns no issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: 'title'))
.to be_empty
end
end
context 'when matching columns is "description"' do
it 'returns no issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'description'))
.to be_empty
end
it 'returns issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: 'description'))
.to eq([searchable_issue])
end
end
context 'when matching columns is "title,description"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'title,description'))
.to eq([searchable_issue])
end
it 'returns issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: 'title,description'))
.to eq([searchable_issue])
end
end
context 'when matching columns is nil"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: nil))
.to eq([searchable_issue])
end
it 'returns issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: nil))
.to eq([searchable_issue])
end
end
context 'when matching columns is "invalid"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'invalid'))
.to eq([searchable_issue])
end
it 'returns issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: 'invalid'))
.to eq([searchable_issue])
end
end
context 'when matching columns is "title,invalid"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'title,invalid'))
.to eq([searchable_issue])
end
it 'returns no issues with a matching description' do
expect(issuable_class.full_search(searchable_issue.description, matched_columns: 'title,invalid'))
.to be_empty
end
end
end end
describe '.to_ability_name' do describe '.to_ability_name' do
......
...@@ -208,6 +208,18 @@ describe API::Issues do ...@@ -208,6 +208,18 @@ describe API::Issues do
expect_paginated_array_response(issue.id) expect_paginated_array_response(issue.id)
end end
it 'returns issues matching given search string for title and scoped in title' do
get api("/issues", user), params: { search: issue.title, in: 'title' }
expect_paginated_array_response(issue.id)
end
it 'returns an empty array if no issue matches given search string for title and scoped in description' do
get api("/issues", user), params: { search: issue.title, in: 'description' }
expect_paginated_array_response([])
end
it 'returns issues matching given search string for description' do it 'returns issues matching given search string for description' do
get api("/issues", user), params: { search: issue.description } get api("/issues", user), params: { search: issue.description }
......
...@@ -260,6 +260,18 @@ describe API::MergeRequests do ...@@ -260,6 +260,18 @@ describe API::MergeRequests do
expect_response_ordered_exactly(merge_request) expect_response_ordered_exactly(merge_request)
end end
it 'returns merge requests matching given search string for title and scoped in title' do
get api("/merge_requests", user), params: { search: merge_request.title, in: 'title' }
expect_response_ordered_exactly(merge_request)
end
it 'returns an empty array if no merge reques matches given search string for description and scoped in title' do
get api("/merge_requests", user), params: { search: merge_request.description, in: 'title' }
expect_response_contain_exactly
end
it 'returns merge requests for project matching given search string for description' do it 'returns merge requests for project matching given search string for description' do
get api("/merge_requests", user), params: { project_id: project.id, search: merge_request.description } get api("/merge_requests", user), params: { project_id: project.id, search: merge_request.description }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment