Skip to content

Commit 540ed35

Browse files
authored
Improved support for refresh during updates (#323)
* Increase configurability of bulk refresh parameter The Elasticsearch bulk APIs support a configurable 'refresh' parameter that can be set to: 1. empty string / true : perform refresh after operation completes 2. 'wait_for': wait until next scheduled refresh 3. false (default): don't do any refresh See documentation at: https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-refresh.html Currently the `Document.update()` API accepts a `refresh` parameter that, if `True`, passes `True` to the ES bulk API. There isn't currently a way to pass 'wait_for'. This commit modifies how `Document.update()` works so that it is possible to pass `refresh='wait_for'`. I've implemented this in a generic way, as opposed to hardcoding logic for 'wait_for', to support future changes to this ES bulk API. Note that, in keeping with the current convention, if `refresh='wait_for'` is passed, this will override the value of auto_refresh set either via setting or via the Document class. This gives developers the maximum configurability here. * Add support for --refresh to search_index command Currently when updating or rebuilding an index, the `refresh` parameter is never set, regardless if settings.ELASTICSEARCH_DSL_AUTO_REFRESH is set or if auto_refresh is set on the Document class. This commit adds the ability to pass `--refresh` to the `search_index` management command, to force a refresh when the index is updated. This can be useful, for example, if you want to programmatically call that command from some Django tests that fill an index, and then immediately run tests against that index. Without setting `refresh=True`, there's no guarantee that the refreshed index will be available to search. * Update refresh parameter overrides auto_refresh * Support Document.auto_refresh = 'wait_for'
1 parent 1bf5ce8 commit 540ed35

File tree

6 files changed

+71
-12
lines changed

6 files changed

+71
-12
lines changed

django_elasticsearch_dsl/documents.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,10 @@ def update(self, thing, refresh=None, action='index', parallel=False, **kwargs):
191191
"""
192192
Update each document in ES for a model, iterable of models or queryset
193193
"""
194-
if refresh is True or (
195-
refresh is None and self.django.auto_refresh
196-
):
197-
kwargs['refresh'] = True
194+
if refresh is not None:
195+
kwargs['refresh'] = refresh
196+
elif self.django.auto_refresh:
197+
kwargs['refresh'] = self.django.auto_refresh
198198

199199
if isinstance(thing, models.Model):
200200
object_list = [thing]

django_elasticsearch_dsl/management/commands/search_index.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ def add_arguments(self, parser):
6464
help='Run populate/rebuild update single threaded'
6565
)
6666
parser.set_defaults(parallel=getattr(settings, 'ELASTICSEARCH_DSL_PARALLEL', False))
67+
parser.add_argument(
68+
'--refresh',
69+
action='store_true',
70+
dest='refresh',
71+
default=None,
72+
help='Refresh indices after populate/rebuild'
73+
)
6774
parser.add_argument(
6875
'--no-count',
6976
action='store_false',
@@ -114,7 +121,7 @@ def _populate(self, models, options):
114121
"(parallel)" if parallel else "")
115122
)
116123
qs = doc().get_indexing_queryset()
117-
doc().update(qs, parallel=parallel)
124+
doc().update(qs, parallel=parallel, refresh=options['refresh'])
118125

119126
def _delete(self, models, options):
120127
index_names = [index._name for index in registry.get_indices(models)]

docs/source/management.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ Populate the Elasticsearch mappings with the django models data (index need to b
1818

1919
::
2020

21-
$ search_index --populate [--models [app[.model] app[.model] ...]] [--parallel]
21+
$ search_index --populate [--models [app[.model] app[.model] ...]] [--parallel] [--refresh]
2222

2323
Recreate and repopulate the indices:
2424

2525
::
2626

27-
$ search_index --rebuild [-f] [--models [app[.model] app[.model] ...]] [--parallel]
27+
$ search_index --rebuild [-f] [--models [app[.model] app[.model] ...]] [--parallel] [--refresh]
2828

docs/source/quickstart.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ It is required to define ``Document`` class in ``documents.py`` in your app dir
8787
# or deleted:
8888
# ignore_signals = True
8989
90-
# Don't perform an index refresh after every update (overrides global setting):
90+
# Configure how the index should be refreshed after an update.
91+
# See Elasticsearch documentation for supported options:
92+
# https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-refresh.html
93+
# This per-Document setting overrides settings.ELASTICSEARCH_DSL_AUTO_REFRESH.
9194
# auto_refresh = False
9295
9396
# Paginate the django queryset used to populate the index with the specified size

tests/test_commands.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,24 @@ def test_create_all_indices(self):
112112

113113
def test_populate_all_doc_type(self):
114114
call_command('search_index', stdout=self.out, action='populate')
115+
expected_kwargs = {'parallel': False, 'refresh': None}
115116
# One call for "Indexing NNN documents", one for indexing itself (via get_index_queryset).
116117
assert self.doc_a1.get_queryset.call_count == 2
117-
self.doc_a1.update.assert_called_once_with(self.doc_a1_qs.iterator(), parallel=False)
118+
self.doc_a1.update.assert_called_once_with(self.doc_a1_qs.iterator(), **expected_kwargs)
118119
assert self.doc_a2.get_queryset.call_count == 2
119-
self.doc_a2.update.assert_called_once_with(self.doc_a2_qs.iterator(), parallel=False)
120+
self.doc_a2.update.assert_called_once_with(self.doc_a2_qs.iterator(), **expected_kwargs)
120121
assert self.doc_b1.get_queryset.call_count == 2
121-
self.doc_b1.update.assert_called_once_with(self.doc_b1_qs.iterator(), parallel=False)
122+
self.doc_b1.update.assert_called_once_with(self.doc_b1_qs.iterator(), **expected_kwargs)
122123
assert self.doc_c1.get_queryset.call_count == 2
123-
self.doc_c1.update.assert_called_once_with(self.doc_c1_qs.iterator(), parallel=False)
124+
self.doc_c1.update.assert_called_once_with(self.doc_c1_qs.iterator(), **expected_kwargs)
125+
126+
def test_populate_all_doc_type_refresh(self):
127+
call_command('search_index', stdout=self.out, action='populate', refresh=True)
128+
expected_kwargs = {'parallel': False, 'refresh': True}
129+
self.doc_a1.update.assert_called_once_with(self.doc_a1_qs.iterator(), **expected_kwargs)
130+
self.doc_a2.update.assert_called_once_with(self.doc_a2_qs.iterator(), **expected_kwargs)
131+
self.doc_b1.update.assert_called_once_with(self.doc_b1_qs.iterator(), **expected_kwargs)
132+
self.doc_c1.update.assert_called_once_with(self.doc_c1_qs.iterator(), **expected_kwargs)
124133

125134
def test_rebuild_indices(self):
126135

tests/test_documents.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,46 @@ def test_model_instance_update_no_refresh(self):
268268
doc.update(car)
269269
self.assertNotIn('refresh', mock.call_args_list[0][1])
270270

271+
def test_model_instance_update_refresh_true(self):
272+
doc = CarDocument()
273+
doc.django.auto_refresh = False
274+
car = Car()
275+
with patch('django_elasticsearch_dsl.documents.bulk') as mock:
276+
doc.update(car, refresh=True)
277+
self.assertEqual(
278+
mock.call_args_list[0][1]['refresh'], True
279+
)
280+
281+
def test_model_instance_update_refresh_wait_for(self):
282+
doc = CarDocument()
283+
doc.django.auto_refresh = False
284+
car = Car()
285+
with patch('django_elasticsearch_dsl.documents.bulk') as mock:
286+
doc.update(car, refresh='wait_for')
287+
self.assertEqual(
288+
mock.call_args_list[0][1]['refresh'], 'wait_for'
289+
)
290+
291+
def test_model_instance_update_auto_refresh_wait_for(self):
292+
doc = CarDocument()
293+
doc.django.auto_refresh = 'wait_for'
294+
car = Car()
295+
with patch('django_elasticsearch_dsl.documents.bulk') as mock:
296+
doc.update(car)
297+
self.assertEqual(
298+
mock.call_args_list[0][1]['refresh'], 'wait_for'
299+
)
300+
301+
def test_model_instance_update_refresh_overrides_auto_refresh(self):
302+
doc = CarDocument()
303+
doc.django.auto_refresh = True
304+
car = Car()
305+
with patch('django_elasticsearch_dsl.documents.bulk') as mock:
306+
doc.update(car, refresh=False)
307+
self.assertEqual(
308+
mock.call_args_list[0][1]['refresh'], False
309+
)
310+
271311
def test_model_instance_iterable_update_with_pagination(self):
272312
class CarDocument2(DocType):
273313
class Django:

0 commit comments

Comments
 (0)