kuzzle 2.31.0 → 2.32.0-beta.1
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.d.ts +1 -0
- package/index.js +1 -0
- package/lib/api/controllers/authController.d.ts +3 -2
- package/lib/api/funnel.js +2 -1
- package/lib/config/default.config.js +1 -0
- package/lib/core/backend/backendStorage.d.ts +3 -5
- package/lib/core/backend/backendStorage.js +8 -10
- package/lib/core/plugin/pluginContext.d.ts +2 -3
- package/lib/core/plugin/pluginContext.js +6 -4
- package/lib/core/security/tokenRepository.d.ts +1 -1
- package/lib/core/security/tokenRepository.js +1 -1
- package/lib/core/shared/ObjectRepository.d.ts +1 -1
- package/lib/core/storage/clientAdapter.js +6 -4
- package/lib/core/storage/storageEngine.js +4 -5
- package/lib/kerror/index.js +1 -1
- package/lib/kuzzle/event/KuzzleEventEmitter.d.ts +70 -0
- package/lib/kuzzle/event/KuzzleEventEmitter.js +328 -0
- package/lib/kuzzle/index.d.ts +3 -0
- package/lib/kuzzle/index.js +7 -4
- package/lib/kuzzle/kuzzle.d.ts +32 -19
- package/lib/kuzzle/kuzzle.js +31 -31
- package/lib/service/storage/{elasticsearch.d.ts → 7/elasticsearch.d.ts} +40 -22
- package/lib/service/storage/{elasticsearch.js → 7/elasticsearch.js} +24 -43
- package/lib/service/storage/{esWrapper.js → 7/esWrapper.js} +6 -4
- package/lib/service/storage/8/elasticsearch.d.ts +972 -0
- package/lib/service/storage/8/elasticsearch.js +2925 -0
- package/lib/service/storage/8/esWrapper.js +303 -0
- package/lib/service/storage/Elasticsearch.d.ts +9 -0
- package/lib/service/storage/Elasticsearch.js +48 -0
- package/lib/service/storage/commons/queryTranslator.d.ts +5 -0
- package/lib/service/storage/commons/queryTranslator.js +189 -0
- package/lib/types/EventHandler.d.ts +29 -1
- package/lib/types/config/KuzzleConfiguration.d.ts +2 -1
- package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.d.ts +6 -2
- package/lib/types/storage/{Elasticsearch.d.ts → 7/Elasticsearch.d.ts} +1 -1
- package/lib/types/storage/8/Elasticsearch.d.ts +59 -0
- package/lib/types/storage/8/Elasticsearch.js +3 -0
- package/package.json +7 -4
- package/lib/kuzzle/event/kuzzleEventEmitter.js +0 -405
- package/lib/service/storage/queryTranslator.js +0 -219
- /package/lib/types/storage/{Elasticsearch.js → 7/Elasticsearch.js} +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Kuzzle, a backend software, self-hostable and ready to use
|
|
3
|
+
* to power modern apps
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2015-2022 Kuzzle
|
|
6
|
+
* mailto: support AT kuzzle.io
|
|
7
|
+
* website: http://kuzzle.io
|
|
8
|
+
*
|
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
* you may not use this file except in compliance with the License.
|
|
11
|
+
* You may obtain a copy of the License at
|
|
12
|
+
*
|
|
13
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
*
|
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
* See the License for the specific language governing permissions and
|
|
19
|
+
* limitations under the License.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* eslint sort-keys: 0 */
|
|
23
|
+
|
|
24
|
+
"use strict";
|
|
25
|
+
|
|
26
|
+
const Bluebird = require("bluebird");
|
|
27
|
+
const _ = require("lodash");
|
|
28
|
+
const es = require("sdk-es8");
|
|
29
|
+
|
|
30
|
+
const { KuzzleError } = require("../../../kerror/errors");
|
|
31
|
+
const debug = require("../../../util/debug")(
|
|
32
|
+
"kuzzle:services:storage:ESCommon",
|
|
33
|
+
);
|
|
34
|
+
const kerror = require("../../../kerror").wrap("services", "storage");
|
|
35
|
+
|
|
36
|
+
const errorMessagesMapping = [
|
|
37
|
+
{
|
|
38
|
+
regex:
|
|
39
|
+
/^\[es_rejected_execution_exception] rejected execution .*? on EsThreadPoolExecutor\[(.*?), .*$/,
|
|
40
|
+
subCode: "too_many_operations",
|
|
41
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
// [illegal_argument_exception] object mapping [titi] can't be changed from nested to non-nested
|
|
45
|
+
regex:
|
|
46
|
+
/^\[illegal_argument_exception] object mapping \[(.*?)] can't be changed from nested to non-nested$/,
|
|
47
|
+
subcode: "cannot_change_mapping",
|
|
48
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
// [illegal_argument_exception] object mapping [baz] can't be changed from non-nested to nested
|
|
52
|
+
regex:
|
|
53
|
+
/^\[illegal_argument_exception] object mapping \[(.*?)] can't be changed from non-nested to nested$/,
|
|
54
|
+
subcode: "cannot_change_mapping",
|
|
55
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
// [illegal_argument_exception] Can't merge a non object mapping [aeaze] with an object mapping [aeaze]
|
|
59
|
+
regex:
|
|
60
|
+
/^\[illegal_argument_exception] Can't merge a non object mapping \[(.*?)] with an object mapping \[(.*?)]$/,
|
|
61
|
+
subcode: "cannot_change_mapping",
|
|
62
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
// [illegal_argument_exception] [tutu.tutu] is defined as an object in mapping [aze] but this name is already used for a field in other types
|
|
66
|
+
regex:
|
|
67
|
+
/^\[illegal_argument_exception] \[(.*?)] is defined as an object in mapping \[(.*?)] but this name is already used for a field in other types$/,
|
|
68
|
+
subcode: "duplicate_field_mapping",
|
|
69
|
+
getPlaceholders: (esError, matches) => [matches[1], matches[2]],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
// [illegal_argument_exception] mapper [source.flags] of different type, current_type [string], merged_type [long]
|
|
73
|
+
regex:
|
|
74
|
+
/^mapper \[(.*?)] of different type, current_type \[(.*?)], merged_type \[(.*?)]$/,
|
|
75
|
+
subcode: "cannot_change_mapping",
|
|
76
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
// [mapper_parsing_exception] Mapping definition for [flags] has unsupported parameters: [index : not_analyzed]
|
|
80
|
+
// eslint-disable-next-line no-regex-spaces
|
|
81
|
+
regex:
|
|
82
|
+
/^\[mapper_parsing_exception] Mapping definition for \[(.*?)] has unsupported parameters: \[(.*?)]$/,
|
|
83
|
+
subcode: "unexpected_properties",
|
|
84
|
+
getPlaceholders: (esError, matches) => [matches[2], matches[1]],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
// [mapper_parsing_exception] No handler for type [boolean] declared on field [not]
|
|
88
|
+
regex:
|
|
89
|
+
/^\[mapper_parsing_exception] No handler for type \[(.*?)] declared on field \[(.*?)]$/,
|
|
90
|
+
subcode: "invalid_mapping_type",
|
|
91
|
+
getPlaceholders: (esError, matches) => [matches[2], matches[1]],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
// [mapper_parsing_exception] failed to parse [conditions.host.flags]
|
|
95
|
+
regex: /^\[mapper_parsing_exception] failed to parse \[(.*?)]$/,
|
|
96
|
+
subcode: "wrong_mapping_property",
|
|
97
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
// Failed to parse mapping [_doc]: Expected map for property [fields] on field [enabled] but got a class java.lang.String
|
|
101
|
+
regex:
|
|
102
|
+
/^Failed to parse mapping \[.*\]: Expected \w+ for property \[(.*)\] on field \[(.*)\]/,
|
|
103
|
+
subcode: "wrong_mapping_property",
|
|
104
|
+
getPlaceholders: (esError, matches) => [`${matches[2]}.${matches[1]}`],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
// [index_not_found_exception] no such index, with { resource.type=index_or_alias & resource.id=foso & index=foso }
|
|
108
|
+
regex: /^no such index \[([%&])(.*)\.(.*)\]$/,
|
|
109
|
+
subcode: "unknown_collection",
|
|
110
|
+
getPlaceholders: (esError, matches) => [matches[2], matches[3]],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
// [mapper_parsing_exception] Expected map for property [fields] on field [foo] but got a class java.lang.String
|
|
114
|
+
regex:
|
|
115
|
+
/^\[mapper_parsing_exception] Expected map for property \[(.*?)] on field \[(.*?)] but got a class java\.lang\.String$/,
|
|
116
|
+
subcode: "wrong_mapping_property",
|
|
117
|
+
getPlaceholders: (esError, matches) => [`${matches[2]}.${matches[1]}`],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
regex:
|
|
121
|
+
/^\[version_conflict_engine_exception] \[data]\[(.*?)]: version conflict.*$/,
|
|
122
|
+
subcode: "too_many_changes",
|
|
123
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
//[liia]: version conflict, document already exists (current version [2])
|
|
127
|
+
regex: /^\[(.*)\]: version conflict, document already exists.*/,
|
|
128
|
+
subcode: "document_already_exists",
|
|
129
|
+
getPlaceholders: () => [],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
// Unknown key for a START_OBJECT in [term].
|
|
133
|
+
regex: /^Unknown key for a START_OBJECT in \[(.*)\].*/,
|
|
134
|
+
subcode: "invalid_search_query",
|
|
135
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
// mapping set to strict, dynamic introduction of [lehuong] within [_doc] is not allowed
|
|
139
|
+
regex:
|
|
140
|
+
/^\[(.+)\] mapping set to strict, dynamic introduction of \[(.+)\] within \[.+\] is not allowed/,
|
|
141
|
+
subcode: "strict_mapping_rejection",
|
|
142
|
+
getPlaceholders: (esError, matches) => {
|
|
143
|
+
// "/%26index.collection/_doc"
|
|
144
|
+
const esPath = esError.meta.meta.request.params.path;
|
|
145
|
+
// keep only "index"
|
|
146
|
+
const index = esPath.split(".")[0].split("%26")[1];
|
|
147
|
+
// keep only "collection"
|
|
148
|
+
const collection = esPath.substr(esPath.indexOf(".") + 1).split("/")[0];
|
|
149
|
+
|
|
150
|
+
return [matches[2], index, collection];
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
// [and] query malformed, no start_object after query name
|
|
155
|
+
regex: /^\[(.*)\] query malformed, no start_object after query name/,
|
|
156
|
+
subcode: "unknown_query_keyword",
|
|
157
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
// no [query] registered for [equals]
|
|
161
|
+
regex: /^no \[query\] registered for \[(.*)\]/,
|
|
162
|
+
subcode: "unknown_query_keyword",
|
|
163
|
+
getPlaceholders: (esError, matches) => [matches[1]],
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
class ESWrapper {
|
|
168
|
+
constructor(client) {
|
|
169
|
+
this.client = client;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Transforms raw ES errors into a normalized Kuzzle version
|
|
174
|
+
*
|
|
175
|
+
* @param {Error} error
|
|
176
|
+
* @returns {KuzzleError}
|
|
177
|
+
*/
|
|
178
|
+
formatESError(error) {
|
|
179
|
+
if (error instanceof KuzzleError) {
|
|
180
|
+
return error;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
global.kuzzle.emit("services:storage:error", {
|
|
184
|
+
message: `Elasticsearch Client error: ${error.message}`,
|
|
185
|
+
// /!\ not all ES error classes have a "meta" property
|
|
186
|
+
meta: error.meta || null,
|
|
187
|
+
stack: error.stack,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (error instanceof es.errors.NoLivingConnectionsError) {
|
|
191
|
+
throw kerror.get("not_connected");
|
|
192
|
+
}
|
|
193
|
+
const message = _.get(error, "meta.body.error.reason", error.message);
|
|
194
|
+
|
|
195
|
+
// Try to match a known elasticsearch error
|
|
196
|
+
for (const betterError of errorMessagesMapping) {
|
|
197
|
+
const matches = message.match(betterError.regex);
|
|
198
|
+
|
|
199
|
+
if (matches) {
|
|
200
|
+
return kerror.get(
|
|
201
|
+
betterError.subcode,
|
|
202
|
+
...betterError.getPlaceholders(error, matches),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Try to match using error codes
|
|
208
|
+
if (error.meta) {
|
|
209
|
+
switch (error.meta.statusCode) {
|
|
210
|
+
case 400:
|
|
211
|
+
return this._handleBadRequestError(error, message);
|
|
212
|
+
case 404:
|
|
213
|
+
return this._handleNotFoundError(error, message);
|
|
214
|
+
case 409:
|
|
215
|
+
return this._handleConflictError(error, message);
|
|
216
|
+
default:
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return this._handleUnknownError(error, message);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
reject(error) {
|
|
225
|
+
return Bluebird.reject(this.formatESError(error));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_handleConflictError(error, message) {
|
|
229
|
+
debug('unhandled "Conflict" elasticsearch error: %a', error);
|
|
230
|
+
|
|
231
|
+
return kerror.get("unexpected_error", message);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
_handleNotFoundError(error, message) {
|
|
235
|
+
let errorMessage = message;
|
|
236
|
+
|
|
237
|
+
if (!error.body._index) {
|
|
238
|
+
return kerror.get("unexpected_not_found", errorMessage);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// _index= "&nyc-open-data.yellow-taxi"
|
|
242
|
+
const index = error.body._index.split(".")[0].slice(1);
|
|
243
|
+
const collection = error.body._index.split(".")[1];
|
|
244
|
+
|
|
245
|
+
// 404 on a GET document
|
|
246
|
+
if (error.body.found === false) {
|
|
247
|
+
return kerror.get("not_found", error.body._id, index, collection);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 404 on DELETE document (ES error payloads are so cool!)
|
|
251
|
+
if (error.meta.body._id) {
|
|
252
|
+
return kerror.get("not_found", error.meta.body._id, index, collection);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (error.meta.body && error.meta.body.error) {
|
|
256
|
+
errorMessage = error.meta.body.error
|
|
257
|
+
? `${error.meta.body.error.reason}: ${error.meta.body.error["resource.id"]}`
|
|
258
|
+
: `${error.message}: ${error.body._id}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
debug('unhandled "NotFound" elasticsearch error: %a', error);
|
|
262
|
+
|
|
263
|
+
return kerror.get("unexpected_not_found", errorMessage);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
_handleBadRequestError(error, message) {
|
|
267
|
+
let errorMessage = message;
|
|
268
|
+
|
|
269
|
+
if (error.meta.body && error.meta.body.error) {
|
|
270
|
+
errorMessage = error.meta.body.error.root_cause
|
|
271
|
+
? error.meta.body.error.root_cause[0].reason
|
|
272
|
+
: error.meta.body.error.reason;
|
|
273
|
+
|
|
274
|
+
// empty query throws exception with ES 7
|
|
275
|
+
if (
|
|
276
|
+
error.meta.body.error.type === "parsing_exception" &&
|
|
277
|
+
_.get(error, "meta.body.error.caused_by.type") ===
|
|
278
|
+
"illegal_argument_exception"
|
|
279
|
+
) {
|
|
280
|
+
errorMessage = error.meta.body.error.caused_by.reason;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
debug(
|
|
285
|
+
'unhandled "BadRequest" elasticsearch error: %a',
|
|
286
|
+
_.get(error, "meta.body.error.reason", error.message),
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return kerror.get("unexpected_bad_request", errorMessage);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_handleUnknownError(error, message) {
|
|
293
|
+
debug(
|
|
294
|
+
"unhandled elasticsearch error (unhandled type: %s): %o",
|
|
295
|
+
_.get(error, "error.meta.statusCode", "<no status code>"),
|
|
296
|
+
error,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return kerror.get("unexpected_error", message);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
module.exports = ESWrapper;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Service from "../service";
|
|
2
|
+
import { storeScopeEnum } from "../../core/storage/storeScopeEnum";
|
|
3
|
+
export declare class Elasticsearch extends Service {
|
|
4
|
+
client: any;
|
|
5
|
+
constructor(config: any, scope?: storeScopeEnum);
|
|
6
|
+
static buildClient(config: any, version?: "7" | "8"): any;
|
|
7
|
+
_initSequence(): Promise<void>;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Elasticsearch = void 0;
|
|
7
|
+
const sdk_es7_1 = require("sdk-es7");
|
|
8
|
+
const sdk_es8_1 = require("sdk-es8");
|
|
9
|
+
const elasticsearch_1 = require("./7/elasticsearch");
|
|
10
|
+
const elasticsearch_2 = require("./8/elasticsearch");
|
|
11
|
+
const service_1 = __importDefault(require("../service"));
|
|
12
|
+
const storeScopeEnum_1 = require("../../core/storage/storeScopeEnum");
|
|
13
|
+
class Elasticsearch extends service_1.default {
|
|
14
|
+
constructor(config, scope = storeScopeEnum_1.storeScopeEnum.PUBLIC) {
|
|
15
|
+
super("elasticsearch", config);
|
|
16
|
+
global.kuzzle.log.info(`[ℹ] Elasticsearch configuration is set to major version : ${config.majorVersion}`);
|
|
17
|
+
if (config.majorVersion === "7") {
|
|
18
|
+
this.client = new elasticsearch_1.ES7(config, scope);
|
|
19
|
+
}
|
|
20
|
+
else if (config.majorVersion === "8") {
|
|
21
|
+
this.client = new elasticsearch_2.ES8(config, scope);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
throw new Error("Invalid Elasticsearch version.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
static buildClient(config, version) {
|
|
28
|
+
if (!version) {
|
|
29
|
+
version = "7";
|
|
30
|
+
}
|
|
31
|
+
switch (version) {
|
|
32
|
+
case "7":
|
|
33
|
+
return new sdk_es7_1.Client(config);
|
|
34
|
+
case "8":
|
|
35
|
+
return new sdk_es8_1.Client(config);
|
|
36
|
+
default:
|
|
37
|
+
throw new Error("Invalid Elasticsearch version.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async _initSequence() {
|
|
41
|
+
await this.client._initSequence();
|
|
42
|
+
}
|
|
43
|
+
async init() {
|
|
44
|
+
await super.init();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.Elasticsearch = Elasticsearch;
|
|
48
|
+
//# sourceMappingURL=Elasticsearch.js.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueryTranslator = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* Kuzzle, a backend software, self-hostable and ready to use
|
|
6
|
+
* to power modern apps
|
|
7
|
+
*
|
|
8
|
+
* Copyright 2015-2022 Kuzzle
|
|
9
|
+
* mailto: support AT kuzzle.io
|
|
10
|
+
* website: http://kuzzle.io
|
|
11
|
+
*
|
|
12
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
+
* you may not use this file except in compliance with the License.
|
|
14
|
+
* You may obtain a copy of the License at
|
|
15
|
+
*
|
|
16
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
+
*
|
|
18
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
* See the License for the specific language governing permissions and
|
|
22
|
+
* limitations under the License.
|
|
23
|
+
*/
|
|
24
|
+
const kerror_1 = require("../../../kerror");
|
|
25
|
+
class KeywordError extends Error {
|
|
26
|
+
constructor(type, name) {
|
|
27
|
+
super(`The ${type} "${name}" of Koncorde DSL is not supported for search queries.`);
|
|
28
|
+
this.keyword = { name, type };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const KONCORDE_OPERATORS = ["and", "or", "not", "bool"];
|
|
32
|
+
/**
|
|
33
|
+
* Parse the Koncorde path to extract the path and the value
|
|
34
|
+
* path have the form "path.to.field[json_value]"
|
|
35
|
+
*
|
|
36
|
+
* @param {string} path
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
function parseKoncordePath(path) {
|
|
40
|
+
const firstBracket = path.indexOf("[");
|
|
41
|
+
if (firstBracket < 0) {
|
|
42
|
+
return {
|
|
43
|
+
path,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const lastBracket = path.lastIndexOf("]");
|
|
47
|
+
if (lastBracket < 0) {
|
|
48
|
+
throw (0, kerror_1.get)("services", "koncorde", "elastic_translation_error", `Invalid exists path "${path}": missing closing bracket`);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
path: path.slice(0, firstBracket),
|
|
52
|
+
value: JSON.parse(path.slice(firstBracket + 1, lastBracket)),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const KONCORDE_CLAUSES_TO_ES = {
|
|
56
|
+
equals: (content) => ({
|
|
57
|
+
term: {
|
|
58
|
+
...content,
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
exists: (field) => {
|
|
62
|
+
// Support old syntax { exists: { field: "path" } } and { exists: "path" }
|
|
63
|
+
const parsedInfo = parseKoncordePath(field.field || field);
|
|
64
|
+
// If we have a value, we need to use a range query to be sure that the value is the same
|
|
65
|
+
if (parsedInfo.value) {
|
|
66
|
+
return {
|
|
67
|
+
bool: {
|
|
68
|
+
filter: [
|
|
69
|
+
{
|
|
70
|
+
exists: {
|
|
71
|
+
field: parsedInfo.path,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
range: {
|
|
76
|
+
[parsedInfo.path]: {
|
|
77
|
+
gte: parsedInfo.value,
|
|
78
|
+
lte: parsedInfo.value,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
exists: {
|
|
88
|
+
field: parsedInfo.path,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
geoBoundingBox: (content) => ({
|
|
93
|
+
geo_bounding_box: {
|
|
94
|
+
...content,
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
geoDistance: (content) => ({
|
|
98
|
+
geo_distance: {
|
|
99
|
+
...content,
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
geoDistanceRange: (content) => ({
|
|
103
|
+
geo_distance_range: {
|
|
104
|
+
...content,
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
107
|
+
geoPolygon: (content) => ({
|
|
108
|
+
geo_polygon: {
|
|
109
|
+
...content,
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
ids: (content) => ({
|
|
113
|
+
ids: {
|
|
114
|
+
...content,
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
in: (content) => ({
|
|
118
|
+
terms: {
|
|
119
|
+
...content,
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
missing: (field) => ({
|
|
123
|
+
bool: {
|
|
124
|
+
must_not: [{ exists: { field } }],
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
range: (content) => ({
|
|
128
|
+
range: {
|
|
129
|
+
...content,
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
133
|
+
const KONCORDE_OPERATORS_TO_ES = {
|
|
134
|
+
and: (content) => ({
|
|
135
|
+
bool: {
|
|
136
|
+
filter: [...content],
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
bool: undefined,
|
|
140
|
+
not: (content) => {
|
|
141
|
+
const [name, value] = Object.entries(content[0])[0];
|
|
142
|
+
return {
|
|
143
|
+
bool: {
|
|
144
|
+
must_not: [{ [name]: value }],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
or: (content) => ({
|
|
149
|
+
bool: {
|
|
150
|
+
should: [...content],
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
class QueryTranslator {
|
|
155
|
+
translate(filters) {
|
|
156
|
+
const [name, value] = Object.entries(filters)[0];
|
|
157
|
+
if (KONCORDE_OPERATORS.includes(name)) {
|
|
158
|
+
return this._translateOperator(name, value);
|
|
159
|
+
}
|
|
160
|
+
return this._translateClause(name, value);
|
|
161
|
+
}
|
|
162
|
+
_translateOperator(operator, operands) {
|
|
163
|
+
const converter = KONCORDE_OPERATORS_TO_ES[operator];
|
|
164
|
+
if (converter === undefined) {
|
|
165
|
+
throw new KeywordError("operator", operator);
|
|
166
|
+
}
|
|
167
|
+
const esOperands = [];
|
|
168
|
+
if (operator === "not") {
|
|
169
|
+
esOperands.push(this.translate(operands));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
for (const operand of operands) {
|
|
173
|
+
esOperands.push(this.translate(operand));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return converter(esOperands);
|
|
177
|
+
}
|
|
178
|
+
_translateClause(clause, content) {
|
|
179
|
+
const converter = KONCORDE_CLAUSES_TO_ES[clause];
|
|
180
|
+
if (converter === undefined) {
|
|
181
|
+
return {
|
|
182
|
+
[clause]: content,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return converter(content);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.QueryTranslator = QueryTranslator;
|
|
189
|
+
//# sourceMappingURL=queryTranslator.js.map
|
|
@@ -14,10 +14,30 @@ export type EventDefinition = {
|
|
|
14
14
|
*/
|
|
15
15
|
args: any[];
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Describe an ask event with it's name and the payload and result types
|
|
19
|
+
*/
|
|
20
|
+
export type AskEventDefinition = {
|
|
21
|
+
/**
|
|
22
|
+
* Name of the event
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* "core:document:create"
|
|
26
|
+
*/
|
|
27
|
+
name: string;
|
|
28
|
+
/**
|
|
29
|
+
* Payload of the event
|
|
30
|
+
*/
|
|
31
|
+
payload: any;
|
|
32
|
+
/**
|
|
33
|
+
* Result of the event
|
|
34
|
+
*/
|
|
35
|
+
result: any;
|
|
36
|
+
};
|
|
17
37
|
/**
|
|
18
38
|
* Handler for hook events
|
|
19
39
|
*/
|
|
20
|
-
export type HookEventHandler<TEventDefinition extends EventDefinition = EventDefinition> = (...args: TEventDefinition["args"]) => void;
|
|
40
|
+
export type HookEventHandler<TEventDefinition extends EventDefinition = EventDefinition> = (...args: TEventDefinition["args"]) => Promise<void> | void;
|
|
21
41
|
/**
|
|
22
42
|
* Handler for pipe event.
|
|
23
43
|
*
|
|
@@ -28,6 +48,14 @@ export type PipeEventHandler<TEventDefinition extends EventDefinition = EventDef
|
|
|
28
48
|
* Handler for cluster event.
|
|
29
49
|
*/
|
|
30
50
|
export type ClusterEventHandler<TEventDefinition extends EventDefinition = EventDefinition> = (...args: TEventDefinition["args"]) => any;
|
|
51
|
+
/**
|
|
52
|
+
* Handler for ask event.
|
|
53
|
+
*/
|
|
54
|
+
export type AskEventHandler<TAskEventDefinition extends AskEventDefinition = AskEventDefinition> = (...args: [payload?: TAskEventDefinition["payload"], ...rest: any[]]) => Promise<TAskEventDefinition["result"]> | TAskEventDefinition["result"];
|
|
55
|
+
/**
|
|
56
|
+
* Handler for call event.
|
|
57
|
+
*/
|
|
58
|
+
export type CallEventHandler<TAskEventDefinition extends AskEventDefinition = AskEventDefinition> = (...args: [payload?: TAskEventDefinition["payload"], ...rest: any[]]) => TAskEventDefinition["result"];
|
|
31
59
|
/**
|
|
32
60
|
* @deprecated Use HookEventHandler, PipeEventHandler or ClusterEventHandler
|
|
33
61
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DumpConfiguration, HttpConfiguration, LimitsConfiguration, PluginsConfiguration, SecurityConfiguration, ServerConfiguration, ServicesConfiguration } from "../index";
|
|
2
2
|
export interface IKuzzleConfiguration {
|
|
3
3
|
realtime: {
|
|
4
4
|
/**
|
|
@@ -121,6 +121,7 @@ export interface IKuzzleConfiguration {
|
|
|
121
121
|
};
|
|
122
122
|
internal: {
|
|
123
123
|
hash: any;
|
|
124
|
+
notifiableProtocols: string[];
|
|
124
125
|
};
|
|
125
126
|
validation: Record<string, unknown>;
|
|
126
127
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { ClientOptions } from "@elastic/elasticsearch";
|
|
2
1
|
export type StorageEngineElasticsearch = {
|
|
2
|
+
/**
|
|
3
|
+
* Elasticsearch major version
|
|
4
|
+
* @default "7"
|
|
5
|
+
*/
|
|
6
|
+
majorVersion: "7" | "8";
|
|
3
7
|
/**
|
|
4
8
|
* @default ['storageEngine']
|
|
5
9
|
*/
|
|
@@ -22,7 +26,7 @@ export type StorageEngineElasticsearch = {
|
|
|
22
26
|
* }
|
|
23
27
|
*
|
|
24
28
|
*/
|
|
25
|
-
client:
|
|
29
|
+
client: any;
|
|
26
30
|
/**
|
|
27
31
|
* Default policy against new fields that are not referenced in the
|
|
28
32
|
* collection mapping.
|