def test_nested_query(self):
     query_string = 'assignments.assignee_id:someuse'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'nested': {'path': 'assignments_nested', 'query': {'bool': {
             'must': [{'term': {'assignments_nested.assignee_id': 'someuse'}}]}}}}]}}
 def test_very_complicated_query(self):
     query_string = '((assignments.assignee_id:someuser OR assignments.is_completed:false) ' \
                    'OR (value:true AND another:false AND (some:true AND NOT field:true)) ' \
                    'AND NOT (complicated:true OR complicated:false)) ' \
                    'OR owner_id:someuser AND NOT completed:false'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'bool': {
             'must_not': [{'term': {'completed': 'false'}}],
             'should': [{'bool': {
                 'must_not': [{'bool': {
                     'should': [{'term': {'complicated': 'true'}},
                                {'term': {'complicated': 'false'}}],
                     'minimum_should_match': 1}}],
                 'should': [{'bool': {
                     'should': [{'nested': {'path': 'assignments_nested', 'query': {'bool': {
                         'should': [{'term': {'assignments_nested.assignee_id': 'someuser'}},
                                    {'term': {'assignments_nested.is_completed': 'false'}}],
                         'minimum_should_match': 1}}}}],
                     'minimum_should_match': 1}},
                     {'bool': {'must': [{'term': {'value': 'true'}},
                                        {'term': {'another': 'false'}}, {'bool': {
                             'must': [{'term': {'some': 'true'}}],
                             'must_not': [{'term': {'field': 'true'}}]}}]}}],
                 'minimum_should_match': 1}}, {'term': {'owner_id': 'someuser'}}],
             'minimum_should_match': 1}}]}}
 def test_simple_query_with_parentheses(self):
     query_string = '(assignments.assignee_id:"qweqweqwe")'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'nested': {'path': 'assignments_nested', 'query': {'bool': {
             'must': [{'term': {'assignments_nested.assignee_id': 'qweqweqwe'}}]}}}}]}}
 def test_nested_query_and(self):
     query_string = 'assignments.assignee_id:someuse AND assignments.is_completed:true'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {'must': [{'bool': {'must': [{'nested': {'query': {'bool': {
         'must': [{'term': {'assignments_nested.assignee_id': 'someuse'}},
                  {'term': {'assignments_nested.is_completed': 'true'}}]}},
                                                     'path': 'assignments_nested'}}]}}]}}
 def test_nested_query_with_quotes(self):
     query_string = 'assignments.assignee_id:"[email protected]."'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'nested': {'query': {'bool': {
             'must': [{'term': {'assignments_nested.assignee_id': '[email protected].'}}]}},
             'path': 'assignments_nested'}}]}}
 def test_do_not_apply_needless_parentheses(self):
     query_string = '((((assignments.assignee_id:someuse)) AND ((assignments.is_completed:true))))'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {'must': [{'bool': {'must': [{'nested': {'query': {'bool': {
         'must': [{'term': {'assignments_nested.assignee_id': 'someuse'}},
                  {'term': {'assignments_nested.is_completed': 'true'}}]}},
                                                     'path': 'assignments_nested'}}]}}]}}
 def test_nested_query_and_with_quotes(self):
     query_string = 'assignments.assignee_id:"someuser.some.last.name" ' \
              'AND assignments.assignor_id:"changed.user.name"'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {'must': [{'bool': {'must': [{'nested': {'path': 'assignments_nested', 'query': {
         'bool': {
             'must': [{'term': {'assignments_nested.assignee_id': 'someuser.some.last.name'}},
                      {'term': {'assignments_nested.assignor_id': 'changed.user.name'}}]}}}}]}}]}}
 def test_parse_statement_inside_param_value(self):
     query_string = '(assignments.assignee_id:"qweqweqwe")'
     params = {'es_q': query_string, 'project_id': '(2 OR 3)'}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'bool': {
             'must': [{'nested': {'path': 'assignments_nested',
                                  'query': {'bool': {'must': [{'term': {'assignments_nested.assignee_id': 'qweqweqwe'}}]}}}},
                      {'bool': {'should': [{'term': {'project_id': '2'}}, {'term': {'project_id': '3'}}],
                                'minimum_should_match': 1}}]}}]}}
 def test_range_query_with_brackets(self):
     query_string ='((schedules.end_date:[2016-10-11T03:00:00 TO 2016-10-18T02:59:59]))'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'nested': {'query': {'bool': {
             'must': [{
                 'range': {'schedules_nested.end_date': {'gte': '2016-10-11T03:00:00',
                                                         'lte': '2016-10-18T02:59:59'}}}]}},
             'path': 'schedules_nested'}}]}}
 def test_range_query_nested(self):
     query_string = 'schedules.end_date:[2016-10-11T03:00:00 TO 2016-10-18T02:59:59] AND schedules.obj_status:active'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'bool': {
             'must': [{'nested': {'query': {'bool': {
                 'must': [{'range': {'schedules_nested.end_date': {
                     'lte': '2016-10-18T02:59:59',
                     'gte': '2016-10-11T03:00:00'}}},
                     {'term': {'schedules_nested.obj_status': 'active'}}]}},
                 'path': 'schedules_nested'}}]}}]}}
 def test_nested_query_inside_query(self):
     query_string = '(assignments.assignee_id:someuser OR assignments.is_completed:false AND assignments.assignor_id:another) OR owner_id:someuser'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'bool': {
             'should': [{'bool': {
                 'should': [{'nested': {'path': 'assignments_nested', 'query': {'bool': {
                     'should': [{'term': {'assignments_nested.assignee_id': 'someuser'}},
                                {'term': {'assignments_nested.is_completed': 'false'}}],
                     'minimum_should_match': 1}}}}], 'must': [{'nested': {'path': 'assignments_nested', 'query': {'bool': {
                     'must': [{'term': {'assignments_nested.assignor_id': 'another'}}]}}}}],
                 'minimum_should_match': 1}}, {'term': {'owner_id': 'someuser'}}], 'minimum_should_match': 1}}]}}
 def test_do_not_apply_needless_parentheses_with_statements(self):
     query_string = '((((assignments.assignee_id:someuse)) AND ((assignments.is_completed:true))) OR assignments.is_completed:brayan)'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {
         'must': [{'bool': {
             'must': [{'nested': {'query': {'bool': {
                 'must': [{'term': {'assignments_nested.assignee_id': 'someuse'}}]}},
                 'path': 'assignments_nested'}}, {'bool': {
                 'should': [{'nested': {'query': {'bool': {
                     'should': [{'term': {'assignments_nested.is_completed': 'true'}},
                                {'term': {'assignments_nested.is_completed': 'brayan'}}],
                     'minimum_should_match': 1
                 }},
                     'path': 'assignments_nested'}}], 'minimum_should_match': 1}}]}}]}}
 def test_range_query_with_missed_from_and_to(self):
     query_string = '(x:[_missing_ TO 2016-10-18T02:59:59] OR z:[_missing_ TO 2016-10-18T02:59:59]) AND (schedules.end_date:[2016-10-11T03:00:00 TO _missing_] AND schedules.obj_status:active)'
     params = {'es_q': query_string}
     result = compile_es_query(params)
     assert result == {'bool': {'must': [{'bool': {
         'must': [{
             'bool': {
                 'should': [{
                     'range': {'x': {
                         'lte': '2016-10-18T02:59:59'}}}, {
                     'range': {'z': {
                         'lte': '2016-10-18T02:59:59'}}}],
             'minimum_should_match': 1
             }}, {
             'bool': {
                 'must': [{
                     'nested': {'query': {'bool': {
                         'must': [{
                             'range': {'schedules_nested.end_date': {
                                 'gte': '2016-10-11T03:00:00'}}},
                             {'term': {'schedules_nested.obj_status': 'active'}}]}},
                         'path': 'schedules_nested'}}]}}]}}]}}
    def build_search_params(self, params):
        params = dictset(params)

        _params = dict(
            index=self.index_name,
            doc_type=self.doc_type
        )
        _raw_terms = params.pop('q', '')

        if 'body' not in params and 'es_q' not in params:
            analyzed_terms = apply_analyzer(params, self.doc_type, engine.get_document_cls)

            query_string = self.build_qs(params.remove(RESERVED_PARAMS), _raw_terms)

            query = {'must': []}

            if query_string:
                query['must'].append({'query_string': {'query': query_string}})

            if analyzed_terms:
                query['must'].append(analyzed_terms)

            if query['must']:
                _params['body'] = {'query': {'bool': query}}
            else:
                _params['body'] = {'query': {'match_all': {}}}

        if 'body' in params:
            raise JHTTPUnprocessableEntity('Illegal parameter "body"')

        if '_limit' not in params:
            params['_limit'] = self.api.count(index=self.index_name)['count']
        _params['from_'], _params['size'] = process_limit(
            params.get('_start', None),
            params.get('_page', None),
            params['_limit'])

        if 'es_q' in params:
            _params['body'] = {}

            try:
                _params['body']['query'] = compile_es_query(params)
            except Exception as exc:
                log.exception('es_q parsing error: {exc}'.format(exc=exc))
                raise JHTTPBadRequest('Bad query string for {params}'
                                        .format(
                                                params=_params['body']['query']['query_string']['query']))

            log.debug('Parsed ES request body {body}'.format(body=_params['body']['query']))

        if '_sort' in params and self.proxy:
            params['_sort'] = substitute_nested_terms(params['_sort'], self.proxy.substitutions)

        if '_sort' in params:
            _params['sort'] = apply_sort(params['_sort'])

        if '_fields' in params:
            params['_fields'] = self.add_nested_fields(params['_fields'], ',')
            _params['fields'] = params['_fields']

        if '_search_fields' in params:
            search_fields = params['_search_fields'].split(',')

            search_fields.reverse()

            # Substitute search fields and add ^index
            for index, search_field in enumerate(search_fields):
                sf_terms = search_field.split('.')

                if self.proxy is not None:
                    if len(sf_terms) > 0 and sf_terms[0] in self.proxy.substitutions:
                        sf_terms[0] += "_nested"
                        search_field = '.'.join(sf_terms)

                search_fields[index] = search_field + '^' + str(index + 1)

            must_query = _params['body']['query']['bool']['must']
            query_string = {}

            for query_item in must_query:
                if 'query_string' in query_item:
                    query_string = query_item
                    break

            current_qs = query_string.get('query_string', None)

            if current_qs:
                query_string['query_string']['fields'] = search_fields

        return _params