Skip to content

Commit

Permalink
feat: Add dashboard search filter support (#112)
Browse files Browse the repository at this point in the history
* feat: Add search filter support dashboard

* fix mypy

* keep table filter endpoint the same for now

* fix issue

* pass index by default

* remove index and keep page_index

* update tag name

* update per feedback

* update version
  • Loading branch information
feng-tao authored Jun 16, 2020
1 parent a56bea6 commit 17c6739
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 280 deletions.
7 changes: 5 additions & 2 deletions search_service/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Dict, Any # noqa: F401
from flasgger import Swagger

from search_service.api.dashboard import SearchDashboardAPI
from search_service.api.dashboard import SearchDashboardAPI, SearchDashboardFilterAPI
from search_service.api.table import SearchTableAPI, SearchTableFilterAPI
from search_service.api.user import SearchUserAPI
from search_service.api.document import DocumentUserAPI, DocumentTableAPI, DocumentTablesAPI, DocumentUsersAPI
Expand Down Expand Up @@ -81,17 +81,20 @@ def create_app(*, config_module_class: str) -> Flask:
api_bp.add_url_rule('/healthcheck', 'healthcheck', healthcheck)
api = Api(api_bp)
# Table Search API
# TODO: Rename endpoint to be more generic and accept a resource type so that logic can be re-used

api.add_resource(SearchTableFilterAPI, '/search_table')
# TODO: Rename endpoint to be more generic and accept a resource type so that logic can be re-used
api.add_resource(SearchTableAPI, '/search')

# User Search API
api.add_resource(SearchUserAPI, '/search_user')

# Dashboard Search API
api.add_resource(SearchDashboardAPI, '/search_dashboard')
api.add_resource(SearchDashboardFilterAPI, '/search_dashboard_filter')

# DocumentAPI
# todo: needs to update to handle dashboard/user or other entities use cases.
api.add_resource(DocumentTablesAPI, '/document_table')
api.add_resource(DocumentTableAPI, '/document_table/<document_id>')

Expand Down
59 changes: 59 additions & 0 deletions search_service/api/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from http import HTTPStatus
from typing import Any, Dict, Iterable # noqa: F401

from flask_restful import Resource, reqparse
from marshmallow_annotations.ext.attrs import AttrsSchema

from search_service.proxy import get_proxy_client


class BaseFilterAPI(Resource):
"""
Base Filter API for search filtering
This API should be generic enough to support every search filter use case.
"""

def __init__(self, *, schema: AttrsSchema, index: str) -> None:
self.proxy = get_proxy_client()
self.schema = schema
self.index = index
self.parser = reqparse.RequestParser(bundle_errors=True)

self.parser.add_argument('page_index', required=False, default=0, type=int)
self.parser.add_argument('query_term', required=False, type=str)
self.parser.add_argument('search_request', type=dict)

super(BaseFilterAPI, self).__init__()

def post(self) -> Iterable[Any]:
"""
Fetch search results based on the page_index, query_term, and
search_request dictionary posted in the request JSON.
:return: json payload of schema.
doesn't match any tables
"""
args = self.parser.parse_args(strict=True)
page_index = args.get('page_index') # type: int

search_request = args.get('search_request') # type: Dict
if search_request is None:
msg = 'The search request payload is not available in the request'
return {'message': msg}, HTTPStatus.BAD_REQUEST

query_term = args.get('query_term') # type: str
if ':' in query_term:
msg = 'The query term contains an invalid character'
return {'message': msg}, HTTPStatus.BAD_REQUEST

try:
results = self.proxy.fetch_search_results_with_filter(
search_request=search_request,
query_term=query_term,
page_index=page_index,
index=self.index
)

return self.schema().dump(results).data, HTTPStatus.OK
except RuntimeError as e:
raise e
18 changes: 18 additions & 0 deletions search_service/api/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flasgger import swag_from
from flask_restful import Resource, reqparse # noqa: I201

from search_service.api.base import BaseFilterAPI
from search_service.exception import NotFoundException
from search_service.models.dashboard import SearchDashboardResultSchema
from search_service.proxy import get_proxy_client
Expand Down Expand Up @@ -57,3 +58,20 @@ def get(self) -> Iterable[Any]:
err_msg = 'Exception encountered while processing search request'
LOGGING.exception(err_msg)
return {'message': err_msg}, HTTPStatus.INTERNAL_SERVER_ERROR


class SearchDashboardFilterAPI(BaseFilterAPI):
"""
Search Filter for Dashboard
"""
def __init__(self) -> None:
super().__init__(schema=SearchDashboardResultSchema,
index=DASHBOARD_INDEX)

@swag_from('swagger_doc/dashboard/search_dashboard_filter.yml')
def post(self) -> Iterable[Any]:
try:
return super().post()
except RuntimeError:
err_msg = 'Exception encountered while processing search request'
return {'message': err_msg}, HTTPStatus.INTERNAL_SERVER_ERROR
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Dashboard search
This is used by the frontend API to search dashboard information.
---
tags:
- 'search_dashboard_filter'
paths:
/search_dashboard:
post:
summary: This is used by the frontend API to search dashboard information.
requestBody:
description: The json data passed from the frontend API to execute a search.
required: true
content:
application/json:
schema:
type: object
properties:
index:
type: string
page_index:
type: integer
query_term:
type: string
search_request:
type: object
responses:
200:
description: dashboard result information with query string
content:
application/json:
schema:
$ref: '#/components/schemas/SearchDashboardResults'
500:
description: Exception encountered while searching
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
7 changes: 3 additions & 4 deletions search_service/api/swagger_doc/table/search_table_filter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Table search
This is used by the frontend API to search table information.
---
tags:
- 'search'
- 'search_table'
paths:
/search_table:
post:
Expand All @@ -15,11 +15,10 @@ paths:
schema:
type: object
properties:
index:
type: string
page_index:
type: integer
schema:
type: integer
default: 0
query_term:
type: string
search_request:
Expand Down
50 changes: 7 additions & 43 deletions search_service/api/table.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from http import HTTPStatus
from typing import Any, Dict, Iterable # noqa: F401
from typing import Any, Iterable # noqa: F401

from flask_restful import Resource, reqparse
from flasgger import swag_from

from search_service.api.base import BaseFilterAPI
from search_service.models.table import SearchTableResultSchema
from search_service.proxy import get_proxy_client

Expand Down Expand Up @@ -53,55 +54,18 @@ def get(self) -> Iterable[Any]:
return {'message': err_msg}, HTTPStatus.INTERNAL_SERVER_ERROR


class SearchTableFilterAPI(Resource):
class SearchTableFilterAPI(BaseFilterAPI):
"""
Search Table API using search filtering
This API should be generic enough to support every search filter use case.
TODO: Deprecate the SearchTableFieldAPI for this more flexible API
Search Filter for table
"""

def __init__(self) -> None:
self.proxy = get_proxy_client()

self.parser = reqparse.RequestParser(bundle_errors=True)

self.parser.add_argument('index', required=False, default=TABLE_INDEX, type=str)
self.parser.add_argument('page_index', required=False, default=0, type=int)
self.parser.add_argument('query_term', required=False, type=str)
self.parser.add_argument('search_request', type=dict)

super(SearchTableFilterAPI, self).__init__()
super().__init__(schema=SearchTableResultSchema,
index=TABLE_INDEX)

@swag_from('swagger_doc/table/search_table_filter.yml')
def post(self) -> Iterable[Any]:
"""
Fetch search results based on the page_index, query_term, and
search_request dictionary posted in the request JSON.
:return: list of table results. List can be empty if query
doesn't match any tables
"""
args = self.parser.parse_args(strict=True)
page_index = args.get('page_index') # type: int

search_request = args.get('search_request') # type: Dict
if search_request is None:
msg = 'The search request payload is not available in the request'
return {'message': msg}, HTTPStatus.BAD_REQUEST

query_term = args.get('query_term') # type: str
if ':' in query_term:
msg = 'The query term contains an invalid character'
return {'message': msg}, HTTPStatus.BAD_REQUEST

try:
results = self.proxy.fetch_table_search_results_with_filter(
search_request=search_request,
query_term=query_term,
page_index=page_index,
index=args['index']
)
return SearchTableResultSchema().dump(results).data, HTTPStatus.OK
return super().post()
except RuntimeError:
err_msg = 'Exception encountered while processing search request'
return {'message': err_msg}, HTTPStatus.INTERNAL_SERVER_ERROR
10 changes: 5 additions & 5 deletions search_service/proxy/atlas.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ def fetch_table_search_results(self, *,

return SearchTableResult(total_results=approx_count, results=tables)

def fetch_table_search_results_with_filter(self, *,
query_term: str,
search_request: dict,
page_index: int = 0,
index: str = '') -> SearchTableResult:
def fetch_search_results_with_filter(self, *,
query_term: str,
search_request: dict,
page_index: int = 0,
index: str = '') -> SearchTableResult:
"""
Conduct an 'Advanced Search' to narrow down search results with a use of filters.
Expand Down
13 changes: 7 additions & 6 deletions search_service/proxy/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABCMeta, abstractmethod
from typing import Any, Dict, List
from typing import Any, Dict, List, Union

from search_service.models.dashboard import SearchDashboardResult
from search_service.models.table import SearchTableResult
Expand Down Expand Up @@ -45,11 +45,12 @@ def delete_document(self, *,
pass

@abstractmethod
def fetch_table_search_results_with_filter(self, *,
query_term: str,
search_request: dict,
page_index: int = 0,
index: str = '') -> SearchTableResult:
def fetch_search_results_with_filter(self, *,
query_term: str,
search_request: dict,
page_index: int = 0,
index: str = '') -> Union[SearchTableResult,
SearchDashboardResult]:
pass

@abstractmethod
Expand Down
Loading

0 comments on commit 17c6739

Please sign in to comment.