Skip to content

Commit

Permalink
Some code cleanup; Bump version for test
Browse files Browse the repository at this point in the history
  • Loading branch information
ttannis committed Feb 12, 2020
1 parent e67a465 commit d602d42
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 81 deletions.
2 changes: 1 addition & 1 deletion search_service/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ def create_app(*, config_module_class: str) -> Flask:
api_bp.add_url_rule('/healthcheck', 'healthcheck', healthcheck)
api = Api(api_bp)
# Table Search API
api.add_resource(SearchTableFilterAPI, '/search_table')
api.add_resource(SearchTableAPI, '/search')
api.add_resource(SearchTableFieldAPI,
'/search/field/<field_name>/field_val/<field_value>')
api.add_resource(SearchTableFilterAPI, '/search_table')

# User Search API
api.add_resource(SearchUserAPI, '/search_user')
Expand Down
63 changes: 34 additions & 29 deletions search_service/api/swagger_doc/table/search_table_filter.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
Table search
This is used by the frontend API to search table information.
'TODO(ttannis): Update this file'
---
tags:
- 'search'
parameters:
- name: page_index
in: query
type: integer
schema:
type: integer
default: 0
required: false
- name: index
in: query
type: string
schema:
type: string
default: 'table_search_index'
required: false
responses:
200:
description: table result information with query string
content:
application/json:
schema:
$ref: '#/components/schemas/SearchTableResults'
500:
description: Exception encountered while searching
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
paths:
/search_table:
post:
summary: This is used by the frontend API to search table information.
requestBody:
description: The json data passed from the frontend API to execute a search.
required: true
content:
application/json:
schema:
type: object
properties:
page_index:
type: integer
schema:
type: integer
default: 0
query_term:
type: string
search_request:
type: object
responses:
200:
description: table result information with query string
content:
application/json:
schema:
$ref: '#/components/schemas/SearchTableResults'
500:
description: Exception encountered while searching
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
4 changes: 2 additions & 2 deletions search_service/api/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ def post(self) -> Iterable[Any]:
doesn't match any tables
"""
args = self.parser.parse_args(strict=True)
page_index = args.get('page_index')
page_index = args.get('page_index') # type: int

search_request = args.get('search_request')
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
Expand Down
65 changes: 34 additions & 31 deletions search_service/proxy/elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

LOGGING = logging.getLogger(__name__)

# mapping to translate request
# mapping to translate request for table resources
TABLE_MAPPING = {
'tag': 'tags',
'schema': 'schema_name.raw',
Expand Down Expand Up @@ -377,7 +377,33 @@ def fetch_table_search_results(self, *,
model=Table)

@staticmethod
def convert_query_json_to_query_dsl(search_request: dict,
def parse_filters(filter_list: Dict) -> str:
query_list = [] # type: List[str]
for category, item_list in filter_list.items():
mapped_category = TABLE_MAPPING.get(category)
if mapped_category is None:
LOGGING.warn(f'Unsupported filter category: {category} passed in list of filters')
else:
query_list.append(mapped_category + ':' + '(' + ' OR '.join(item_list) + ')')

if len(query_list) == 0:
return ''

return ' AND '.join(query_list)

@staticmethod
def parse_query_term(query_term: str) -> str:
# TODO: Might be some issue with using wildcard & underscore
# https://discuss.elastic.co/t/wildcard-search-with-underscore-is-giving-no-result/114010/8
return f'(name:(*{query_term}*) OR name:({query_term}) ' \
f'OR schema_name:(*{query_term}*) OR schema_name:({query_term}) ' \
f'OR description:(*{query_term}*) OR description:({query_term}) ' \
f'OR column_names:(*{query_term}*) OR column_names:({query_term}) ' \
f'OR column_descriptions:(*{query_term}*) OR column_descriptions:({query_term}))'

@classmethod
def convert_query_json_to_query_dsl(self, *,
search_request: dict,
query_term: str) -> str:
"""
Convert the generic query json to query DSL
Expand Down Expand Up @@ -407,39 +433,15 @@ def convert_query_json_to_query_dsl(search_request: dict,
:param search_request:
:return: The search engine query DSL
"""
filter_list = search_request.get('filters')
filter_list = search_request.get('filters') # type: Dict[str, List[str]]
add_query = ''
query_dsl = ''

if filter_list:
query_dsl = ' AND '.join([(category + ':' + '(' +
' OR '.join(item_list) + ')')
for category, item_list in filter_list.items()])

# exact match
for k, v in TABLE_MAPPING.items():
query_dsl = query_dsl.replace(k, v)
query_dsl = self.parse_filters(filter_list)

if query_term:
if '.' in query_term:
# query_term is schema.table
schema, table = query_term.split('.')
add_query = '(name:(*{table_name}*) ' \
'AND schema_name:(*{schema}*))'.format(table_name=table,
schema=schema)
else:
# TODO: Might be some issue with using wildcard & underscore
# https://discuss.elastic.co/t/wildcard-search-with-underscore-is-giving-no-result/114010/8
add_query = '(name:(*{name}*) ' \
'OR name:({name}) ' \
'OR schema_name:(*{name}*) ' \
'OR schema_name:({name}) ' \
'OR description:(*{name}*) ' \
'OR description:({name}) ' \
'OR column_names:(*{name}*) ' \
'OR column_names:({name}) ' \
'OR column_descriptions:(*{name}*) ' \
'OR column_descriptions:({name}))'.format(name=query_term)
add_query = self.parse_query_term(query_term)

if not query_dsl and not add_query:
raise Exception('Unable to convert parameters to valid query dsl')
Expand Down Expand Up @@ -468,13 +470,14 @@ def fetch_table_search_results_with_filter(self, *,
:return: SearchResult Object
"""
current_index = index if index else \
current_app.config.get(config.ELASTICSEARCH_INDEX_KEY, DEFAULT_ES_INDEX)
current_app.config.get(config.ELASTICSEARCH_INDEX_KEY, DEFAULT_ES_INDEX) # type: str
if not search_request:
# return empty result for blank query term
return SearchResult(total_results=0, results=[])

try:
query_string = self.convert_query_json_to_query_dsl(search_request, query_term)
query_string = self.convert_query_json_to_query_dsl(search_request=search_request,
query_term=query_term) # type: str
except Exception as e:
LOGGING.exception(e)
# return nothing if any exception is thrown under the hood
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from setuptools import setup, find_packages

__version__ = '1.5.0rc0'
__version__ = '1.5.0rc1'

requirements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'requirements.txt')
with open(requirements_path) as requirements_file:
Expand Down
30 changes: 13 additions & 17 deletions tests/unit/proxy/test_elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ def test_search_table_filter_return_no_results_if_dsl_conversion_error(self) ->
self.assertEquals(resp.total_results, 0)
self.assertEquals(resp.results, [])

# TODO: Update test to mock results of static methods and not hardcode expected_result
def test_convert_query_json_to_query_dsl_term_and_filters(self) -> None:
term = 'test'
search_request = {
Expand All @@ -385,17 +386,19 @@ def test_convert_query_json_to_query_dsl_term_and_filters(self) -> None:
}
}
expected_result = "database.raw:(hive OR bigquery) " \
"AND schema_name.raw:(test-schema_name.raw1 OR test-schema_name.raw2) " \
"AND schema_name.raw:(test-schema1 OR test-schema2) " \
"AND name.raw:(*amundsen*) " \
"AND column_names.raw:(*ds*) " \
"AND tags:(test-tags) " \
"AND tags:(test-tag) " \
"AND (name:(*test*) OR name:(test) OR schema_name:(*test*) OR " \
"schema_name:(test) OR description:(*test*) OR description:(test) OR " \
"column_names:(*test*) OR column_names:(test) OR " \
"column_descriptions:(*test*) OR column_descriptions:(test))"
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request, term)
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request=search_request,
query_term=term)
self.assertEquals(ret_result, expected_result)

# TODO: Update test to mock results of static methods and not hardcode expected_result
def test_convert_query_json_to_query_dsl_no_term(self) -> None:
term = ''
search_request = {
Expand All @@ -409,13 +412,15 @@ def test_convert_query_json_to_query_dsl_no_term(self) -> None:
}
}
expected_result = "database.raw:(hive OR bigquery) " \
"AND schema_name.raw:(test-schema_name.raw1 OR test-schema_name.raw2) " \
"AND schema_name.raw:(test-schema1 OR test-schema2) " \
"AND name.raw:(*amundsen*) " \
"AND column_names.raw:(*ds*) " \
"AND tags:(test-tags)"
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request, term)
"AND tags:(test-tag)"
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request=search_request,
query_term=term)
self.assertEquals(ret_result, expected_result)

# TODO: Update test to mock results of static methods and not hardcode expected_result
def test_convert_query_json_to_query_dsl_no_filters(self) -> None:
term = 'test'
search_request = {
Expand All @@ -426,17 +431,8 @@ def test_convert_query_json_to_query_dsl_no_filters(self) -> None:
"schema_name:(test) OR description:(*test*) OR description:(test) OR " \
"column_names:(*test*) OR column_names:(test) OR " \
"column_descriptions:(*test*) OR column_descriptions:(test))"
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request, term)
self.assertEquals(ret_result, expected_result)

def test_convert_query_json_to_query_dsl_schematable_logic(self) -> None:
term = 'schema.table'
search_request = {
'type': 'AND',
'filters': {}
}
expected_result = "(name:(*table*) AND schema_name:(*schema*))"
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request, term)
ret_result = self.es_proxy.convert_query_json_to_query_dsl(search_request=search_request,
query_term=term)
self.assertEquals(ret_result, expected_result)

def test_convert_query_json_to_query_dsl_raise_exception_no_term_or_filters(self) -> None:
Expand Down

0 comments on commit d602d42

Please sign in to comment.