@terascope/elasticsearch-api 3.0.2 → 3.2.0
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.
- package/index.js +101 -59
- package/package.json +15 -5
- package/test/api-spec.js +1 -1
- package/test/bulk-send-limit-spec.js +43 -0
- package/test/helpers/config.js +22 -0
- package/test/helpers/data.js +10023 -0
- package/test/helpers/elasticsearch.js +130 -0
- package/types/index.d.ts +4 -0
package/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// polyfill because opensearch has references to an api that won't exist
|
|
4
|
+
// on the client side, should be able to remove in the future
|
|
5
|
+
require('setimmediate');
|
|
6
|
+
|
|
3
7
|
const Promise = require('bluebird');
|
|
4
8
|
const {
|
|
5
9
|
isTest,
|
|
@@ -23,9 +27,12 @@ const {
|
|
|
23
27
|
getTypeOf,
|
|
24
28
|
isProd
|
|
25
29
|
} = require('@terascope/utils');
|
|
30
|
+
const { ElasticsearchDistribution } = require('@terascope/types');
|
|
31
|
+
|
|
26
32
|
const { inspect } = require('util');
|
|
27
33
|
|
|
28
34
|
const DOCUMENT_EXISTS = 409;
|
|
35
|
+
const TOO_MANY_REQUESTS = 429;
|
|
29
36
|
|
|
30
37
|
// Module to manage persistence in Elasticsearch.
|
|
31
38
|
// All functions in this module return promises that must be resolved to get the final result.
|
|
@@ -73,8 +80,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
function search(query) {
|
|
76
|
-
|
|
77
|
-
if (esVersion >= 7) {
|
|
83
|
+
if (!isElasticsearch6()) {
|
|
78
84
|
if (query._sourceExclude) {
|
|
79
85
|
query._sourceExcludes = query._sourceExclude.slice();
|
|
80
86
|
delete query._sourceExclude;
|
|
@@ -101,7 +107,10 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
101
107
|
|
|
102
108
|
function _runRequest() {
|
|
103
109
|
clientBase[endpoint](query)
|
|
104
|
-
.then(
|
|
110
|
+
.then((rawResponse) => {
|
|
111
|
+
const response = get(rawResponse, 'body', rawResponse);
|
|
112
|
+
resolve(response);
|
|
113
|
+
})
|
|
105
114
|
.catch(errHandler);
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -228,31 +237,29 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
228
237
|
);
|
|
229
238
|
return Promise.resolve(true);
|
|
230
239
|
}
|
|
231
|
-
return client.cluster.stats({}).then((data) => {
|
|
232
|
-
if (!_checkVersion(data.nodes.versions[0])) {
|
|
233
|
-
return Promise.resolve();
|
|
234
|
-
}
|
|
235
|
-
return client.indices
|
|
236
|
-
.getSettings({})
|
|
237
|
-
.then((results) => {
|
|
238
|
-
const resultIndex = _verifyIndex(results, config.index);
|
|
239
|
-
if (resultIndex.found) {
|
|
240
|
-
resultIndex.indexWindowSize.forEach((ind) => {
|
|
241
|
-
logger.warn(
|
|
242
|
-
`max_result_window for index: ${ind.name} is set at ${ind.windowSize}. On very large indices it is possible that a slice can not be divided to stay below this limit. If that occurs an error will be thrown by Elasticsearch and the slice can not be processed. Increasing max_result_window in the Elasticsearch index settings will resolve the problem.`
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
} else {
|
|
246
|
-
const error = new TSError('index specified in reader does not exist', {
|
|
247
|
-
statusCode: 404,
|
|
248
|
-
});
|
|
249
|
-
return Promise.reject(error);
|
|
250
|
-
}
|
|
251
240
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
241
|
+
return client.indices
|
|
242
|
+
.getSettings({})
|
|
243
|
+
.then((results) => {
|
|
244
|
+
const settingsData = results.body && results.meta ? results.body : results;
|
|
245
|
+
const resultIndex = _verifyIndex(settingsData, config.index);
|
|
246
|
+
|
|
247
|
+
if (resultIndex.found) {
|
|
248
|
+
resultIndex.indexWindowSize.forEach((ind) => {
|
|
249
|
+
logger.warn(
|
|
250
|
+
`max_result_window for index: ${ind.name} is set at ${ind.windowSize}. On very large indices it is possible that a slice can not be divided to stay below this limit. If that occurs an error will be thrown by Elasticsearch and the slice can not be processed. Increasing max_result_window in the Elasticsearch index settings will resolve the problem.`
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
} else {
|
|
254
|
+
const error = new TSError('index specified in reader does not exist', {
|
|
255
|
+
statusCode: 404,
|
|
256
|
+
});
|
|
257
|
+
return Promise.reject(error);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return Promise.resolve();
|
|
261
|
+
})
|
|
262
|
+
.catch((err) => Promise.reject(new TSError(err)));
|
|
256
263
|
}
|
|
257
264
|
|
|
258
265
|
function putTemplate(template, name) {
|
|
@@ -291,7 +298,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
291
298
|
continue;
|
|
292
299
|
}
|
|
293
300
|
|
|
294
|
-
if (item.error.type === 'es_rejected_execution_exception') {
|
|
301
|
+
if (item.status === TOO_MANY_REQUESTS || item.error.type === 'es_rejected_execution_exception') {
|
|
295
302
|
if (actionRecords[i] == null) {
|
|
296
303
|
// this error should not happen in production,
|
|
297
304
|
// only in tests where the bulk function is mocked
|
|
@@ -340,7 +347,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
340
347
|
throw new Error(`Bulk send record is missing the action property${dbg}`);
|
|
341
348
|
}
|
|
342
349
|
|
|
343
|
-
if (
|
|
350
|
+
if (!isElasticsearch6()) {
|
|
344
351
|
const actionKey = getFirstKey(record.action);
|
|
345
352
|
const { _type, ...withoutTypeAction } = record.action[actionKey];
|
|
346
353
|
// if data is specified return both
|
|
@@ -357,10 +364,11 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
357
364
|
return record.data ? [record.action, record.data] : [record.action];
|
|
358
365
|
});
|
|
359
366
|
|
|
360
|
-
const
|
|
367
|
+
const response = await _clientRequest('bulk', { body });
|
|
368
|
+
const results = response.body ? response.body : response;
|
|
361
369
|
|
|
362
|
-
if (!
|
|
363
|
-
return
|
|
370
|
+
if (!results.errors) {
|
|
371
|
+
return results.items.reduce((c, item) => {
|
|
364
372
|
const [value] = Object.values(item);
|
|
365
373
|
// ignore non-successful status codes
|
|
366
374
|
if (value.status != null && value.status >= 400) return c;
|
|
@@ -370,7 +378,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
370
378
|
|
|
371
379
|
const {
|
|
372
380
|
retry, successful, error, reason
|
|
373
|
-
} = _filterRetryRecords(actionRecords,
|
|
381
|
+
} = _filterRetryRecords(actionRecords, results);
|
|
374
382
|
|
|
375
383
|
if (error) {
|
|
376
384
|
throw new Error(`bulk send error: ${reason}`);
|
|
@@ -631,14 +639,19 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
631
639
|
client
|
|
632
640
|
.search(queryParam)
|
|
633
641
|
.then((data) => {
|
|
634
|
-
const
|
|
642
|
+
const failuresReasons = [];
|
|
643
|
+
const results = data.body ? data.body : data;
|
|
644
|
+
const { failures, failed } = results._shards;
|
|
645
|
+
|
|
635
646
|
if (!failed) {
|
|
636
|
-
resolve(
|
|
647
|
+
resolve(results);
|
|
637
648
|
return;
|
|
638
649
|
}
|
|
639
650
|
|
|
651
|
+
failuresReasons.push(...failures);
|
|
652
|
+
|
|
640
653
|
const reasons = uniq(
|
|
641
|
-
flatten(
|
|
654
|
+
flatten(failuresReasons.map((shard) => shard.reason.type))
|
|
642
655
|
);
|
|
643
656
|
|
|
644
657
|
if (
|
|
@@ -665,13 +678,13 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
665
678
|
}
|
|
666
679
|
|
|
667
680
|
function _esV7adjustments(queryParam) {
|
|
668
|
-
if (
|
|
681
|
+
if (!isElasticsearch6()) {
|
|
669
682
|
queryParam.trackTotalHits = true;
|
|
670
683
|
}
|
|
671
684
|
}
|
|
672
685
|
|
|
673
686
|
function _adjustTypeForEs7(query) {
|
|
674
|
-
if (
|
|
687
|
+
if (!isElasticsearch6()) {
|
|
675
688
|
if (Array.isArray(query)) {
|
|
676
689
|
return _removeTypeFromBulkRequest(query);
|
|
677
690
|
}
|
|
@@ -682,7 +695,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
682
695
|
}
|
|
683
696
|
|
|
684
697
|
function _removeTypeFromBulkRequest(query) {
|
|
685
|
-
if (
|
|
698
|
+
if (isElasticsearch6()) return query;
|
|
686
699
|
|
|
687
700
|
return query.map((queryItem) => {
|
|
688
701
|
if (isSimpleObject(queryItem)) {
|
|
@@ -812,11 +825,6 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
812
825
|
};
|
|
813
826
|
}
|
|
814
827
|
|
|
815
|
-
function _checkVersion(str) {
|
|
816
|
-
const num = Number(str.replace(/\./g, ''));
|
|
817
|
-
return num >= 210;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
828
|
function isAvailable(index, recordType) {
|
|
821
829
|
const query = {
|
|
822
830
|
index,
|
|
@@ -870,7 +878,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
870
878
|
});
|
|
871
879
|
});
|
|
872
880
|
}
|
|
873
|
-
|
|
881
|
+
// TODO: verifyClient needs to be checked with new client usage
|
|
874
882
|
/**
|
|
875
883
|
* Verify the state of the underlying es client.
|
|
876
884
|
* Will throw error if in fatal state, like the connection is closed.
|
|
@@ -894,16 +902,46 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
894
902
|
|
|
895
903
|
return true;
|
|
896
904
|
}
|
|
897
|
-
|
|
905
|
+
/** This is deprecated as an external api,
|
|
906
|
+
* please use getClientMetadata
|
|
907
|
+
* */
|
|
898
908
|
function getESVersion() {
|
|
899
|
-
const
|
|
909
|
+
const newClientVersion = get(client, '__meta.version');
|
|
910
|
+
const esVersion = newClientVersion || get(client, 'transport._config.apiVersion', '6.5');
|
|
911
|
+
|
|
900
912
|
if (esVersion && isString(esVersion)) {
|
|
901
913
|
const [majorVersion] = esVersion.split('.');
|
|
902
914
|
return toNumber(majorVersion);
|
|
903
915
|
}
|
|
916
|
+
|
|
904
917
|
return 6;
|
|
905
918
|
}
|
|
906
919
|
|
|
920
|
+
function getClientMetadata() {
|
|
921
|
+
const newClientVersion = get(client, '__meta.version');
|
|
922
|
+
const esVersion = newClientVersion || get(client, 'transport._config.apiVersion', '6.5');
|
|
923
|
+
const distribution = get(client, '__meta.distribution', ElasticsearchDistribution.elasticsearch);
|
|
924
|
+
|
|
925
|
+
return {
|
|
926
|
+
distribution,
|
|
927
|
+
version: esVersion
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function isElasticsearch6() {
|
|
932
|
+
const { distribution, version: esVersion } = getClientMetadata();
|
|
933
|
+
const parsedVersion = toNumber(esVersion.split('.', 1)[0]);
|
|
934
|
+
|
|
935
|
+
return distribution === ElasticsearchDistribution.elasticsearch && parsedVersion === 6;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function isElasticsearch8() {
|
|
939
|
+
const { distribution, version: esVersion } = getClientMetadata();
|
|
940
|
+
const parsedVersion = toNumber(esVersion.split('.', 1)[0]);
|
|
941
|
+
|
|
942
|
+
return distribution === ElasticsearchDistribution.elasticsearch && parsedVersion === 8;
|
|
943
|
+
}
|
|
944
|
+
|
|
907
945
|
function _fixMappingRequest(_params, isTemplate) {
|
|
908
946
|
if (!_params || !_params.body) {
|
|
909
947
|
throw new Error('Invalid mapping request');
|
|
@@ -911,17 +949,14 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
911
949
|
const params = cloneDeep(_params);
|
|
912
950
|
const defaultParams = {};
|
|
913
951
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
if (isTemplate) {
|
|
918
|
-
params.body.index_patterns = castArray(params.body.template).slice();
|
|
919
|
-
}
|
|
920
|
-
delete params.body.template;
|
|
952
|
+
if (params.body.template != null) {
|
|
953
|
+
if (isTemplate && params.body.index_patterns == null) {
|
|
954
|
+
params.body.index_patterns = castArray(params.body.template).slice();
|
|
921
955
|
}
|
|
956
|
+
delete params.body.template;
|
|
922
957
|
}
|
|
923
958
|
|
|
924
|
-
if (
|
|
959
|
+
if (!isElasticsearch6()) {
|
|
925
960
|
const typeMappings = get(params.body, 'mappings', {});
|
|
926
961
|
if (typeMappings.properties) {
|
|
927
962
|
defaultParams.includeTypeName = false;
|
|
@@ -935,6 +970,11 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
935
970
|
});
|
|
936
971
|
}
|
|
937
972
|
}
|
|
973
|
+
|
|
974
|
+
if (isElasticsearch8(client)) {
|
|
975
|
+
delete defaultParams.includeTypeName;
|
|
976
|
+
}
|
|
977
|
+
|
|
938
978
|
return Object.assign({}, defaultParams, params);
|
|
939
979
|
}
|
|
940
980
|
|
|
@@ -1028,13 +1068,13 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
1028
1068
|
});
|
|
1029
1069
|
return Promise.reject(error);
|
|
1030
1070
|
});
|
|
1031
|
-
});
|
|
1071
|
+
}).catch((err) => Promise.reject(err));
|
|
1032
1072
|
}
|
|
1033
1073
|
|
|
1034
1074
|
function _verifyMapping(query, configMapping, recordType) {
|
|
1035
1075
|
const params = Object.assign({}, query);
|
|
1036
|
-
|
|
1037
|
-
if (
|
|
1076
|
+
|
|
1077
|
+
if (!isElasticsearch6()) {
|
|
1038
1078
|
if (recordType) {
|
|
1039
1079
|
params.includeTypeName = true;
|
|
1040
1080
|
}
|
|
@@ -1202,12 +1242,14 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
|
|
|
1202
1242
|
indexRecovery,
|
|
1203
1243
|
indexSetup,
|
|
1204
1244
|
verifyClient,
|
|
1205
|
-
getESVersion,
|
|
1206
1245
|
validateGeoParameters,
|
|
1246
|
+
getClientMetadata,
|
|
1247
|
+
isElasticsearch6,
|
|
1207
1248
|
// The APIs below are deprecated and should be removed.
|
|
1208
1249
|
index_exists: indexExists,
|
|
1209
1250
|
index_create: indexCreate,
|
|
1210
1251
|
index_refresh: indexRefresh,
|
|
1211
1252
|
index_recovery: indexRecovery,
|
|
1253
|
+
getESVersion,
|
|
1212
1254
|
};
|
|
1213
1255
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terascope/elasticsearch-api",
|
|
3
3
|
"displayName": "Elasticsearch API",
|
|
4
|
-
"version": "3.0
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"description": "Elasticsearch client api used across multiple services, handles retries and exponential backoff",
|
|
6
6
|
"homepage": "https://github.com/terascope/teraslice/tree/master/packages/elasticsearch-api#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -14,15 +14,25 @@
|
|
|
14
14
|
"typings": "types/index.d.ts",
|
|
15
15
|
"scripts": {
|
|
16
16
|
"test": "ts-scripts test . --",
|
|
17
|
+
"test:7": "ELASTICSEARCH_VERSION='7.9.3' yarn run test",
|
|
18
|
+
"test:8": "ELASTICSEARCH_VERSION='8.1.2' yarn run test",
|
|
17
19
|
"test:debug": "ts-scripts test --debug . --",
|
|
20
|
+
"test:legacy": "LEGACY_CLIENT=true yarn run test",
|
|
21
|
+
"test:opensearch": "TEST_OPENSEARCH='true' yarn run test",
|
|
18
22
|
"test:watch": "ts-scripts test --watch . --"
|
|
19
23
|
},
|
|
20
24
|
"dependencies": {
|
|
21
|
-
"@terascope/utils": "^0.44.
|
|
22
|
-
"bluebird": "^3.7.2"
|
|
25
|
+
"@terascope/utils": "^0.44.2",
|
|
26
|
+
"bluebird": "^3.7.2",
|
|
27
|
+
"setimmediate": "^1.0.5"
|
|
23
28
|
},
|
|
24
29
|
"devDependencies": {
|
|
25
|
-
"@
|
|
30
|
+
"@opensearch-project/opensearch": "^1.0.2",
|
|
31
|
+
"@types/elasticsearch": "^5.0.40",
|
|
32
|
+
"elasticsearch": "^15.4.1",
|
|
33
|
+
"elasticsearch6": "npm:@elastic/elasticsearch@^6.7.0",
|
|
34
|
+
"elasticsearch7": "npm:@elastic/elasticsearch@^7.0.0",
|
|
35
|
+
"elasticsearch8": "npm:@elastic/elasticsearch@^8.0.0"
|
|
26
36
|
},
|
|
27
37
|
"engines": {
|
|
28
38
|
"node": "^12.22.0 || >=14.17.0",
|
|
@@ -33,6 +43,6 @@
|
|
|
33
43
|
"registry": "https://registry.npmjs.org/"
|
|
34
44
|
},
|
|
35
45
|
"terascope": {
|
|
36
|
-
"testSuite": "
|
|
46
|
+
"testSuite": "restrained"
|
|
37
47
|
}
|
|
38
48
|
}
|
package/test/api-spec.js
CHANGED
|
@@ -360,7 +360,7 @@ describe('elasticsearch-api', () => {
|
|
|
360
360
|
stats: () => Promise.resolve({ _nodes: {} })
|
|
361
361
|
},
|
|
362
362
|
cluster: {
|
|
363
|
-
stats: () => Promise.resolve({ nodes: { versions: ['
|
|
363
|
+
stats: () => Promise.resolve({ nodes: { versions: ['6.8.1'] } })
|
|
364
364
|
},
|
|
365
365
|
__testing: {
|
|
366
366
|
start: 100,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { debugLogger, chunk, pMap } = require('@terascope/utils');
|
|
4
|
+
const elasticsearchAPI = require('../index');
|
|
5
|
+
const { data } = require('./helpers/data');
|
|
6
|
+
const { TEST_INDEX_PREFIX } = require('./helpers/config');
|
|
7
|
+
const {
|
|
8
|
+
makeClient, cleanupIndex,
|
|
9
|
+
waitForData, formatUploadData
|
|
10
|
+
} = require('./helpers/elasticsearch');
|
|
11
|
+
|
|
12
|
+
const THREE_MINUTES = 3 * 60 * 1000;
|
|
13
|
+
|
|
14
|
+
jest.setTimeout(THREE_MINUTES + 30000);
|
|
15
|
+
|
|
16
|
+
describe('bulkSend can work with congested queues', () => {
|
|
17
|
+
const logger = debugLogger('congested_test');
|
|
18
|
+
const index = `${TEST_INDEX_PREFIX}_congested_queues_`;
|
|
19
|
+
|
|
20
|
+
let client;
|
|
21
|
+
let api;
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
client = await makeClient();
|
|
25
|
+
await cleanupIndex(client, index);
|
|
26
|
+
api = elasticsearchAPI(client, logger);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
await cleanupIndex(client, index);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('can get correct data even with congested queues', async () => {
|
|
34
|
+
const chunkedData = chunk(data, 50);
|
|
35
|
+
|
|
36
|
+
await pMap(chunkedData, async (cData) => {
|
|
37
|
+
const formattedData = formatUploadData(index, cData);
|
|
38
|
+
return api.bulkSend(formattedData);
|
|
39
|
+
}, { concurrency: 9 });
|
|
40
|
+
|
|
41
|
+
await waitForData(client, index, data.length, logger, THREE_MINUTES);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
TEST_INDEX_PREFIX = 'teratest_',
|
|
5
|
+
ELASTICSEARCH_HOST = 'http://localhost:9200',
|
|
6
|
+
ELASTICSEARCH_API_VERSION = '6.5',
|
|
7
|
+
ELASTICSEARCH_VERSION = '6.8.6',
|
|
8
|
+
OPENSEARCH_USER = 'admin',
|
|
9
|
+
OPENSEARCH_PASSWORD = 'admin',
|
|
10
|
+
OPENSEARCH_VERSION = '1.3.0',
|
|
11
|
+
RESTRAINED_OPENSEARCH_PORT = process.env.RESTRAINED_OPENSEARCH_PORT || '49206',
|
|
12
|
+
RESTRAINED_OPENSEARCH_HOST = `http://${OPENSEARCH_USER}:${OPENSEARCH_PASSWORD}@http://localhost:${RESTRAINED_OPENSEARCH_PORT}`,
|
|
13
|
+
} = process.env;
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
TEST_INDEX_PREFIX,
|
|
17
|
+
ELASTICSEARCH_HOST,
|
|
18
|
+
ELASTICSEARCH_API_VERSION,
|
|
19
|
+
ELASTICSEARCH_VERSION,
|
|
20
|
+
RESTRAINED_OPENSEARCH_HOST,
|
|
21
|
+
OPENSEARCH_VERSION
|
|
22
|
+
};
|