From 1e928310afef793e77c20e838a5c32013c064a68 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 15 Jul 2020 17:00:26 -0700 Subject: [PATCH 01/12] Adds naive implementation for match queries --- Makefile | 22 +++++------ elasticmock/fake_elasticsearch.py | 50 ++++++++++++++++++++++++- tests/fake_elasticsearch/test_search.py | 8 ++++ 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 99cfdf6..6491b8a 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,26 @@ ELASTICMOCK_VERSION='1.5.1' install: - @pip install -r requirements.txt + pip install -r requirements.txt test_install: install - @pip install -r requirements_test.txt + pip install -r requirements_test.txt test: test_install - @tox -p 20 --parallel--safe-build + tox -p 20 --parallel--safe-build upload: create_dist - @pip install twine - @twine upload dist/* - @git push + pip install twine + twine upload dist/* + git push create_dist: create_dist_commit update_pip - @rm -rf dist - @python setup.py sdist + rm -rf dist + python setup.py sdist create_dist_commit: - @git commit --all -m "Bump version ${ELASTICMOCK_VERSION}" - @git tag ${ELASTICMOCK_VERSION} + git commit --all -m "Bump version ${ELASTICMOCK_VERSION}" + git tag ${ELASTICMOCK_VERSION} update_pip: - @pip install --upgrade pip + pip install --upgrade pip diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index a93b65f..3aa8bdc 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -18,6 +18,38 @@ unicode = str +class MatchType: + MATCH = object() + + @classmethod + def get_match_type(cls, type_str): + if type_str == 'match': + return cls.MATCH + +class FakeQueryCondition: + type = None + conditon = None + + def __init__(self, type, condition): + self.type = type + self.condition = condition + + def evaluate(self, document): + return_val = False + doc_source = document['_source'] + for field, value in self.condition.items(): + if hasattr(doc_source, field): + doc_val = str(getattr(doc_source, field)) + if str(value) in doc_val: + return_val = True + break + elif field in doc_source: + doc_val = str(doc_source[field]) + if str(value) in doc_val: + return_val = True + break + return return_val + @for_all_methods([server_failure]) class FakeElasticsearch(Elasticsearch): __documents_dict = None @@ -201,6 +233,9 @@ def count(self, index=None, doc_type=None, body=None, params=None, headers=None) return result + def _get_fake_query_condition(self, match_type_str, condition): + return FakeQueryCondition(MatchType.get_match_type(match_type_str), condition) + @query_params('_source', '_source_exclude', '_source_include', 'allow_no_indices', 'analyze_wildcard', 'analyzer', 'default_operator', 'df', 'expand_wildcards', 'explain', 'fielddata_fields', 'fields', @@ -213,6 +248,12 @@ def search(self, index=None, doc_type=None, body=None, params=None, headers=None searchable_indexes = self._normalize_index_to_list(index) matches = [] + conditions = [] + + if body and 'query' in body: + query = body['query'] + for match_type, condition in query.items(): + conditions.append(self._get_fake_query_condition(match_type, condition)) for searchable_index in searchable_indexes: for document in self.__documents_dict[searchable_index]: if doc_type: @@ -220,7 +261,14 @@ def search(self, index=None, doc_type=None, body=None, params=None, headers=None continue if isinstance(doc_type, str) and document.get('_type') != doc_type: continue - matches.append(document) + if conditions: + match = False + for condition in conditions: + if condition.evaluate(document): + matches.append(document) + break + else: + matches.append(document) result = { 'hits': { diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 098f77b..7173182 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -65,3 +65,11 @@ def test_search_with_scroll_param(self): self.assertNotEqual(None, result.get('_scroll_id', None)) self.assertEqual(30, len(result.get('hits').get('hits'))) self.assertEqual(100, result.get('hits').get('total')) + + def test_search_for_keyword(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': f'test_{i}'}) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': 'test_3' } } }) + self.assertEqual(response['hits']['total'], 1) + From 1108ff6a23e779f412c4c05ec4329f49a78794f6 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 15 Jul 2020 17:19:36 -0700 Subject: [PATCH 02/12] Begin process of splitting out logic for different query types --- elasticmock/fake_elasticsearch.py | 32 ++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index 3aa8bdc..0ccc664 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -18,13 +18,19 @@ unicode = str -class MatchType: - MATCH = object() +MATCH = object() +TERM = object() + + +class QueryType: @classmethod - def get_match_type(cls, type_str): + def get_query_type(cls, type_str): if type_str == 'match': - return cls.MATCH + return MATCH + elif type_str == 'term': + return TERM + class FakeQueryCondition: type = None @@ -35,8 +41,15 @@ def __init__(self, type, condition): self.condition = condition def evaluate(self, document): - return_val = False + return self._evaluate_for_query_type(document) + + def _evaluate_for_query_type(self, document): + if self.type == MATCH: + return self._evaluate_for_match_type(document) + + def _evaluate_for_match_type(self, document): doc_source = document['_source'] + return_val = False for field, value in self.condition.items(): if hasattr(doc_source, field): doc_val = str(getattr(doc_source, field)) @@ -50,6 +63,7 @@ def evaluate(self, document): break return return_val + @for_all_methods([server_failure]) class FakeElasticsearch(Elasticsearch): __documents_dict = None @@ -233,8 +247,8 @@ def count(self, index=None, doc_type=None, body=None, params=None, headers=None) return result - def _get_fake_query_condition(self, match_type_str, condition): - return FakeQueryCondition(MatchType.get_match_type(match_type_str), condition) + def _get_fake_query_condition(self, query_type_str, condition): + return FakeQueryCondition(QueryType.get_query_type(query_type_str), condition) @query_params('_source', '_source_exclude', '_source_include', 'allow_no_indices', 'analyze_wildcard', 'analyzer', 'default_operator', @@ -252,8 +266,8 @@ def search(self, index=None, doc_type=None, body=None, params=None, headers=None if body and 'query' in body: query = body['query'] - for match_type, condition in query.items(): - conditions.append(self._get_fake_query_condition(match_type, condition)) + for query_type_str, condition in query.items(): + conditions.append(self._get_fake_query_condition(query_type_str, condition)) for searchable_index in searchable_indexes: for document in self.__documents_dict[searchable_index]: if doc_type: From 5d88a85213b1af2bcf6ba23b6996b83940ba8c45 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 15 Jul 2020 17:22:38 -0700 Subject: [PATCH 03/12] Fixes failing py2.7 test --- tests/fake_elasticsearch/test_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 7173182..7a18109 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -68,7 +68,7 @@ def test_search_with_scroll_param(self): def test_search_for_keyword(self): for i in range(0, 10): - self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': f'test_{i}'}) + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': 'test_{0}'.format(i)}) response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': 'test_3' } } }) self.assertEqual(response['hits']['total'], 1) From 61289bfc6d438f65ca5bab469c0396fdfaf1cf32 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 15 Jul 2020 18:14:28 -0700 Subject: [PATCH 04/12] Adds naive search for term queries --- elasticmock/fake_elasticsearch.py | 24 +++++++++++++++++++++--- tests/fake_elasticsearch/test_search.py | 21 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index 0ccc664..0ea1b22 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -45,9 +45,28 @@ def evaluate(self, document): def _evaluate_for_query_type(self, document): if self.type == MATCH: - return self._evaluate_for_match_type(document) + return self._evaluate_for_match_query_type(document) + if self.type == TERM: + return self._evaluate_for_term_query_type(document) - def _evaluate_for_match_type(self, document): + def _evaluate_for_match_query_type(self, document): + doc_source = document['_source'] + return_val = False + for field, value in self.condition.items(): + value = str(value).lower() + if hasattr(doc_source, field): + doc_val = str(getattr(doc_source, field)).lower() + if value in doc_val: + return_val = True + break + elif field in doc_source: + doc_val = str(doc_source[field]).lower() + if value in doc_val: + return_val = True + break + return return_val + + def _evaluate_for_term_query_type(self, document): doc_source = document['_source'] return_val = False for field, value in self.condition.items(): @@ -63,7 +82,6 @@ def _evaluate_for_match_type(self, document): break return return_val - @for_all_methods([server_failure]) class FakeElasticsearch(Elasticsearch): __documents_dict = None diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 7a18109..3b04ed8 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -66,10 +66,27 @@ def test_search_with_scroll_param(self): self.assertEqual(30, len(result.get('hits').get('hits'))) self.assertEqual(100, result.get('hits').get('total')) - def test_search_for_keyword(self): + def test_search_with_match_query(self): for i in range(0, 10): self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': 'test_{0}'.format(i)}) - response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': 'test_3' } } }) + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': '3' } } }) self.assertEqual(response['hits']['total'], 1) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 1) + self.assertEqual(hits[0]['_source'], {'data': 'test_3'}) + def test_search_with_term_query(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': 'test_{0}'.format(i)}) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'term': {'data': 'TEST' } } }) + self.assertEqual(response['hits']['total'], 0) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 0) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'term': {'data': '3' } } }) + self.assertEqual(response['hits']['total'], 1) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 1) + self.assertEqual(hits[0]['_source'], {'data': 'test_3'}) From abb475f1256758a14906c0cb61c18ca13ab191d6 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 15 Jul 2020 18:39:07 -0700 Subject: [PATCH 05/12] Removes unused variable --- elasticmock/fake_elasticsearch.py | 1 - 1 file changed, 1 deletion(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index 0ea1b22..732405f 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -294,7 +294,6 @@ def search(self, index=None, doc_type=None, body=None, params=None, headers=None if isinstance(doc_type, str) and document.get('_type') != doc_type: continue if conditions: - match = False for condition in conditions: if condition.evaluate(document): matches.append(document) From 43ed5f7937ab2ade943e621f8413f1a45caa729d Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Wed, 22 Jul 2020 22:00:12 +0000 Subject: [PATCH 06/12] Adds searching within lists --- elasticmock/fake_elasticsearch.py | 56 +++++++++++++++---------- tests/fake_elasticsearch/test_search.py | 21 +++++++++- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index 732405f..0510a22 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -53,35 +53,49 @@ def _evaluate_for_match_query_type(self, document): doc_source = document['_source'] return_val = False for field, value in self.condition.items(): - value = str(value).lower() - if hasattr(doc_source, field): - doc_val = str(getattr(doc_source, field)).lower() - if value in doc_val: - return_val = True - break - elif field in doc_source: - doc_val = str(doc_source[field]).lower() - if value in doc_val: - return_val = True - break + return_val = self._compare_value_for_field(doc_source, field, value, True) + if return_val: + break return return_val def _evaluate_for_term_query_type(self, document): doc_source = document['_source'] return_val = False for field, value in self.condition.items(): - if hasattr(doc_source, field): - doc_val = str(getattr(doc_source, field)) - if str(value) in doc_val: - return_val = True - break - elif field in doc_source: - doc_val = str(doc_source[field]) - if str(value) in doc_val: - return_val = True - break + return_val = self._compare_value_for_field(doc_source, field, value, False) + if return_val: + break return return_val + def _compare_value_for_field(self, doc_source, field, value, ignore_case): + value = str(value).lower() if ignore_case and isinstance(value, str) else value + doc_val = None + if hasattr(doc_source, field): + doc_val = getattr(doc_source, field) + elif field in doc_source: + doc_val = doc_source[field] + + if isinstance(doc_val, list): + for val in doc_val: + val = val if isinstance(val, (int, float, complex)) else str(val) + if ignore_case and isinstance(val, str): + val = val.lower() + if isinstance(val, str) and value in val: + return True + if value == val: + return True + else: + doc_val = doc_val if isinstance(doc_val, (int, float, complex)) else str(doc_val) + if ignore_case and isinstance(doc_val, str): + doc_val = doc_val.lower() + if isinstance(doc_val, str) and value in doc_val: + return True + if value == doc_val: + return True + + return False + + @for_all_methods([server_failure]) class FakeElasticsearch(Elasticsearch): __documents_dict = None diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 3b04ed8..f1e690a 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -75,7 +75,26 @@ def test_search_with_match_query(self): hits = response['hits']['hits'] self.assertEqual(len(hits), 1) self.assertEqual(hits[0]['_source'], {'data': 'test_3'}) - + + def test_search_with_match_query_in_int_list(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': [i, 11, 13]}) + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': 1 } } }) + self.assertEqual(response['hits']['total'], 1) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 1) + self.assertEqual(hits[0]['_source'], {'data': [1, 11, 13] }) + + def test_search_with_match_query_in_string_list(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': [str(i), 'two', 'three']}) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': '1' } } }) + self.assertEqual(response['hits']['total'], 1) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 1) + self.assertEqual(hits[0]['_source'], {'data': ['1', 'two', 'three']}) + def test_search_with_term_query(self): for i in range(0, 10): self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': 'test_{0}'.format(i)}) From 2a4f7b4673eb88587fedb5303f1dc83849b93917 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Thu, 23 Jul 2020 20:54:05 +0000 Subject: [PATCH 07/12] Adds negative test case for match query --- tests/fake_elasticsearch/test_search.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index f1e690a..5f2c0b0 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -69,7 +69,12 @@ def test_search_with_scroll_param(self): def test_search_with_match_query(self): for i in range(0, 10): self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'data': 'test_{0}'.format(i)}) - + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': 'TEST' } } }) + self.assertEqual(response['hits']['total'], 10) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 10) + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'match': {'data': '3' } } }) self.assertEqual(response['hits']['total'], 1) hits = response['hits']['hits'] From 95f4a7e9c6f81e430e0813a5e1497e60fdfde950 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Sat, 1 Aug 2020 01:07:24 +0000 Subject: [PATCH 08/12] Adds FILTER and BOOL query types --- elasticmock/fake_elasticsearch.py | 92 ++++++++++++++++++------- tests/fake_elasticsearch/test_search.py | 9 +++ 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index 0510a22..b1b1306 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -18,57 +18,86 @@ unicode = str -MATCH = object() -TERM = object() - - class QueryType: - @classmethod - def get_query_type(cls, type_str): + MATCH = object() + TERM = object() + BOOL = object() + FILTER = object() + + @staticmethod + def get_query_type(type_str): if type_str == 'match': - return MATCH + return QueryType.MATCH elif type_str == 'term': - return TERM + return QueryType.TERM + elif type_str == 'bool': + return QueryType.BOOL + elif type_str == 'filter': + return QueryType.FILTER + else: + raise NotImplementedError(f'type {type_str} is not implemented for QueryType') class FakeQueryCondition: type = None - conditon = None + condition = None def __init__(self, type, condition): self.type = type self.condition = condition - + def evaluate(self, document): return self._evaluate_for_query_type(document) - + def _evaluate_for_query_type(self, document): - if self.type == MATCH: + if self.type == QueryType.MATCH: return self._evaluate_for_match_query_type(document) - if self.type == TERM: + if self.type == QueryType.TERM: return self._evaluate_for_term_query_type(document) - + if self.type == QueryType.BOOL: + return self._evaluate_for_compound_query_type(document) + if self.type == QueryType.FILTER: + return self._evaluate_for_compound_query_type(document) + def _evaluate_for_match_query_type(self, document): + return self._evaluate_for_field(document, True) + + def _evaluate_for_term_query_type(self, document): + return self._evaluate_for_field(document, False) + + def _evaluate_for_field(self, document, ignore_case): doc_source = document['_source'] return_val = False for field, value in self.condition.items(): - return_val = self._compare_value_for_field(doc_source, field, value, True) + return_val = self._compare_value_for_field( + doc_source, + field, + value, + ignore_case + ) if return_val: break return return_val - def _evaluate_for_term_query_type(self, document): - doc_source = document['_source'] + def _evaluate_for_compound_query_type(self, document): return_val = False - for field, value in self.condition.items(): - return_val = self._compare_value_for_field(doc_source, field, value, False) - if return_val: - break + if isinstance(self.condition, dict): + for query_type, sub_query in self.condition.items(): + return_val = FakeQueryCondition( + QueryType.get_query_type(query_type), + sub_query + ).evaluate(document) + if return_val: + break + elif isinstance(self.condition, list): + raise NotImplementedError() + return return_val def _compare_value_for_field(self, doc_source, field, value, ignore_case): - value = str(value).lower() if ignore_case and isinstance(value, str) else value + value = str(value).lower() if ignore_case and isinstance(value, str) \ + else value doc_val = None if hasattr(doc_source, field): doc_val = getattr(doc_source, field) @@ -77,7 +106,8 @@ def _compare_value_for_field(self, doc_source, field, value, ignore_case): if isinstance(doc_val, list): for val in doc_val: - val = val if isinstance(val, (int, float, complex)) else str(val) + val = val if isinstance(val, (int, float, complex)) \ + else str(val) if ignore_case and isinstance(val, str): val = val.lower() if isinstance(val, str) and value in val: @@ -85,7 +115,8 @@ def _compare_value_for_field(self, doc_source, field, value, ignore_case): if value == val: return True else: - doc_val = doc_val if isinstance(doc_val, (int, float, complex)) else str(doc_val) + doc_val = doc_val if isinstance(doc_val, (int, float, complex)) \ + else str(doc_val) if ignore_case and isinstance(doc_val, str): doc_val = doc_val.lower() if isinstance(doc_val, str) and value in doc_val: @@ -133,8 +164,17 @@ def info(self, params=None, headers=None): 'tagline': 'You Know, for Search' } - @query_params('consistency', 'op_type', 'parent', 'refresh', 'replication', - 'routing', 'timeout', 'timestamp', 'ttl', 'version', 'version_type') + @query_params('consistency', + 'op_type', + 'parent', + 'refresh', + 'replication', + 'routing', + 'timeout', + 'timestamp', + 'ttl', + 'version', + 'version_type') def index(self, index, body, doc_type='_doc', id=None, params=None, headers=None): if index not in self.__documents_dict: self.__documents_dict[index] = list() diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 5f2c0b0..63c6860 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -114,3 +114,12 @@ def test_search_with_term_query(self): hits = response['hits']['hits'] self.assertEqual(len(hits), 1) self.assertEqual(hits[0]['_source'], {'data': 'test_3'}) + + def test_search_with_bool_query(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'id': i}) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'bool': {'filter': [{'term': {'id': 1}}]}}}) + self.assertEqual(response['hits']['total'], 1) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 1) From 090f040de01a4fddd2242e4f6ccba74d4ceb31a9 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Mon, 10 Aug 2020 21:06:22 +0000 Subject: [PATCH 09/12] Implements bool query type --- Makefile | 6 +++--- elasticmock/fake_elasticsearch.py | 34 ++++++++++++++++++++----------- requirements.txt | 3 ++- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 6491b8a..1627946 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -ELASTICMOCK_VERSION='1.5.1' +ELASTICMOCK_VERSION='2.0' install: pip install -r requirements.txt @@ -7,7 +7,7 @@ test_install: install pip install -r requirements_test.txt test: test_install - tox -p 20 --parallel--safe-build + python3 setup.py test upload: create_dist pip install twine @@ -16,7 +16,7 @@ upload: create_dist create_dist: create_dist_commit update_pip rm -rf dist - python setup.py sdist + python3 setup.py sdist create_dist_commit: git commit --all -m "Bump version ${ELASTICMOCK_VERSION}" diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index b1b1306..ce4239b 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -20,21 +20,24 @@ class QueryType: - MATCH = object() - TERM = object() - BOOL = object() - FILTER = object() + BOOL = 'BOOL' + FILTER = 'FILTER' + MATCH = 'MATCH' + TERM = 'TERM' + TERMS = 'TERMS' @staticmethod def get_query_type(type_str): - if type_str == 'match': - return QueryType.MATCH - elif type_str == 'term': - return QueryType.TERM - elif type_str == 'bool': + if type_str == 'bool': return QueryType.BOOL elif type_str == 'filter': return QueryType.FILTER + elif type_str == 'match': + return QueryType.MATCH + elif type_str == 'term': + return QueryType.TERM + elif type_str == 'terms': + return QueryType.TERMS else: raise NotImplementedError(f'type {type_str} is not implemented for QueryType') @@ -88,10 +91,17 @@ def _evaluate_for_compound_query_type(self, document): QueryType.get_query_type(query_type), sub_query ).evaluate(document) - if return_val: - break + if not return_val: + return False elif isinstance(self.condition, list): - raise NotImplementedError() + for sub_condition in self.condition: + for sub_condition_key in sub_condition: + return_val = FakeQueryCondition( + QueryType.get_query_type(sub_condition_key), + sub_condition[sub_condition_key] + ).evaluate(document) + if not return_val: + return False return return_val diff --git a/requirements.txt b/requirements.txt index 7f58b5a..822a3ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ elasticsearch>=1.9.0,<8.0.0 -mock==3.0.5 \ No newline at end of file +mock==3.0.5 +ipdb \ No newline at end of file From 929598e00c35fd47bad2bc375d49aa11e919ffce Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Mon, 10 Aug 2020 22:10:26 +0000 Subject: [PATCH 10/12] Adds implementation of terms query --- elasticmock/fake_elasticsearch.py | 17 ++++++++++++++--- tests/fake_elasticsearch/test_search.py | 9 +++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/elasticmock/fake_elasticsearch.py b/elasticmock/fake_elasticsearch.py index ce4239b..541a17a 100644 --- a/elasticmock/fake_elasticsearch.py +++ b/elasticmock/fake_elasticsearch.py @@ -56,12 +56,16 @@ def evaluate(self, document): def _evaluate_for_query_type(self, document): if self.type == QueryType.MATCH: return self._evaluate_for_match_query_type(document) - if self.type == QueryType.TERM: + elif self.type == QueryType.TERM: return self._evaluate_for_term_query_type(document) - if self.type == QueryType.BOOL: + elif self.type == QueryType.TERMS: + return self._evaluate_for_terms_query_type(document) + elif self.type == QueryType.BOOL: return self._evaluate_for_compound_query_type(document) - if self.type == QueryType.FILTER: + elif self.type == QueryType.FILTER: return self._evaluate_for_compound_query_type(document) + else: + raise NotImplementedError('Fake query evaluation not implemented for query type: %s' % self.type) def _evaluate_for_match_query_type(self, document): return self._evaluate_for_field(document, True) @@ -69,6 +73,13 @@ def _evaluate_for_match_query_type(self, document): def _evaluate_for_term_query_type(self, document): return self._evaluate_for_field(document, False) + def _evaluate_for_terms_query_type(self, document): + for field in self.condition: + for term in self.condition[field]: + if FakeQueryCondition(QueryType.TERM, {field: term}).evaluate(document): + return True + return False + def _evaluate_for_field(self, document, ignore_case): doc_source = document['_source'] return_val = False diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 63c6860..886dcab 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -123,3 +123,12 @@ def test_search_with_bool_query(self): self.assertEqual(response['hits']['total'], 1) hits = response['hits']['hits'] self.assertEqual(len(hits), 1) + + def test_search_with_terms_query(self): + for i in range(0, 10): + self.es.index(index='index_for_search', doc_type=DOC_TYPE, body={'id': i}) + + response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'terms': {'id': [1, 2, 3]}}}) + self.assertEqual(response['hits']['total'], 3) + hits = response['hits']['hits'] + self.assertEqual(len(hits), 3) \ No newline at end of file From dbf6bf58d3ac5f1b3966e9ed43281c51b6141dfa Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Mon, 10 Aug 2020 22:10:26 +0000 Subject: [PATCH 11/12] Adds implementation of terms query --- .travis.yml | 1 - tests/fake_elasticsearch/test_search.py | 2 +- tox.ini | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f67258..80f3649 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ dist: xenial language: python python: - - "2.7" - "3.6" - "3.7" - "3.8" diff --git a/tests/fake_elasticsearch/test_search.py b/tests/fake_elasticsearch/test_search.py index 886dcab..74079fe 100644 --- a/tests/fake_elasticsearch/test_search.py +++ b/tests/fake_elasticsearch/test_search.py @@ -131,4 +131,4 @@ def test_search_with_terms_query(self): response = self.es.search(index='index_for_search', doc_type=DOC_TYPE, body={'query': {'terms': {'id': [1, 2, 3]}}}) self.assertEqual(response['hits']['total'], 3) hits = response['hits']['hits'] - self.assertEqual(len(hits), 3) \ No newline at end of file + self.assertEqual(len(hits), 3) diff --git a/tox.ini b/tox.ini index be51eaf..cbc50e8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ # content of: tox.ini , put in same dir as setup.py [tox] envlist = - py27-elasticsearch{1,2,5,6,7} py36-elasticsearch{1,2,5,6,7} py37-elasticsearch{1,2,5,6,7} py38-elasticsearch{1,2,5,6,7} From 191a917d0a961e038c4e626c9df9aa8f37a47a21 Mon Sep 17 00:00:00 2001 From: Kyland Holmes Date: Mon, 10 Aug 2020 22:58:39 +0000 Subject: [PATCH 12/12] bump version to 2.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 72cb368..eafcc94 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import setuptools -__version__ = '1.5.1' +__version__ = '2.0' # read the contents of your readme file from os import path