Skip to content

Commit 56832d8

Browse files
authored
Support Elasticsearch 8 (#1664)
1 parent 9afbea1 commit 56832d8

File tree

13 files changed

+118
-90
lines changed

13 files changed

+118
-90
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
"3.10",
6868
"3.11",
6969
]
70-
es-version: [7.0.0, 7.10.0]
70+
es-version: [8.0.0, 8.9.0]
7171

7272
steps:
7373
- name: Checkout Repository
@@ -76,7 +76,7 @@ jobs:
7676
run: |
7777
mkdir /tmp/elasticsearch
7878
wget -O - https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${{ matrix.es-version }}-linux-x86_64.tar.gz | tar xz --directory=/tmp/elasticsearch --strip-components=1
79-
/tmp/elasticsearch/bin/elasticsearch -d
79+
/tmp/elasticsearch/bin/elasticsearch -E xpack.security.enabled=false -E discovery.type=single-node -d
8080
- name: Setup Python - ${{ matrix.python-version }}
8181
uses: actions/setup-python@v4
8282
with:

elasticsearch_dsl/connections.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ class Connections:
2626
singleton in this module.
2727
"""
2828

29-
def __init__(self):
29+
def __init__(self, *, elasticsearch_class=Elasticsearch):
3030
self._kwargs = {}
3131
self._conns = {}
32+
self.elasticsearch_class = elasticsearch_class
3233

3334
def configure(self, **kwargs):
3435
"""
@@ -80,7 +81,7 @@ def create_connection(self, alias="default", **kwargs):
8081
it under given alias.
8182
"""
8283
kwargs.setdefault("serializer", serializer)
83-
conn = self._conns[alias] = Elasticsearch(**kwargs)
84+
conn = self._conns[alias] = self.elasticsearch_class(**kwargs)
8485
return conn
8586

8687
def get_connection(self, alias="default"):

elasticsearch_dsl/search.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import collections.abc
1919
import copy
2020

21-
from elasticsearch.exceptions import TransportError
21+
from elasticsearch.exceptions import ApiError
2222
from elasticsearch.helpers import scan
2323

2424
from .aggs import A, AggBase
@@ -693,7 +693,8 @@ def count(self):
693693

694694
d = self.to_dict(count=True)
695695
# TODO: failed shards detection
696-
return es.count(index=self._index, body=d, **self._params)["count"]
696+
resp = es.count(index=self._index, query=d.get("query", None), **self._params)
697+
return resp["count"]
697698

698699
def execute(self, ignore_cache=False):
699700
"""
@@ -707,7 +708,8 @@ def execute(self, ignore_cache=False):
707708
es = get_connection(self._using)
708709

709710
self._response = self._response_class(
710-
self, es.search(index=self._index, body=self.to_dict(), **self._params)
711+
self,
712+
es.search(index=self._index, body=self.to_dict(), **self._params).body,
711713
)
712714
return self._response
713715

@@ -799,7 +801,7 @@ def execute(self, ignore_cache=False, raise_on_error=True):
799801
for s, r in zip(self._searches, responses["responses"]):
800802
if r.get("error", False):
801803
if raise_on_error:
802-
raise TransportError("N/A", r["error"]["type"], r["error"])
804+
raise ApiError("N/A", meta=responses.meta, body=r)
803805
r = None
804806
else:
805807
r = Response(s, r)

examples/alias_migration.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,8 @@ def migrate(move_data=True, update_alias=True):
106106

107107
if move_data:
108108
# move data from current alias to the new index
109-
es.reindex(
110-
body={"source": {"index": ALIAS}, "dest": {"index": next_index}},
111-
request_timeout=3600,
109+
es.options(request_timeout=3600).reindex(
110+
body={"source": {"index": ALIAS}, "dest": {"index": next_index}}
112111
)
113112
# refresh the index to make the changes visible
114113
es.indices.refresh(index=next_index)

setup.cfg

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,4 @@ filterwarnings =
1212
# The body parameter is no longer deprecated, see
1313
# https://github.com/elastic/elasticsearch-py/issues/2181#issuecomment-1490932964
1414
ignore:The 'body' parameter is deprecated .*:DeprecationWarning
15-
# calendar_interval was only added in Elasticsearch 7.2 and we still support Elasticsearch 7.0
16-
# using `default` instead of `ignore` to show it in the output as a reminder to remove it for Elasticsearch 8
17-
default:\[interval\] on \[date_histogram\] is deprecated, use \[fixed_interval\] or \[calendar_interval\] in the future.:elasticsearch.exceptions.ElasticsearchWarning
15+
ignore:Legacy index templates are deprecated in favor of composable templates.:elasticsearch.exceptions.ElasticsearchWarning

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
install_requires = [
3131
"python-dateutil",
32-
"elasticsearch>=7.0.0,<8.0.0",
32+
"elasticsearch>=8.0.0,<9.0.0",
3333
]
3434

3535
develop_requires = [

tests/conftest.py

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from unittest import SkipTest, TestCase
2424
from unittest.mock import Mock
2525

26+
from elastic_transport import ObjectApiResponse
2627
from elasticsearch import Elasticsearch
2728
from elasticsearch.exceptions import ConnectionError
2829
from elasticsearch.helpers import bulk
@@ -47,7 +48,7 @@
4748

4849
def get_test_client(wait=True, **kwargs):
4950
# construct kwargs from the environment
50-
kw = {"timeout": 30}
51+
kw = {"request_timeout": 30}
5152

5253
if "PYTHON_CONNECTION_CLASS" in os.environ:
5354
from elasticsearch import connection
@@ -131,8 +132,9 @@ def es_version(client):
131132
@fixture
132133
def write_client(client):
133134
yield client
134-
client.indices.delete(index="test-*", ignore=404)
135-
client.indices.delete_template(name="test-template", ignore=404)
135+
for index_name in client.indices.get(index="test-*", expand_wildcards="all"):
136+
client.indices.delete(index=index_name)
137+
client.options(ignore_status=404).indices.delete_template(name="test-template")
136138

137139

138140
@fixture
@@ -160,55 +162,58 @@ def data_client(client):
160162

161163
@fixture
162164
def dummy_response():
163-
return {
164-
"_shards": {"failed": 0, "successful": 10, "total": 10},
165-
"hits": {
166-
"hits": [
167-
{
168-
"_index": "test-index",
169-
"_type": "company",
170-
"_id": "elasticsearch",
171-
"_score": 12.0,
172-
"_source": {"city": "Amsterdam", "name": "Elasticsearch"},
173-
},
174-
{
175-
"_index": "test-index",
176-
"_type": "employee",
177-
"_id": "42",
178-
"_score": 11.123,
179-
"_routing": "elasticsearch",
180-
"_source": {
181-
"name": {"first": "Shay", "last": "Bannon"},
182-
"lang": "java",
183-
"twitter": "kimchy",
165+
return ObjectApiResponse(
166+
meta=None,
167+
body={
168+
"_shards": {"failed": 0, "successful": 10, "total": 10},
169+
"hits": {
170+
"hits": [
171+
{
172+
"_index": "test-index",
173+
"_type": "company",
174+
"_id": "elasticsearch",
175+
"_score": 12.0,
176+
"_source": {"city": "Amsterdam", "name": "Elasticsearch"},
177+
},
178+
{
179+
"_index": "test-index",
180+
"_type": "employee",
181+
"_id": "42",
182+
"_score": 11.123,
183+
"_routing": "elasticsearch",
184+
"_source": {
185+
"name": {"first": "Shay", "last": "Bannon"},
186+
"lang": "java",
187+
"twitter": "kimchy",
188+
},
189+
},
190+
{
191+
"_index": "test-index",
192+
"_type": "employee",
193+
"_id": "47",
194+
"_score": 1,
195+
"_routing": "elasticsearch",
196+
"_source": {
197+
"name": {"first": "Honza", "last": "Král"},
198+
"lang": "python",
199+
"twitter": "honzakral",
200+
},
184201
},
185-
},
186-
{
187-
"_index": "test-index",
188-
"_type": "employee",
189-
"_id": "47",
190-
"_score": 1,
191-
"_routing": "elasticsearch",
192-
"_source": {
193-
"name": {"first": "Honza", "last": "Král"},
194-
"lang": "python",
195-
"twitter": "honzakral",
202+
{
203+
"_index": "test-index",
204+
"_type": "employee",
205+
"_id": "53",
206+
"_score": 16.0,
207+
"_routing": "elasticsearch",
196208
},
197-
},
198-
{
199-
"_index": "test-index",
200-
"_type": "employee",
201-
"_id": "53",
202-
"_score": 16.0,
203-
"_routing": "elasticsearch",
204-
},
205-
],
206-
"max_score": 12.0,
207-
"total": 123,
209+
],
210+
"max_score": 12.0,
211+
"total": 123,
212+
},
213+
"timed_out": False,
214+
"took": 123,
208215
},
209-
"timed_out": False,
210-
"took": 123,
211-
}
216+
)
212217

213218

214219
@fixture

tests/test_connections.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
from elasticsearch_dsl import connections, serializer
2222

2323

24+
class DummyElasticsearch:
25+
def __init__(self, *args, hosts, **kwargs):
26+
self.hosts = hosts
27+
28+
2429
def test_default_connection_is_returned_by_default():
2530
c = connections.Connections()
2631

@@ -33,27 +38,36 @@ def test_default_connection_is_returned_by_default():
3338

3439

3540
def test_get_connection_created_connection_if_needed():
36-
c = connections.Connections()
37-
c.configure(default={"hosts": ["es.com"]}, local={"hosts": ["localhost"]})
41+
c = connections.Connections(elasticsearch_class=DummyElasticsearch)
42+
c.configure(
43+
default={"hosts": ["https://es.com:9200"]},
44+
local={"hosts": ["https://localhost:9200"]},
45+
)
3846

3947
default = c.get_connection()
4048
local = c.get_connection("local")
4149

42-
assert isinstance(default, Elasticsearch)
43-
assert isinstance(local, Elasticsearch)
50+
assert isinstance(default, DummyElasticsearch)
51+
assert isinstance(local, DummyElasticsearch)
4452

45-
assert [{"host": "es.com"}] == default.transport.hosts
46-
assert [{"host": "localhost"}] == local.transport.hosts
53+
assert default.hosts == ["https://es.com:9200"]
54+
assert local.hosts == ["https://localhost:9200"]
4755

4856

4957
def test_configure_preserves_unchanged_connections():
50-
c = connections.Connections()
58+
c = connections.Connections(elasticsearch_class=DummyElasticsearch)
5159

52-
c.configure(default={"hosts": ["es.com"]}, local={"hosts": ["localhost"]})
60+
c.configure(
61+
default={"hosts": ["https://es.com:9200"]},
62+
local={"hosts": ["https://localhost:9200"]},
63+
)
5364
default = c.get_connection()
5465
local = c.get_connection("local")
5566

56-
c.configure(default={"hosts": ["not-es.com"]}, local={"hosts": ["localhost"]})
67+
c.configure(
68+
default={"hosts": ["https://not-es.com:9200"]},
69+
local={"hosts": ["https://localhost:9200"]},
70+
)
5771
new_default = c.get_connection()
5872
new_local = c.get_connection("local")
5973

@@ -62,9 +76,12 @@ def test_configure_preserves_unchanged_connections():
6276

6377

6478
def test_remove_connection_removes_both_conn_and_conf():
65-
c = connections.Connections()
79+
c = connections.Connections(elasticsearch_class=DummyElasticsearch)
6680

67-
c.configure(default={"hosts": ["es.com"]}, local={"hosts": ["localhost"]})
81+
c.configure(
82+
default={"hosts": ["https://es.com:9200"]},
83+
local={"hosts": ["https://localhost:9200"]},
84+
)
6885
c.add_connection("local2", object())
6986

7087
c.remove_connection("default")
@@ -77,15 +94,16 @@ def test_remove_connection_removes_both_conn_and_conf():
7794

7895

7996
def test_create_connection_constructs_client():
80-
c = connections.Connections()
81-
c.create_connection("testing", hosts=["es.com"])
97+
c = connections.Connections(elasticsearch_class=DummyElasticsearch)
98+
c.create_connection("testing", hosts=["https://es.com:9200"])
8299

83100
con = c.get_connection("testing")
84-
assert [{"host": "es.com"}] == con.transport.hosts
101+
assert con.hosts == ["https://es.com:9200"]
85102

86103

87104
def test_create_connection_adds_our_serializer():
88-
c = connections.Connections()
89-
c.create_connection("testing", hosts=["es.com"])
105+
c = connections.Connections(elasticsearch_class=Elasticsearch)
106+
c.create_connection("testing", hosts=["https://es.com:9200"])
90107

91-
assert c.get_connection("testing").transport.serializer is serializer.serializer
108+
c_serializers = c.get_connection("testing").transport.serializers
109+
assert c_serializers.serializers["application/json"] is serializer.serializer

tests/test_integration/test_document.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ def test_save_and_update_return_doc_meta(write_client):
279279
"_primary_term",
280280
"_seq_no",
281281
"_shards",
282-
"_type",
283282
"_version",
284283
"result",
285284
}
@@ -295,7 +294,6 @@ def test_save_and_update_return_doc_meta(write_client):
295294
"_primary_term",
296295
"_seq_no",
297296
"_shards",
298-
"_type",
299297
"_version",
300298
"result",
301299
}
@@ -318,11 +316,15 @@ def test_get_raises_404_on_non_existent_id(data_client):
318316

319317

320318
def test_get_returns_none_if_404_ignored(data_client):
321-
assert None is Repository.get("elasticsearch-dsl-php", ignore=404)
319+
assert None is Repository.get(
320+
"elasticsearch-dsl-php", using=data_client.options(ignore_status=404)
321+
)
322322

323323

324324
def test_get_returns_none_if_404_ignored_and_index_doesnt_exist(data_client):
325-
assert None is Repository.get("42", index="not-there", ignore=404)
325+
assert None is Repository.get(
326+
"42", index="not-there", using=data_client.options(ignore_status=404)
327+
)
326328

327329

328330
def test_get(data_client):

tests/test_integration/test_examples/test_composite_aggs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ def test_scan_aggs_with_multiple_aggs(data_client):
3434
{"files": A("terms", field="files")},
3535
{
3636
"months": {
37-
"date_histogram": {"field": "committed_date", "interval": "month"}
37+
"date_histogram": {
38+
"field": "committed_date",
39+
"calendar_interval": "month",
40+
}
3841
}
3942
},
4043
]

tests/test_integration/test_search.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# under the License.
1717

1818

19-
from elasticsearch import TransportError
19+
from elasticsearch import ApiError
2020
from pytest import raises
2121

2222
from elasticsearch_dsl import Date, Document, Keyword, MultiSearch, Q, Search, Text
@@ -143,7 +143,7 @@ def test_multi_missing(data_client):
143143
ms = MultiSearch()
144144
ms = ms.add(s1).add(s2).add(s3)
145145

146-
with raises(TransportError):
146+
with raises(ApiError):
147147
ms.execute()
148148

149149
r1, r2, r3 = ms.execute(raise_on_error=False)

0 commit comments

Comments
 (0)