lemon-core 3.2.8 → 3.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
18
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
19
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
20
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
21
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
22
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
23
+ function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
24
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
25
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
26
+ function fulfill(value) { resume("next", value); }
27
+ function reject(value) { resume("throw", value); }
28
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
29
+ };
11
30
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
32
  };
14
33
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.DummyElastic6Service = exports.$ERROR = exports.Elastic6Service = exports.$hangul = exports.elasticsearch = void 0;
34
+ exports.DummyElastic6Service = exports.$ERROR = exports.Elastic6Service = exports.ElasticIndexService = exports.$hangul = exports.elasticsearch = void 0;
16
35
  /**
17
36
  * `elastic6-service.ts`
18
37
  * - common service for elastic-search v6
@@ -32,32 +51,35 @@ const hangul_service_1 = __importDefault(require("./hangul-service"));
32
51
  exports.$hangul = hangul_service_1.default;
33
52
  const tools_1 = require("../../tools");
34
53
  const test_helper_1 = require("../../common/test-helper");
54
+ const fs_1 = __importDefault(require("fs"));
55
+ const path_1 = __importDefault(require("path"));
35
56
  const NS = engine_1.$U.NS('ES6', 'green'); // NAMESPACE TO BE PRINTED.
36
57
  /**
37
58
  * convert to string.
38
59
  */
39
60
  const _S = (v, def = '') => typeof v === 'string' ? v : v === undefined || v === null ? def : typeof v === 'object' ? engine_1.$U.json(v) : `${v}`;
61
+ /** ****************************************************************************************************************
62
+ * Elastic Index Service
63
+ ** ****************************************************************************************************************/
40
64
  /**
41
- * class: `Elastic6Service`
42
- * - basic CRUD service for Elastic Search 6
65
+ * abstarct class: `ElasticIndexService`
66
+ * - abstract class for basic Elasticsearch CRUD operations
67
+ * - common operations that are shared across different versions.
68
+ * TODO - support `Elastic` and `OpenSearch`
43
69
  */
44
- class Elastic6Service {
70
+ class ElasticIndexService {
45
71
  /**
46
72
  * default constuctor w/ options.
47
73
  * @param options { endpoint, indexName } is required.
48
74
  */
49
75
  constructor(options) {
50
- /**
51
- * say hello of identity.
52
- */
53
- this.hello = () => `elastic6-service:${this.options.indexName}:${this.version}`;
54
- (0, engine_1._inf)(NS, `Elastic6Service(${options.indexName}/${options.idName})...`);
76
+ (0, engine_1._inf)(NS, `ElasticIndexService(${options.indexName}/${options.idName})...`);
55
77
  if (!options.endpoint)
56
78
  throw new Error('.endpoint (URL) is required');
57
79
  if (!options.indexName)
58
80
  throw new Error('.indexName (string) is required');
59
81
  // default option values: docType='_doc', idName='$id'
60
- const { client } = Elastic6Service.instance(options.endpoint);
82
+ const { client } = ElasticIndexService.instance(options.endpoint);
61
83
  this._options = Object.assign({ docType: '_doc', idName: '$id', version: '6.8' }, options);
62
84
  this._client = client;
63
85
  }
@@ -65,7 +87,7 @@ class Elastic6Service {
65
87
  * simple instance maker.
66
88
  *
67
89
  * ```js
68
- * const { client } = Elastic6Service.instance(endpoint);
90
+ * const { client } = ElasticIndexService.instance(endpoint);
69
91
  * ```
70
92
  *
71
93
  * @param endpoint service-url
@@ -93,18 +115,157 @@ class Elastic6Service {
93
115
  get options() {
94
116
  return this._options;
95
117
  }
118
+ /**
119
+ * get the version from options
120
+ */
96
121
  get version() {
97
122
  const ver = engine_1.$U.F(this.options.version, 6.8);
98
123
  return ver;
99
124
  }
125
+ }
126
+ exports.ElasticIndexService = ElasticIndexService;
127
+ /**
128
+ * class: `Elastic6Service`
129
+ * - extends `ElasticIndexService` and adds version-specific implementation
130
+ */
131
+ class Elastic6Service extends ElasticIndexService {
132
+ /**
133
+ * default constuctor w/ options.
134
+ * @param options { endpoint, indexName } is required.
135
+ */
136
+ constructor(options) {
137
+ super(options);
138
+ /**
139
+ * say hello of identity.
140
+ */
141
+ this.hello = () => `elastic6-service:${this.options.indexName}:${this.options.version}`;
142
+ (0, engine_1._inf)('Elastic6Service', `Elastic6Service(${options.indexName}/${options.idName})...`);
143
+ }
144
+ /**
145
+ * get isOldES6
146
+ * - used when setting doctype
147
+ * - used when verifying mismatched error and results of search
148
+ */
149
+ get isOldES6() {
150
+ return this.parsedVersion.major < 7 && this.parsedVersion.engine === 'es';
151
+ }
152
+ /**
153
+ * get isOldES71
154
+ * - used when verifying mismatched error
155
+ */
156
+ get isOldES71() {
157
+ return this.parsedVersion.major == 7 && this.parsedVersion.minor == 1 && this.parsedVersion.engine === 'es';
158
+ }
159
+ /**
160
+ * get isLatestOS2
161
+ * - used when verifying results of search
162
+ */
163
+ get isLatestOS2() {
164
+ return this.parsedVersion.major >= 2 && this.parsedVersion.engine === 'os';
165
+ }
166
+ /**
167
+ * get the parsedVersion
168
+ */
169
+ get parsedVersion() {
170
+ return this.parseVersion(this.options.version);
171
+ }
172
+ /**
173
+ * get the root version from client
174
+ *
175
+ * @protected only for internal test.
176
+ */
177
+ getVersion(options) {
178
+ var _a, _b, _c;
179
+ return __awaiter(this, void 0, void 0, function* () {
180
+ const isDump = (_a = options === null || options === void 0 ? void 0 : options.dump) !== null && _a !== void 0 ? _a : false;
181
+ // it consumes about >20ms
182
+ const info = yield this.client.info();
183
+ const rootVersion = engine_1.$U.S(info.body.version.number);
184
+ const parsedVersion = this.parseVersion(rootVersion, { throwable: true });
185
+ if (isDump) {
186
+ //* save into `info.json`.
187
+ const description = Object.assign({ '!': `${(_b = this.parsedVersion) === null || _b === void 0 ? void 0 : _b.engine}${this.options.version} client info` }, info);
188
+ const filePath = path_1.default.resolve(__dirname, `../../../data/samples/${(_c = this.parsedVersion) === null || _c === void 0 ? void 0 : _c.engine}${this.options.version}/info.json`);
189
+ yield this.saveInfoToFile(description, filePath);
190
+ }
191
+ return parsedVersion;
192
+ });
193
+ }
194
+ /**
195
+ * check whether the service version matches the version provided in the options.
196
+ *
197
+ * @protected only for internal test.
198
+ */
199
+ executeSelfTest() {
200
+ return __awaiter(this, void 0, void 0, function* () {
201
+ // STEP.1 read the parsed-version.
202
+ const optionVersion = this.parsedVersion;
203
+ // STEP.2 get the real version via `getVersion()`
204
+ const rootVersion = yield this.getVersion();
205
+ // STEP.3 validate version
206
+ const isEqual = optionVersion.engine === rootVersion.engine &&
207
+ optionVersion.major === rootVersion.major &&
208
+ optionVersion.minor === rootVersion.minor;
209
+ // Return the comparison result
210
+ return {
211
+ isEqual: isEqual,
212
+ optionVersion: optionVersion,
213
+ rootVersion: rootVersion,
214
+ };
215
+ });
216
+ }
217
+ /**
218
+ * parse version according to Semantic Versioning (SemVer) rules.
219
+ *
220
+ * @param version The version string to parse (e.g., "1.2.3", "1.2.3-alpha.1", "1.2.3+build.001").
221
+ * @param options Optional configuration for throwable behavior.
222
+ * @returns A ParsedVersion object or null if parsing fails and throwable is false.
223
+ */
224
+ parseVersion(version, options) {
225
+ var _a;
226
+ const isThrowable = (_a = options === null || options === void 0 ? void 0 : options.throwable) !== null && _a !== void 0 ? _a : true;
227
+ if (!version && isThrowable)
228
+ throw new Error(`@version (string) is required!`);
229
+ // RegEx to match Semantic Versioning patterns
230
+ const match = version === null || version === void 0 ? void 0 : version.match(/^(\d{1,2})(?:\.(\d{1,2}))?(?:\.(\d{1,2}))?(?:-([a-zA-Z0-9-.]+))?(?:\+([a-zA-Z0-9-.]+))?$/);
231
+ if (!match) {
232
+ if (isThrowable)
233
+ throw new Error(`@version[${version}] is invalid - fail to parse`);
234
+ return null;
235
+ }
236
+ const res = Object.assign(Object.assign({ engine: engine_1.$U.N(match[1], 10) < 6 ? 'os' : 'es', major: engine_1.$U.N(match[1], 10), minor: match[2] !== undefined ? engine_1.$U.N(match[2], 10) : 0, patch: match[3] !== undefined ? engine_1.$U.N(match[3], 10) : 0 }, (match[4] !== undefined ? { prerelease: match[4] } : {})), (match[5] !== undefined ? { build: match[5] } : {}));
237
+ return res;
238
+ }
239
+ /**
240
+ * save info to a JSON file.
241
+ * @param info - The information to be saved
242
+ * @param filePath - The file path where should be saved.
243
+ */
244
+ saveInfoToFile(info, filePath) {
245
+ return __awaiter(this, void 0, void 0, function* () {
246
+ try {
247
+ const directory = path_1.default.dirname(filePath);
248
+ // check whether directory exists
249
+ if (!fs_1.default.existsSync(directory)) {
250
+ fs_1.default.mkdirSync(directory, { recursive: true });
251
+ }
252
+ // write info to file
253
+ fs_1.default.writeFileSync(filePath, JSON.stringify(info, null, 2));
254
+ }
255
+ catch (_a) {
256
+ exports.$ERROR.handler('saveIntoFile', e => {
257
+ throw e;
258
+ });
259
+ }
260
+ });
261
+ }
100
262
  /**
101
263
  * list of index
102
264
  */
103
265
  listIndices() {
104
266
  return __awaiter(this, void 0, void 0, function* () {
105
267
  (0, engine_1._log)(NS, `- listIndices()`);
106
- //! call create index..
107
- // const { client } = instance(endpoint);
268
+ //* prepare client..
108
269
  const client = this.client;
109
270
  const res = yield client.cat.indices({ format: 'json' });
110
271
  (0, engine_1._log)(NS, `> indices =`, engine_1.$U.json(res));
@@ -125,12 +286,35 @@ class Elastic6Service {
125
286
  priStoreSize: _S(N['pri.store.size']),
126
287
  storeSize: _S(N['store.size']),
127
288
  }));
128
- //! returns.
129
289
  return { list };
130
290
  });
131
291
  }
292
+ /**
293
+ * get mapping of an index
294
+ * @param indexName - name of the index
295
+ */
296
+ getIndexMapping() {
297
+ var _a;
298
+ return __awaiter(this, void 0, void 0, function* () {
299
+ const client = this.client;
300
+ const indexName = this.options.indexName;
301
+ const res = yield client.indices.getMapping({ index: indexName }).catch(
302
+ // $ERROR.throwAsJson,
303
+ exports.$ERROR.handler('getMapping', e => {
304
+ const msg = (0, test_helper_1.GETERR)(e);
305
+ if (msg.startsWith('404 INDEX NOT FOUND'))
306
+ throw new Error(`404 NOT FOUND - index:${indexName}`);
307
+ throw e;
308
+ }));
309
+ const mapping = (res === null || res === void 0 ? void 0 : res.body) ? (_a = res.body[indexName]) === null || _a === void 0 ? void 0 : _a.mappings : null;
310
+ if (!mapping)
311
+ throw new Error(`@indexName[${indexName}] is not found - ${engine_1.$U.json(res)}!`);
312
+ return mapping;
313
+ });
314
+ }
132
315
  /**
133
316
  * find the index by name
317
+ * @param indexName - name of the index
134
318
  */
135
319
  findIndex(indexName) {
136
320
  return __awaiter(this, void 0, void 0, function* () {
@@ -143,8 +327,7 @@ class Elastic6Service {
143
327
  }
144
328
  /**
145
329
  * create index by name
146
- *
147
- * @param settings creating settings
330
+ * @param settings - creating settings
148
331
  */
149
332
  createIndex(settings) {
150
333
  return __awaiter(this, void 0, void 0, function* () {
@@ -153,14 +336,13 @@ class Elastic6Service {
153
336
  if (!indexName)
154
337
  new Error('@index is required!');
155
338
  (0, engine_1._log)(NS, `- createIndex(${indexName})`);
156
- //! prepare payload
339
+ //* prepare payload
157
340
  const payload = Object.assign({ settings: {
158
341
  number_of_shards: 5,
159
342
  number_of_replicas: 1,
160
343
  } }, settings);
161
344
  (0, engine_1._log)(NS, `> settings[${indexName}] = `, engine_1.$U.json(payload));
162
- //! call create index..
163
- // const { client } = instance(endpoint);
345
+ //* call create index..
164
346
  const client = this.client;
165
347
  const res = yield client.indices.create({ index: indexName, body: payload }).catch(
166
348
  // $ERROR.throwAsJson,
@@ -172,7 +354,7 @@ class Elastic6Service {
172
354
  }));
173
355
  // if (res) throw res;
174
356
  (0, engine_1._log)(NS, `> create[${indexName}] =`, engine_1.$U.json(Object.assign(Object.assign({}, res), { meta: undefined })));
175
- //! build result.
357
+ //* build result.
176
358
  return {
177
359
  status: res.statusCode,
178
360
  index: indexName,
@@ -189,7 +371,7 @@ class Elastic6Service {
189
371
  if (!indexName)
190
372
  new Error('@index is required!');
191
373
  (0, engine_1._log)(NS, `- destroyIndex(${indexName})`);
192
- //! call create index..
374
+ //* call destroy index..
193
375
  // const { client } = instance(endpoint);
194
376
  const client = this.client;
195
377
  const res = yield client.indices.delete({ index: indexName }).catch(
@@ -218,7 +400,7 @@ class Elastic6Service {
218
400
  if (!indexName)
219
401
  throw new Error('.indexName is required!');
220
402
  (0, engine_1._log)(NS, `- refreshIndex(${indexName})`);
221
- //! call refresh index..
403
+ //* call refresh index..
222
404
  // const { client } = instance(endpoint);
223
405
  const client = this.client;
224
406
  const res = yield client.indices.refresh({ index: indexName }).catch(
@@ -242,7 +424,7 @@ class Elastic6Service {
242
424
  if (!indexName)
243
425
  throw new Error('.indexName is required!');
244
426
  (0, engine_1._log)(NS, `- flushIndex(${indexName})`);
245
- //! call flush index..
427
+ //* call flush index..
246
428
  // const { client } = instance(endpoint);
247
429
  const client = this.client;
248
430
  const res = yield client.indices.flush({ index: indexName }).catch(
@@ -263,9 +445,9 @@ class Elastic6Service {
263
445
  describe() {
264
446
  return __awaiter(this, void 0, void 0, function* () {
265
447
  const { indexName } = this.options;
266
- //! call create index..
448
+ //* call create index..
267
449
  (0, engine_1._log)(NS, `- describe(${indexName})`);
268
- //! read settings.
450
+ //* read settings.
269
451
  // const { client } = instance(endpoint);
270
452
  const client = this.client;
271
453
  const res = yield client.indices.getSettings({ index: indexName }).catch(
@@ -280,20 +462,20 @@ class Elastic6Service {
280
462
  const settings = (res.body && res.body[indexName] && res.body[indexName].settings) || {};
281
463
  (0, engine_1._log)(NS, `> number_of_shards =`, settings.index && settings.index.number_of_shards); // 5
282
464
  (0, engine_1._log)(NS, `> number_of_replicas =`, settings.index && settings.index.number_of_replicas); // 1
283
- //! read mappings.
465
+ //* read mappings.
284
466
  const res2 = yield client.indices.getMapping({ index: indexName });
285
467
  (0, engine_1._log)(NS, `> mappings[${indexName}] =`, engine_1.$U.json(res2));
286
468
  const mappings = (res2.body && res2.body[indexName] && res2.body[indexName].mappings) || {};
287
- //! returns
469
+ //* returns
288
470
  return { settings, mappings };
289
471
  });
290
472
  }
291
473
  /**
292
474
  * save single item
293
475
  *
294
- * @param id id
295
- * @param item item to save
296
- * @param type document type (default: doc-type given at construction time)
476
+ * @param id - id
477
+ * @param item - item to save
478
+ * @param type - document type (default: doc-type given at construction time)
297
479
  */
298
480
  saveItem(id, item, type) {
299
481
  var _a, _b;
@@ -306,7 +488,11 @@ class Elastic6Service {
306
488
  const body = Object.assign(Object.assign({}, item), { [idName]: id });
307
489
  const body2 = this.popullateAutocompleteFields(body);
308
490
  type = `${type || docType}`;
309
- const params = { index: indexName, type, id, body: body2 };
491
+ const params = { index: indexName, id, body: body2 };
492
+ // check version to include 'type' in params
493
+ if (this.isOldES6) {
494
+ params.type = type;
495
+ }
310
496
  if (idName === '_id')
311
497
  delete params.body[idName]; //WARN! `_id` is reserved in ES6.
312
498
  (0, engine_1._log)(NS, `> params[${id}] =`, engine_1.$U.json(params));
@@ -315,12 +501,14 @@ class Elastic6Service {
315
501
  // $ERROR.throwAsJson,
316
502
  exports.$ERROR.handler('save', e => {
317
503
  const msg = (0, test_helper_1.GETERR)(e);
318
- //! try to update document..
504
+ //* try to overwrite document..
319
505
  if (msg.startsWith('409 VERSION CONFLICT ENGINE')) {
320
- delete body2[idName]; // do set id while update
506
+ // delete body2[idName]; // do set id while update
321
507
  // return this.updateItem(id, body2);
322
- const param2 = { index: indexName, type, id, body: { doc: body2 } };
323
- return client.update(param2);
508
+ const param2 = { index: indexName, id, body: Object.assign({}, body2) };
509
+ if (this.isOldES6)
510
+ param2.type = type;
511
+ return client.index(param2);
324
512
  }
325
513
  throw e;
326
514
  }));
@@ -334,7 +522,8 @@ class Elastic6Service {
334
522
  /**
335
523
  * push item for time-series data.
336
524
  *
337
- * @param item item to push
525
+ * @param item - item to push
526
+ * @param type - document type (default: doc-type given at construction time)
338
527
  */
339
528
  pushItem(item, type) {
340
529
  var _a, _b;
@@ -367,8 +556,8 @@ class Elastic6Service {
367
556
  /**
368
557
  * read item with projections
369
558
  *
370
- * @param id item-id
371
- * @param views projections
559
+ * @param id - item-id
560
+ * @param views - projections
372
561
  */
373
562
  readItem(id, views) {
374
563
  var _a, _b, _c;
@@ -394,6 +583,8 @@ class Elastic6Service {
394
583
  const msg = (0, test_helper_1.GETERR)(e);
395
584
  if (msg.startsWith('404 NOT FOUND'))
396
585
  throw new Error(`404 NOT FOUND - id:${id}`);
586
+ if (msg.startsWith('404 INDEX NOT FOUND'))
587
+ throw new Error(`404 NOT FOUND - index:${indexName}`);
397
588
  throw e;
398
589
  }));
399
590
  (0, engine_1._log)(NS, `> read[${id}].res =`, engine_1.$U.json(Object.assign(Object.assign({}, res), { meta: undefined })));
@@ -410,7 +601,7 @@ class Elastic6Service {
410
601
  /**
411
602
  * delete item with projections
412
603
  *
413
- * @param id item-id
604
+ * @param id - item-id
414
605
  */
415
606
  deleteItem(id) {
416
607
  var _a, _b, _c, _d;
@@ -426,6 +617,8 @@ class Elastic6Service {
426
617
  const msg = (0, test_helper_1.GETERR)(e);
427
618
  if (msg.startsWith('404 NOT FOUND'))
428
619
  throw new Error(`404 NOT FOUND - id:${id}`);
620
+ if (msg.startsWith('404 INDEX NOT FOUND'))
621
+ throw new Error(`404 NOT FOUND - index:${indexName}`);
429
622
  throw e;
430
623
  }));
431
624
  // {"_index":"test-v3","_type":"_doc","_id":"aaa","_version":3,"result":"deleted","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":4,"_primary_term":1}
@@ -438,11 +631,17 @@ class Elastic6Service {
438
631
  });
439
632
  }
440
633
  /**
441
- * update item
634
+ * update item (throw if not exist)
635
+ * `update table set a=1, b=b+2 where id='a1'`
636
+ * 0. no of `a1` -> 1,2 (created)
637
+ * 1. a,b := 10,20 -> 11,22
638
+ * 2. a,b := 10,null -> 11,2 (upsert)
639
+ * 3. a,b := null,20 -> 1,22
442
640
  *
443
- * @param id item-id
444
- * @param item item to update
445
- * @param options (optional) request option of client.
641
+ * @param id - item-id
642
+ * @param item - item to update
643
+ * @param increments - item to increase
644
+ * @param options - (optional) request option of client.
446
645
  */
447
646
  updateItem(id, item, increments, options) {
448
647
  return __awaiter(this, void 0, void 0, function* () {
@@ -450,29 +649,55 @@ class Elastic6Service {
450
649
  const type = `${docType}`;
451
650
  (0, engine_1._log)(NS, `- updateItem(${id})`);
452
651
  item = !item && increments ? undefined : item;
453
- //! prepare params.
454
- const params = { index: indexName, type, id, body: { doc: item } };
455
- const version = this.version;
652
+ //* prepare params.
653
+ const params = { index: indexName, id, body: {} };
654
+ // check version to include 'type' in params
655
+ if (this.isOldES6) {
656
+ params.type = type;
657
+ }
658
+ const scripts = [];
456
659
  if (increments) {
457
- //! it will create if not exists.
660
+ //* it will create if not exists.
458
661
  params.body.upsert = Object.assign(Object.assign({}, increments), { [idName]: id });
459
- const scripts = Object.entries(increments).reduce((L, [key, val]) => {
460
- L.push(`ctx._source.${key} += ${val}`);
461
- return L;
462
- }, []);
463
- if (version < 7.0)
464
- params.body.lang = 'painless';
465
- params.body.script = scripts.join('; ');
662
+ Object.entries(increments).forEach(([key, val]) => {
663
+ if (Array.isArray(val)) {
664
+ // If the value is an array, append it to the existing array in the source
665
+ scripts.push(`if (ctx._source.${key} != null && ctx._source.${key} instanceof List) {
666
+ ctx._source.${key}.addAll(params.increments.${key});
667
+ } else {
668
+ ctx._source.${key} = params.increments.${key};
669
+ }`);
670
+ }
671
+ else {
672
+ // If the value is a number, increment the existing field
673
+ scripts.push(`if (ctx._source.${key} != null) {
674
+ ctx._source.${key} += params.increments.${key};
675
+ } else {
676
+ ctx._source.${key} = params.increments.${key};
677
+ }`);
678
+ }
679
+ });
680
+ }
681
+ if (item) {
682
+ // Handle item updates in the script
683
+ Object.entries(item).forEach(([key]) => {
684
+ scripts.push(`ctx._source.${key} = params.item.${key};`);
685
+ });
466
686
  }
687
+ params.body.script = {
688
+ source: scripts.join(' '),
689
+ lang: 'painless',
690
+ params: { item, increments },
691
+ };
467
692
  (0, engine_1._log)(NS, `> params[${id}] =`, engine_1.$U.json(params));
468
693
  // const { client } = instance(endpoint);
469
694
  const client = this.client;
470
695
  const res = yield client.update(params, options).catch(exports.$ERROR.handler('update', (e, E) => {
471
696
  const msg = (0, test_helper_1.GETERR)(e);
472
- //! id 아이템이 없을 경우 발생함.
697
+ //* id 아이템이 없을 경우 발생함.
473
698
  if (msg.startsWith('404 DOCUMENT MISSING'))
474
699
  throw new Error(`404 NOT FOUND - id:${id}`);
475
- //! 해당 속성이 없을때 업데이트 하려면 생길 수 있음.
700
+ //* 해당 속성이 없을때 업데이트 하려면 생길 수 있음.
476
701
  if (msg.startsWith('400 REMOTE TRANSPORT'))
477
702
  throw new Error(`400 INVALID FIELD - id:${id}`);
478
703
  if (msg.startsWith('404 NOT FOUND'))
@@ -483,6 +708,8 @@ class Elastic6Service {
483
708
  throw e; // at ES6.8
484
709
  if (msg.startsWith('400 ILLEGAL ARGUMENT'))
485
710
  throw e; // at ES7.1
711
+ if (msg.startsWith('400 MAPPER PARSING'))
712
+ throw e;
486
713
  throw E;
487
714
  }));
488
715
  // {"_index":"test-v3","_type":"_doc","_id":"aaa","_version":2,"result":"updated","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":8,"_primary_term":1}
@@ -496,6 +723,8 @@ class Elastic6Service {
496
723
  }
497
724
  /**
498
725
  * run search and get the raw response.
726
+ * @param body - Elasticsearch Query DSL that defines the search request (e.g., size, query, filters).
727
+ * @param searchType - type of search (e.g., 'query_then_fetch', 'dfs_query_then_fetch').
499
728
  */
500
729
  searchRaw(body, searchType) {
501
730
  return __awaiter(this, void 0, void 0, function* () {
@@ -506,7 +735,11 @@ class Elastic6Service {
506
735
  (0, engine_1._log)(NS, `> body =`, engine_1.$U.json(body));
507
736
  const tmp = docType ? docType : '';
508
737
  const type = docType ? `${docType}` : undefined;
509
- const params = { index: indexName, type, body, searchType };
738
+ const params = { index: indexName, body, searchType };
739
+ // check version to include 'type' in params
740
+ if (this.isOldES6) {
741
+ params.type = type;
742
+ }
510
743
  (0, engine_1._log)(NS, `> params[${tmp}] =`, engine_1.$U.json(Object.assign(Object.assign({}, params), { body: undefined })));
511
744
  // const { client } = instance(endpoint);
512
745
  const client = this.client;
@@ -520,12 +753,15 @@ class Elastic6Service {
520
753
  // _log(NS, `> search[${tmp}].hits.total =`, $res.hits?.total);
521
754
  // _log(NS, `> search[${tmp}].hits.max_score =`, $res.hits?.max_score);
522
755
  // _log(NS, `> search[${tmp}].hits.hits[0] =`, $res.hits && $U.json($res.hits.hits[0]));
523
- //! return raw results.
756
+ //* return raw results.
524
757
  return $res === null || $res === void 0 ? void 0 : $res.body;
525
758
  });
526
759
  }
527
760
  /**
528
761
  * run search, and get the formatmted response.
762
+ * @param body - Elasticsearch Query DSL that defines the search request (e.g., size, query, filters).
763
+ * @param searchType - type of search (e.g., 'query_then_fetch', 'dfs_query_then_fetch').
764
+ *
529
765
  */
530
766
  search(body, searchType) {
531
767
  var _a, _b, _c;
@@ -545,6 +781,70 @@ class Elastic6Service {
545
781
  };
546
782
  });
547
783
  }
784
+ /**
785
+ * search all until limit (-1 means no-limit)
786
+ * @param body - Elasticsearch Query DSL that defines the search request (e.g., size, query, filters).
787
+ * @param params - parameters including search type, limit, and retry options.
788
+ */
789
+ searchAll(body, params) {
790
+ var e_1, _a;
791
+ return __awaiter(this, void 0, void 0, function* () {
792
+ const list = [];
793
+ try {
794
+ for (var _b = __asyncValues(this.generateSearchResult(body, params)), _c; _c = yield _b.next(), !_c.done;) {
795
+ const chunk = _c.value;
796
+ chunk.forEach((N) => list.push(N));
797
+ }
798
+ }
799
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
800
+ finally {
801
+ try {
802
+ if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
803
+ }
804
+ finally { if (e_1) throw e_1.error; }
805
+ }
806
+ return list;
807
+ });
808
+ }
809
+ /**
810
+ * create async generator that yields items queried until last
811
+ *
812
+ * @param body - Elasticsearch Query DSL that defines the search request (e.g., size, query, filters).
813
+ * @param params - parameters including search type, limit, and retry options.
814
+ */
815
+ generateSearchResult(body, params) {
816
+ var _a, _b, _c, _d, _e, _f, _g;
817
+ return __asyncGenerator(this, arguments, function* generateSearchResult_1() {
818
+ const doRetry = (_b = (_a = params === null || params === void 0 ? void 0 : params.retryOptions) === null || _a === void 0 ? void 0 : _a.do) !== null && _b !== void 0 ? _b : false;
819
+ const t = (_d = (_c = params === null || params === void 0 ? void 0 : params.retryOptions) === null || _c === void 0 ? void 0 : _c.t) !== null && _d !== void 0 ? _d : 5000;
820
+ const maxRetries = (_f = (_e = params === null || params === void 0 ? void 0 : params.retryOptions) === null || _e === void 0 ? void 0 : _e.maxRetries) !== null && _f !== void 0 ? _f : 3;
821
+ let limit = (_g = params === null || params === void 0 ? void 0 : params.limit) !== null && _g !== void 0 ? _g : -1;
822
+ let retryCount = 0;
823
+ if (!body.sort)
824
+ body.sort = '_doc';
825
+ do {
826
+ try {
827
+ const { list, last } = yield __await(this.search(body, params === null || params === void 0 ? void 0 : params.searchType));
828
+ body.search_after = last;
829
+ if (list.length === 0 && !body.search_after) {
830
+ break;
831
+ }
832
+ yield yield __await(list);
833
+ }
834
+ catch (e) {
835
+ const msg = (0, test_helper_1.GETERR)(e);
836
+ if (doRetry && msg.startsWith('429 UNKNOWN') && retryCount < maxRetries) {
837
+ retryCount++;
838
+ yield __await((0, test_helper_1.waited)(t));
839
+ continue;
840
+ }
841
+ else {
842
+ throw e;
843
+ }
844
+ }
845
+ } while (body.search_after && (limit === -1 || --limit > 0));
846
+ });
847
+ }
548
848
  /**
549
849
  * prepare default setting
550
850
  * - migrated from engine-v2.
@@ -562,7 +862,7 @@ class Elastic6Service {
562
862
  const shards = params.shards === undefined ? 4 : params.shards;
563
863
  const replicas = params.replicas === undefined ? 1 : params.replicas;
564
864
  const timeSeries = params.timeSeries === undefined ? false : params.timeSeries;
565
- //! core config.
865
+ //* core config.
566
866
  const CONF_ES_DOCTYPE = docType;
567
867
  const CONF_ID_NAME = idName;
568
868
  const CONF_ES_TIMESERIES = !!timeSeries;
@@ -642,7 +942,7 @@ class Elastic6Service {
642
942
  },
643
943
  },
644
944
  };
645
- //! default settings.
945
+ //* default settings.
646
946
  const ES_SETTINGS = {
647
947
  settings: {
648
948
  number_of_shards: shards,
@@ -677,33 +977,33 @@ class Elastic6Service {
677
977
  autocomplete_case_sensitive: {
678
978
  type: 'custom',
679
979
  tokenizer: 'edge_30grams',
680
- filter: version < 7.0 ? ['standard'] : [], //! error - The [standard] token filter has been removed.
980
+ filter: version < 7 && version >= 6 ? ['standard'] : [], //* error - The [standard] token filter has been removed.
681
981
  },
682
982
  },
683
983
  },
684
984
  },
685
- //! since 7.x. no mapping for types.
686
- mappings: version < 7.0 ? { [CONF_ES_DOCTYPE]: ES_MAPPINGS } : ES_MAPPINGS,
985
+ //* since 7.x. no mapping for types.
986
+ mappings: version < 7 && version >= 6 ? { [CONF_ES_DOCTYPE]: ES_MAPPINGS } : ES_MAPPINGS,
687
987
  };
688
- //! timeseries 데이터로, 기본 timestamp 값을 넣어준다. (주의! save시 current-time 값 자동 저장)
988
+ //* timeseries 데이터로, 기본 timestamp 값을 넣어준다. (주의! save시 current-time 값 자동 저장)
689
989
  if (!!CONF_ES_TIMESERIES) {
690
990
  ES_SETTINGS.settings.refresh_interval = '5s';
691
- if (version < 7.0) {
991
+ if (version < 7 && version >= 6) {
692
992
  ES_SETTINGS.mappings[CONF_ES_DOCTYPE].properties['@timestamp'] = { type: 'date', doc_values: true };
693
993
  ES_SETTINGS.mappings[CONF_ES_DOCTYPE].properties['ip'] = { type: 'ip' };
694
- //! clear mappings.
994
+ //* clear mappings.
695
995
  const CLEANS = '@version,created_at,updated_at,deleted_at'.split(',');
696
996
  CLEANS.map(key => delete ES_SETTINGS.mappings[CONF_ES_DOCTYPE].properties[key]);
697
997
  }
698
998
  else {
699
999
  ES_SETTINGS.mappings.properties['@timestamp'] = { type: 'date', doc_values: true };
700
1000
  ES_SETTINGS.mappings.properties['ip'] = { type: 'ip' };
701
- //! clear mappings.
1001
+ //* clear mappings.
702
1002
  const CLEANS = '@version,created_at,updated_at,deleted_at'.split(',');
703
1003
  CLEANS.map(key => delete ES_SETTINGS.properties[key]);
704
1004
  }
705
1005
  }
706
- //! returns settings.
1006
+ //* returns settings.
707
1007
  return ES_SETTINGS;
708
1008
  }
709
1009
  /**
@@ -797,26 +1097,33 @@ exports.$ERROR = {
797
1097
  const status = `${E.statusCode || ''}`;
798
1098
  const message = `${E.message || E.msg || ''}`;
799
1099
  const reason = ((E) => {
800
- var _a, _b, _c, _d;
801
- //! from ES7.1
1100
+ var _a, _b, _c, _d, _e, _f;
1101
+ //* from ES7.1
802
1102
  if (E.meta && typeof E.meta == 'object') {
803
1103
  const type = _S(E === null || E === void 0 ? void 0 : E.message).toUpperCase().split('_').slice(0, -1).join(' ');
804
1104
  const status = engine_1.$U.N((_a = E.meta) === null || _a === void 0 ? void 0 : _a.statusCode, type.includes('NOT FOUND') ? 404 : 400);
805
- return { status, type: type || (status === 404 ? 'NOT FOUND' : 'UNKNOWN') };
1105
+ const $res = exports.$ERROR.parseMeta(E.meta);
1106
+ //* find the reason.
1107
+ const reason = (_c = (_b = $res.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.reason;
1108
+ const result = { status, type: type || (status === 404 ? 'NOT FOUND' : 'UNKNOWN') };
1109
+ if (typeof reason !== 'undefined') {
1110
+ result.reason = reason;
1111
+ }
1112
+ return result;
806
1113
  }
807
- //! from ES6.2
1114
+ //* from ES6.2
808
1115
  if (!E.response)
809
1116
  return null;
810
1117
  const $res = exports.$ERROR.parseMeta(E.response);
811
- //! find the root-cause.
1118
+ //* find the root-cause.
812
1119
  const pic1 = (N, i = 0) => (N && Array.isArray(N) ? N[i] : N);
813
- const cause = pic1((_b = $res === null || $res === void 0 ? void 0 : $res.error) === null || _b === void 0 ? void 0 : _b.root_cause);
814
- const status = engine_1.$U.N(((_c = $res.error) === null || _c === void 0 ? void 0 : _c.status) || $res.status);
815
- const reason = _S((_d = $res.error) === null || _d === void 0 ? void 0 : _d.reason, $res.found === false || $res.result === 'not_found' ? 'NOT FOUND' : '');
1120
+ const cause = pic1((_d = $res === null || $res === void 0 ? void 0 : $res.error) === null || _d === void 0 ? void 0 : _d.root_cause);
1121
+ const status = engine_1.$U.N(((_e = $res.error) === null || _e === void 0 ? void 0 : _e.status) || $res.status);
1122
+ const reason = _S((_f = $res.error) === null || _f === void 0 ? void 0 : _f.reason, $res.found === false || $res.result === 'not_found' ? 'NOT FOUND' : '');
816
1123
  const type = _S(cause === null || cause === void 0 ? void 0 : cause.type).toUpperCase().split('_').slice(0, -1).join(' ');
817
1124
  return { status, reason, cause, type: type || reason };
818
1125
  })(E);
819
- //! FINAL. convert to error-object.
1126
+ //* FINAL. convert to error-object.
820
1127
  return {
821
1128
  status: engine_1.$U.N(status, (reason === null || reason === void 0 ? void 0 : reason.status) || 0),
822
1129
  message: message || (reason === null || reason === void 0 ? void 0 : reason.reason),
@@ -825,13 +1132,14 @@ exports.$ERROR = {
825
1132
  },
826
1133
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
827
1134
  handler: (name, cb) => (e) => {
1135
+ var _a, _b, _c, _d, _e;
828
1136
  const E = exports.$ERROR.asError(e);
829
- //! unknown error found..
1137
+ //* unknown error found..
830
1138
  if (!(E === null || E === void 0 ? void 0 : E.status)) {
831
1139
  (0, engine_1._err)(NS, `! err[${name}]@handler =`, e instanceof Error, engine_1.$U.json(e));
832
1140
  throw e;
833
1141
  }
834
- const $e = new Error(`${E.status} ${E.reason.type} - ${E.message}`);
1142
+ const $e = new Error(`${E.status} ${(_b = (_a = E.reason) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : ''} - ${(_e = (_d = (_c = E.reason) === null || _c === void 0 ? void 0 : _c.reason) !== null && _d !== void 0 ? _d : E.message) !== null && _e !== void 0 ? _e : ''}`);
835
1143
  if (cb)
836
1144
  return cb($e, E);
837
1145
  throw $e;
@@ -894,10 +1202,28 @@ class DummyElastic6Service extends Elastic6Service {
894
1202
  const org = yield this.readItem(id);
895
1203
  const item = Object.assign(Object.assign(Object.assign({}, org), updates), { _version: Number(org._version || 0) + 1 });
896
1204
  if (increments) {
897
- //TODO - support increments in dummy.
1205
+ Object.entries(increments).forEach(([key, value]) => {
1206
+ if (Array.isArray(value)) {
1207
+ // case of arrary increment
1208
+ if (Array.isArray(item[key])) {
1209
+ item[key] = [...item[key], ...value];
1210
+ }
1211
+ else {
1212
+ item[key] = value;
1213
+ }
1214
+ }
1215
+ else {
1216
+ if (typeof item[key] === 'number') {
1217
+ item[key] = item[key] + value;
1218
+ }
1219
+ else {
1220
+ item[key] = value;
1221
+ }
1222
+ }
1223
+ });
898
1224
  }
899
1225
  this.buffer[id] = item;
900
- return item;
1226
+ return Object.assign({ id: id, _version: Number(org._version || 0) + 1 }, updates);
901
1227
  });
902
1228
  }
903
1229
  }