Skip to content

Commit 73111ea

Browse files
committed
feat(commands): handle multiple indices per Document
* Index creation is now done through the Document.init() to allow naming through a given suffix * Index rebuild makes no sense anymore
1 parent 3263608 commit 73111ea

File tree

4 files changed

+68
-78
lines changed

4 files changed

+68
-78
lines changed

django_opensearch_dsl/management/commands/opensearch.py

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
from argparse import ArgumentParser
77
from collections import defaultdict
8+
from datetime import datetime
89
from typing import Any, Callable
910

1011
import opensearchpy
@@ -17,6 +18,7 @@
1718
from django_opensearch_dsl.registries import registry
1819
from ..enums import OpensearchAction
1920
from ..types import parse
21+
from ...documents import Document
2022

2123

2224
class Command(BaseCommand):
@@ -54,19 +56,40 @@ def __list_index(self, **options): # noqa pragma: no cover
5456
indices = registry.get_indices()
5557
result = defaultdict(list)
5658
for index in indices:
57-
module = index._doc_types[0].__module__.split(".")[-2] # noqa
59+
document = index._doc_types[0]
60+
module = document.__module__.split(".")[-2] # noqa
5861
exists = index.exists()
5962
checkbox = f"[{'X' if exists else ' '}]"
60-
count = f" ({index.search().count()} documents)" if exists else ""
61-
result[module].append(f"{checkbox} {index._name}{count}")
63+
document_indices = document.get_all_indices()
64+
if document_indices:
65+
details = ". Following indices exist:"
66+
for document_index in document_indices:
67+
is_active = ""
68+
if document_index.exists_alias(name=index._name):
69+
is_active = self.style.SUCCESS("Active")
70+
count = f" ({document_index.search().count()} documents)"
71+
details += f"\n - {document_index._name} {is_active}{count}"
72+
elif exists:
73+
details = f" ({index.search().count()} documents)"
74+
else:
75+
details = ""
76+
result[module].append(f"{checkbox} {index._name}{details}")
6277
for app, indices in result.items():
6378
self.stdout.write(self.style.MIGRATE_LABEL(app))
6479
self.stdout.write("\n".join(indices))
6580

66-
def _manage_index(self, action, indices, force, verbosity, ignore_error, **options): # noqa
81+
def _manage_index(self, action, indices, suffix, force, verbosity, ignore_error, **options): # noqa
6782
"""Manage the creation and deletion of indices."""
6883
action = OpensearchAction(action)
69-
known = registry.get_indices()
84+
85+
suffix = suffix or datetime.now().strftime("%Y%m%d%H%M%S%f")
86+
87+
if action == OpensearchAction.CREATE:
88+
known = registry.get_indices()
89+
else:
90+
known = []
91+
for document in [i._doc_types[0] for i in registry.get_indices()]:
92+
known.extend(document.get_all_indices())
7093

7194
# Filter indices
7295
if indices:
@@ -84,9 +107,15 @@ def _manage_index(self, action, indices, force, verbosity, ignore_error, **optio
84107

85108
# Display expected action
86109
if verbosity or not force:
110+
if not indices:
111+
self.stdout.write("Nothing to do, exiting")
112+
exit(0)
87113
self.stdout.write(f"The following indices will be {action.past}:")
88114
for index in indices:
89-
self.stdout.write(f"\t- {index._name}.") # noqa
115+
index_name = index._name # noqa
116+
if action == OpensearchAction.CREATE:
117+
index_name += f"{Document.VERSION_NAME_SEPARATOR}{suffix}"
118+
self.stdout.write(f"\t- {index_name}.") # noqa
90119
self.stdout.write("")
91120

92121
# Ask for confirmation to continue
@@ -101,23 +130,21 @@ def _manage_index(self, action, indices, force, verbosity, ignore_error, **optio
101130

102131
pp = action.present_participle.title()
103132
for index in indices:
133+
index_name = index._name # noqa
134+
if action == OpensearchAction.CREATE:
135+
index_name += f"{Document.VERSION_NAME_SEPARATOR}{suffix}"
136+
104137
if verbosity:
105138
self.stdout.write(
106-
f"{pp} index '{index._name}'...\r",
139+
f"{pp} index '{index_name}'...\r",
107140
ending="",
108141
) # noqa
109142
self.stdout.flush()
110143
try:
111144
if action == OpensearchAction.CREATE:
112-
index.create()
145+
index._doc_types[0].init(suffix=suffix)
113146
elif action == OpensearchAction.DELETE:
114147
index.delete()
115-
else:
116-
try:
117-
index.delete()
118-
except opensearchpy.exceptions.NotFoundError:
119-
pass
120-
index.create()
121148
except opensearchpy.exceptions.NotFoundError:
122149
if verbosity or not ignore_error:
123150
self.stderr.write(f"{pp} index '{index._name}'...{self.style.ERROR('Error (not found)')}") # noqa
@@ -127,17 +154,17 @@ def _manage_index(self, action, indices, force, verbosity, ignore_error, **optio
127154
except opensearchpy.exceptions.RequestError:
128155
if verbosity or not ignore_error:
129156
self.stderr.write(
130-
f"{pp} index '{index._name}'... {self.style.ERROR('Error (already exists)')}"
157+
f"{pp} index '{index._name}'... {self.style.ERROR('Error')}"
131158
) # noqa
132159
if not ignore_error:
133160
self.stderr.write("exiting...")
134161
exit(1)
135162
else:
136163
if verbosity:
137-
self.stdout.write(f"{pp} index '{index._name}'... {self.style.SUCCESS('OK')}") # noqa
164+
self.stdout.write(f"{pp} index '{index_name}'... {self.style.SUCCESS('OK')}") # noqa
138165

139166
def _manage_document(
140-
self, action, indices, force, filters, excludes, verbosity, parallel, count, refresh, missing, **options
167+
self, action, indices, index_suffix, force, filters, excludes, verbosity, parallel, count, refresh, missing, **options
141168
): # noqa
142169
"""Manage the creation and deletion of indices."""
143170
action = OpensearchAction(action)
@@ -206,7 +233,9 @@ def _manage_document(
206233
document = index._doc_types[0]() # noqa
207234
qs = document.get_indexing_queryset(stdout=self.stdout._out, verbose=verbosity, action=action, **kwargs)
208235
success, errors = document.update(
209-
qs, parallel=parallel, refresh=refresh, action=action, raise_on_error=False
236+
qs, action,
237+
parallel=parallel, index_suffix=index_suffix,
238+
refresh=refresh, raise_on_error=False
210239
)
211240

212241
success_str = self.style.SUCCESS(success) if success else success
@@ -241,23 +270,23 @@ def add_arguments(self, parser):
241270
)
242271
subparser.set_defaults(func=self.__list_index)
243272

244-
# 'manage' subcommand
273+
# 'index' subcommand
245274
subparser = subparsers.add_parser(
246275
"index",
247-
help="Manage the creation an deletion of indices.",
248-
description="Manage the creation an deletion of indices.",
276+
help="Manage the deletion of indices.",
277+
description="Manage the deletion of indices.",
249278
)
250279
subparser.set_defaults(func=self._manage_index)
251280
subparser.add_argument(
252281
"action",
253282
type=str,
254-
help="Whether you want to create, delete or rebuild the indices.",
283+
help="Whether you want to create or delete the indices.",
255284
choices=[
256285
OpensearchAction.CREATE.value,
257286
OpensearchAction.DELETE.value,
258-
OpensearchAction.REBUILD.value,
259287
],
260288
)
289+
subparser.add_argument("--suffix", type=str, default=None, help="A suffix for the index name to create/delete (if you don't provide one, a timestamp will be used for creation).")
261290
subparser.add_argument("--force", action="store_true", default=False, help="Do not ask for confirmation.")
262291
subparser.add_argument("--ignore-error", action="store_true", default=False, help="Do not stop on error.")
263292
subparser.add_argument(
@@ -279,7 +308,7 @@ def add_arguments(self, parser):
279308
subparser.add_argument(
280309
"action",
281310
type=str,
282-
help="Whether you want to create, delete or rebuild the indices.",
311+
help="Whether you want to index, delete or update documents in indices.",
283312
choices=[
284313
OpensearchAction.INDEX.value,
285314
OpensearchAction.DELETE.value,
@@ -316,6 +345,7 @@ def add_arguments(self, parser):
316345
),
317346
)
318347
subparser.add_argument("--force", action="store_true", default=False, help="Do not ask for confirmation.")
348+
subparser.add_argument("--index-suffix", type=str, default=None, help="The suffix for the index name (if you don't provide one, the current index will be used).")
319349
subparser.add_argument(
320350
"-i", "--indices", type=str, nargs="*", help="Only update documents on the given indices."
321351
)

django_opensearch_dsl/management/enums.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ class OpensearchAction(str, Enum):
77
INDEX = ("index", "indexing", "indexed")
88
UPDATE = ("update", "updating", "updated")
99
CREATE = ("create", "creating", "created")
10-
REBUILD = ("rebuild", "rebuilding", "rebuilded")
1110
LIST = ("list", "listing", "listed")
1211
DELETE = ("delete", "deleting", "deleted")
13-
MANAGE = ("manage", "managing", "managed")
1412

1513
def __new__(cls, value: str, present_participle: str, past: str): # noqa: D102
1614
obj = str.__new__(cls, value)

tests/tests/management/test_document.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import time
44

55
from django.test import TestCase
6+
from opensearch_dsl.connections import get_connection
67

78
from django_dummy_app.commands import call_command
89
from django_dummy_app.documents import CountryDocument, ContinentDocument, EventDocument
910
from django_dummy_app.models import Country, Event, Continent
10-
from django_opensearch_dsl.registries import registry
1111

1212

1313
class DocumentTestCase(TestCase):
@@ -20,9 +20,7 @@ def setUpClass(cls):
2020
cls.call_command = functools.partial(call_command, stdout=devnull, stderr=devnull)
2121

2222
def setUp(self) -> None:
23-
indices = registry.get_indices()
24-
for i in indices:
25-
i.delete(ignore_unavailable=True)
23+
get_connection().indices.delete('_all')
2624

2725
def test_unknown_index(self):
2826
with self.assertRaises(SystemExit):

tests/tests/management/test_index.py

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33

44
from django.test import SimpleTestCase
5+
from opensearch_dsl.connections import get_connection
56

67
from django_dummy_app.commands import call_command
78
from django_dummy_app.documents import CountryDocument, ContinentDocument, EventDocument
@@ -16,15 +17,13 @@ def setUpClass(cls):
1617
cls.call_command = functools.partial(call_command, stdout=devnull, stderr=devnull)
1718

1819
def setUp(self) -> None:
19-
indices = registry.get_indices()
20-
for i in indices:
21-
i.delete(ignore_unavailable=True)
20+
get_connection().indices.delete('_all')
2221

2322
def test_index_creation_all(self):
2423
indices = registry.get_indices()
2524

2625
self.assertFalse(any(map(lambda i: i.exists(), indices)))
27-
self.call_command("opensearch", "index", "create", force=True)
26+
self.call_command("opensearch", "index", "create", suffix="v1", force=True)
2827
self.assertTrue(all(map(lambda i: i.exists(), indices)))
2928

3029
def test_index_creation_one(self):
@@ -60,38 +59,30 @@ def test_index_creation_two(self):
6059
self.assertTrue(country_document._index.exists())
6160
self.assertTrue(event_document._index.exists())
6261

63-
def test_index_creation_error(self):
64-
country_document = CountryDocument()
65-
self.call_command("opensearch", "index", "create", country_document.Index.name, force=True)
66-
67-
self.call_command("opensearch", "index", "create", country_document.Index.name, force=True, ignore_error=True)
68-
with self.assertRaises(SystemExit):
69-
self.call_command("opensearch", "index", "create", country_document.Index.name, force=True)
70-
7162
def test_index_deletion_all(self):
72-
self.call_command("opensearch", "index", "create", force=True)
63+
self.call_command("opensearch", "index", "create", suffix="v1", force=True)
7364
indices = registry.get_indices()
7465

7566
self.assertTrue(all(map(lambda i: i.exists(), indices)))
7667
self.call_command("opensearch", "index", "delete", force=True)
7768
self.assertFalse(any(map(lambda i: i.exists(), indices)))
7869

7970
def test_index_deletion_one(self):
80-
self.call_command("opensearch", "index", "create", force=True)
71+
self.call_command("opensearch", "index", "create", suffix="v1", force=True)
8172
continent_document = ContinentDocument()
8273
country_document = CountryDocument()
8374
event_document = EventDocument()
8475

8576
self.assertTrue(continent_document._index.exists())
8677
self.assertTrue(country_document._index.exists())
8778
self.assertTrue(event_document._index.exists())
88-
self.call_command("opensearch", "index", "delete", country_document.Index.name, force=True)
79+
self.call_command("opensearch", "index", "delete", "country--v1", force=True)
8980
self.assertTrue(continent_document._index.exists())
9081
self.assertFalse(country_document._index.exists())
9182
self.assertTrue(event_document._index.exists())
9283

9384
def test_index_deletion_two(self):
94-
self.call_command("opensearch", "index", "create", force=True)
85+
self.call_command("opensearch", "index", "create", suffix="v1", force=True)
9586
continent_document = ContinentDocument()
9687
country_document = CountryDocument()
9788
event_document = EventDocument()
@@ -103,8 +94,8 @@ def test_index_deletion_two(self):
10394
"opensearch",
10495
"index",
10596
"delete",
106-
country_document.Index.name,
107-
event_document.Index.name,
97+
"country--v1",
98+
"event--v1",
10899
force=True,
109100
)
110101
self.assertTrue(continent_document._index.exists())
@@ -113,38 +104,11 @@ def test_index_deletion_two(self):
113104

114105
def test_index_deletion_error(self):
115106
country_document = CountryDocument()
107+
country_document.init(suffix="v1")
116108

117-
self.call_command("opensearch", "index", "delete", country_document.Index.name, force=True, ignore_error=True)
109+
self.call_command("opensearch", "index", "delete", "country--v1", force=True, ignore_error=True)
118110
with self.assertRaises(SystemExit):
119-
self.call_command("opensearch", "index", "delete", country_document.Index.name, force=True)
120-
121-
def test_index_rebuild_two(self):
122-
continent_document = ContinentDocument()
123-
country_document = CountryDocument()
124-
event_document = EventDocument()
125-
self.call_command(
126-
"opensearch",
127-
"index",
128-
"create",
129-
continent_document.Index.name,
130-
event_document.Index.name,
131-
force=True,
132-
)
133-
134-
self.assertTrue(continent_document._index.exists())
135-
self.assertFalse(country_document._index.exists())
136-
self.assertTrue(event_document._index.exists())
137-
self.call_command(
138-
"opensearch",
139-
"index",
140-
"rebuild",
141-
country_document.Index.name,
142-
event_document.Index.name,
143-
force=True,
144-
)
145-
self.assertTrue(continent_document._index.exists())
146-
self.assertTrue(country_document._index.exists())
147-
self.assertTrue(event_document._index.exists())
111+
self.call_command("opensearch", "index", "delete", "country--v1", force=True)
148112

149113
def test_unknown_index(self):
150114
with self.assertRaises(SystemExit):

0 commit comments

Comments
 (0)