Skip to content

Commit d7e0483

Browse files
committed
fix: index settings inherited, not mappings
1 parent e18f029 commit d7e0483

4 files changed

Lines changed: 64 additions & 50 deletions

File tree

elasticsearch_metrics/imps/elastic8.py

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
120120
)
121121
return _cls
122122

123+
@classmethod
124+
def construct_index(cls, opts, bases):
125+
"""
126+
Override IndexMeta.construct_index so a new Index is created for each class
127+
and Index.settings, Index.analyzers, and Index.using are inherited
128+
(but not Index.name or Index.aliases)
129+
"""
130+
_base_index_configs = collections.ChainMap(
131+
*(base._index.to_dict() for base in bases if hasattr(base, "_index"))
132+
)
133+
_index_opts = opts or type("Index", (), {})
134+
if not hasattr(_index_opts, "settings") and (
135+
_inherited_settings := _base_index_configs.get("settings")
136+
):
137+
_index_opts.settings = _inherited_settings
138+
if not hasattr(_index_opts, "analyzers") and (
139+
_inherited_analyzers := _base_index_configs.get("analyzers")
140+
):
141+
_index_opts.analyzers = _inherited_analyzers
142+
if not hasattr(_index_opts, "using"):
143+
_inherited_using = _base_index_configs.get("using")
144+
if _inherited_using and (_inherited_using != "default"):
145+
_index_opts.using = _inherited_using
146+
assert _index_opts is not None # guarantee separate index per class
147+
return super().construct_index(_index_opts, bases)
148+
123149
def _get_meta_attr(self, attr_name: str, default: typing.Any = None) -> typing.Any:
124150
_meta = getattr(self, "Meta", None)
125151
return getattr(_meta, attr_name, default)
@@ -189,6 +215,7 @@ def record(
189215
to use to save, or `False` to skip saving (e.g. for use in a bulk operation)
190216
all other kwargs passed thru to the class constructor
191217
"""
218+
assert not cls.is_abstract
192219
_instance = cls(**kwargs)
193220
if using is not False:
194221
_instance.save(using=using)
@@ -241,6 +268,7 @@ def save(
241268
- populate document id based on UNIQUE_TOGETHER_FIELDS
242269
- send pre_save/post_save signals
243270
"""
271+
assert not self.__class__.is_abstract
244272
self.require_been_setup(using=using) # prevent automapped indexes
245273
self._populate_unique_id()
246274
signals.pre_save.send(self.__class__, instance=self, using=using, index=index)
@@ -276,31 +304,34 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
276304
"""
277305
extend _DjelmeRecordMetaclass.__new__ to set index name by app label and type name
278306
"""
279-
_index = attrs.get("Index")
280-
if not _index:
281-
_index = type("Index", (), {})
282-
attrs["Index"] = _index
283-
if not getattr(_index, "name", None):
284-
_app_label = find_app_label_for_module(attrs["__module__"])
285-
_index.name = f"{_app_label.lower()}_{name.lower()}"
286-
return super().__new__(mcls, name, bases, attrs)
307+
_cls = super().__new__(mcls, name, bases, attrs)
308+
if not _cls.is_abstract:
309+
# set `_index._name`, if not already specified
310+
_index_name = _cls._index._name
311+
if not _index_name or (_index_name in ("*", "default")):
312+
_app_label = find_app_label_for_module(attrs["__module__"])
313+
_index_name = f"{_app_label.lower()}_{_cls.__name__.lower()}"
314+
_prefix = _cls.get_index_name_prefix()
315+
if not _index_name.startswith(_prefix):
316+
_index_name = f"{_prefix}{_index_name}"
317+
_cls._index._name = _index_name
318+
# set `_index._using`, so `_index` can be used normally
319+
_backend = djelme_registry.get_backend_for_recordtype(_cls)
320+
_cls._index._using = _backend._elastic8dsl_connection_name
321+
return _cls
287322

288323

289324
class SimpleRecord(BaseDjelmeRecord, metaclass=_SimpleRecordMetaclass):
290-
def __init_subclass__(cls, **kwargs):
291-
super().__init_subclass__(**kwargs)
292-
if not cls.is_abstract:
293-
_prefix = cls.get_index_name_prefix()
294-
_name = cls._index._name
295-
if not _name.startswith(_prefix):
296-
cls._index._name = f"{_prefix}{_name}"
325+
class Meta:
326+
abstract = True
297327

298328
@classmethod
299329
def djelme_index_name(cls) -> str:
300330
"""the index name for this record type
301331
302332
for ProtoDjelmeRecord
303333
"""
334+
assert not cls.is_abstract
304335
return cls._index._name
305336

306337
@classmethod
@@ -311,6 +342,7 @@ def check_djelme_setup(cls, using: str | None = None) -> None:
311342
:raise: IndexOutOfSyncError if mappings are out of sync.
312343
:return: if index exists and mappings are in sync.
313344
"""
345+
assert not cls.is_abstract
314346
_client = cls._get_connection(using)
315347
_index_name = cls.djelme_index_name()
316348
try:
@@ -330,10 +362,13 @@ def check_djelme_setup(cls, using: str | None = None) -> None:
330362

331363
@classmethod
332364
def do_teardown(cls, es_client):
365+
assert not cls.is_abstract
333366
cls._index.delete(using=es_client, ignore_unavailable=True)
334367

335-
class Meta:
336-
abstract = True
368+
@classmethod
369+
def refresh(cls, using: str | None = None) -> None:
370+
assert not cls.is_abstract
371+
cls._index.refresh(using=using)
337372

338373

339374
class TimeseriesRecord(BaseDjelmeRecord):
@@ -366,6 +401,7 @@ def search(
366401
) -> esdsl.Search[
367402
"typing.Self"
368403
]: # typing.Self added in py 3.11 -- str annotation until 3.10 eol
404+
assert not cls.is_abstract
369405
return super().search(
370406
using=using,
371407
index=(index or cls.format_timeseries_index_pattern()),
@@ -389,7 +425,8 @@ def search_timeseries_range(
389425
return cls.search(index=_index_pattern).filter(_timeseries_q)
390426

391427
@classmethod
392-
def refresh_timeseries_indexes(cls, using: str | None = None) -> None:
428+
def refresh(cls, using: str | None = None) -> None:
429+
assert not cls.is_abstract
393430
cls._get_connection(using).indices.refresh(
394431
index=cls.format_timeseries_index_pattern()
395432
)
@@ -408,6 +445,7 @@ def each_timeseries_index(
408445

409446
@classmethod
410447
def do_teardown(cls, es8_client: Elastic8Client) -> None:
448+
assert not cls.is_abstract
411449
_indexname_wildcard = cls.format_timeseries_index_pattern()
412450
_indices = es8_client.indices.get(index=_indexname_wildcard, features=",")
413451
for _index_name in _indices.keys():
@@ -420,6 +458,7 @@ def do_teardown(cls, es8_client: Elastic8Client) -> None:
420458

421459
@classmethod
422460
def get_timeseries_template(cls) -> esdsl.ComposableIndexTemplate:
461+
assert not cls.is_abstract
423462
return cls._index.as_composable_template(
424463
template_name=cls.get_timeseries_template_name(),
425464
pattern=cls.format_timeseries_index_pattern(),

elasticsearch_metrics/tests/dummy8app/metrics.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
class Dummy8Event(djelme.EventRecord):
1111
intensity: int
1212

13-
class Index:
14-
using = "my_elastic8_events"
15-
1613

1714
class Monthly8Event(djelme.EventRecord):
1815
intenzity: int
@@ -22,9 +19,6 @@ class Meta:
2219
timeseries_recordtype_name = "eventlog"
2320
timedepth = 2
2421

25-
class Index:
26-
using = "my_elastic8_events"
27-
2822

2923
class ThingHappened(djelme.EventRecord):
3024
thing_id: str = ""
@@ -36,7 +30,6 @@ class ThingHappened(djelme.EventRecord):
3630

3731
class Index:
3832
settings = {"refresh_interval": "-1"}
39-
using = "my_elastic8_events"
4033

4134
class Meta:
4235
timeseries_recordtype_name = "happen"
@@ -50,9 +43,6 @@ class ThingHappeningsReport(djelme.CyclicRecord):
5043
thing_id: str
5144
happen_count: int
5245

53-
class Index:
54-
using = "my_elastic8_reports"
55-
5646
class Meta:
5747
index_name_prefix = "blarg_"
5848
timedepth = 2 # monthly timeseries indexes
@@ -62,6 +52,3 @@ class SimpleKV(djelme.SimpleRecord):
6252
UNIQUE_TOGETHER_FIELDS = ("key",)
6353
key: str
6454
val: int
65-
66-
class Index:
67-
using = "my_elastic8_reports"

elasticsearch_metrics/tests/settings.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@
1919
"hosts": os.environ.get("ELASTICSEARCH6_URL", ""),
2020
},
2121
},
22-
"my_elastic8_events": {
23-
"elasticsearch_metrics.imps.elastic8": {
24-
"hosts": os.environ.get("ELASTICSEARCH8_URL", ""),
25-
},
26-
},
27-
"my_elastic8_reports": {
22+
"my_elastic8": {
2823
"elasticsearch_metrics.imps.elastic8": {
2924
"hosts": os.environ.get("ELASTICSEARCH8_URL", ""),
3025
},

elasticsearch_metrics/tests/test_imps_elastic8.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
def _es8_client(
33-
backend_name: str = "my_elastic8_events",
33+
backend_name: str = "my_elastic8",
3434
) -> elasticsearch8.Elasticsearch:
3535
_backend = djelme_registry.get_backend(backend_name)
3636
assert isinstance(_backend, djelme.DjelmeElastic8Backend)
@@ -224,9 +224,6 @@ def test_get_index_template_default_template_name(self):
224224

225225
def test_get_index_template_uses_app_label_in_class_meta(self):
226226
class MyRecord(djelme.TimeseriesRecord):
227-
class Index:
228-
using = "my_elastic8_events"
229-
230227
class Meta:
231228
app_label = "myapp"
232229

@@ -248,7 +245,6 @@ class MyBaseRecord(djelme.TimeseriesRecord):
248245

249246
class Index:
250247
settings = {"number_of_shards": 2}
251-
using = "my_elastic8_events"
252248

253249
class Meta:
254250
abstract = True
@@ -263,9 +259,6 @@ class Meta:
263259

264260
def test_source_may_be_enabled(self):
265261
class MyRecord(djelme.TimeseriesRecord):
266-
class Index:
267-
using = "my_elastic8_events"
268-
269262
class Meta:
270263
app_label = "dummy8app"
271264
timeseries_recordtype_name = "myrecord"
@@ -475,7 +468,7 @@ def setUp(self):
475468
Dummy8Event.record(timestamp=dt.datetime(1235, 5, 6), intensity=111)
476469
Dummy8Event.record(timestamp=dt.datetime(2345, 6, 9), intensity=11)
477470
Dummy8Event.record(timestamp=dt.datetime(2345, 7, 9), intensity=13)
478-
Dummy8Event.refresh_timeseries_indexes()
471+
Dummy8Event.refresh()
479472

480473
def test_indexes(self):
481474
_index_names = {
@@ -537,7 +530,7 @@ def setUp(self):
537530
Monthly8Event.record(timestamp=dt.datetime(1235, 5, 6), intenzity=111)
538531
Monthly8Event.record(timestamp=dt.datetime(2345, 6, 9), intenzity=11)
539532
Monthly8Event.record(timestamp=dt.datetime(2345, 7, 9), intenzity=13)
540-
Monthly8Event.refresh_timeseries_indexes()
533+
Monthly8Event.refresh()
541534

542535
def test_indexes(self):
543536
_index_names = {
@@ -598,7 +591,7 @@ def setUp(self):
598591
ThingHappened.record(timestamp=dt.datetime(1235, 5, 6), thing_id="e")
599592
ThingHappened.record(timestamp=dt.datetime(2345, 6, 9), thing_id="f")
600593
ThingHappened.record(timestamp=dt.datetime(2345, 7, 9), thing_id="g")
601-
ThingHappened.refresh_timeseries_indexes()
594+
ThingHappened.refresh()
602595

603596
def test_indexes(self):
604597
_index_names = {
@@ -671,7 +664,7 @@ def setUp(self):
671664
ThingHappeningsReport.record(
672665
cycle_coverage="2000.2", thing_id="c", happen_count=7
673666
)
674-
ThingHappeningsReport.refresh_timeseries_indexes()
667+
ThingHappeningsReport.refresh()
675668

676669
def test_indexes(self):
677670
_index_names = {
@@ -718,7 +711,7 @@ def setUp(self):
718711
super().setUp()
719712
SimpleKV.record(key="hello", val=2)
720713
SimpleKV.record(key="goodbye", val=-2)
721-
SimpleKV._index.refresh()
714+
SimpleKV.refresh()
722715

723716
def test_search(self):
724717
(_hello,) = SimpleKV.search().query({"term": {"key": "hello"}}).execute()

0 commit comments

Comments
 (0)