@opra/elastic 1.0.0-alpha.31 → 1.0.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/cjs/adapter-utils/prepare-filter.js +88 -80
- package/cjs/adapter-utils/prepare-patch.js +35 -0
- package/cjs/adapter-utils/prepare-projection.js +65 -108
- package/cjs/adapter-utils/prepare-sort.js +5 -6
- package/cjs/elastic-adapter.js +4 -7
- package/cjs/elastic-collection-service.js +365 -0
- package/cjs/elastic-entity-service.js +392 -0
- package/cjs/elastic-service.js +20 -103
- package/cjs/index.js +3 -0
- package/esm/adapter-utils/prepare-filter.js +88 -80
- package/esm/adapter-utils/prepare-patch.js +32 -0
- package/esm/adapter-utils/prepare-projection.js +62 -109
- package/esm/adapter-utils/prepare-sort.js +5 -6
- package/esm/elastic-adapter.js +4 -7
- package/esm/elastic-collection-service.js +361 -0
- package/esm/elastic-entity-service.js +388 -0
- package/esm/elastic-service.js +20 -103
- package/esm/index.js +3 -0
- package/esm/package.json +3 -0
- package/package.json +27 -35
- package/types/adapter-utils/prepare-filter.d.ts +3 -2
- package/types/adapter-utils/prepare-patch.d.ts +2 -0
- package/types/adapter-utils/prepare-projection.d.ts +7 -0
- package/types/elastic-adapter.d.ts +7 -2
- package/types/elastic-collection-service.d.ts +217 -0
- package/types/elastic-entity-service.d.ts +262 -0
- package/types/elastic-service.d.ts +20 -99
- package/types/index.d.cts +4 -0
- package/types/index.d.ts +3 -0
- package/cjs/adapter-utils/prepare-key-values.js +0 -22
- package/esm/adapter-utils/prepare-key-values.js +0 -18
- package/types/adapter-utils/prepare-key-values.d.ts +0 -1
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { DATATYPE_METADATA, InternalServerError } from '@opra/common';
|
|
2
|
+
import { isNotNullish } from 'valgen';
|
|
3
|
+
import { ElasticAdapter } from './elastic-adapter.js';
|
|
4
|
+
import { ElasticService } from './elastic-service.js';
|
|
5
|
+
/**
|
|
6
|
+
* @class ElasticEntityService
|
|
7
|
+
* @template T - The type of the documents in the collection.
|
|
8
|
+
*/
|
|
9
|
+
export class ElasticEntityService extends ElasticService {
|
|
10
|
+
/**
|
|
11
|
+
* Constructs a new instance
|
|
12
|
+
*
|
|
13
|
+
* @param {Type | string} dataType - The data type of the array elements.
|
|
14
|
+
* @param {ElasticEntityService.Options} [options] - The options for the array service.
|
|
15
|
+
* @constructor
|
|
16
|
+
*/
|
|
17
|
+
constructor(dataType, options) {
|
|
18
|
+
super(options);
|
|
19
|
+
this._inputCodecs = {};
|
|
20
|
+
this._outputCodecs = {};
|
|
21
|
+
this._dataType_ = dataType;
|
|
22
|
+
if (options?.indexName)
|
|
23
|
+
this.indexName = options?.indexName;
|
|
24
|
+
else {
|
|
25
|
+
if (typeof dataType === 'string')
|
|
26
|
+
this.indexName = dataType;
|
|
27
|
+
if (typeof dataType === 'function') {
|
|
28
|
+
const metadata = Reflect.getMetadata(DATATYPE_METADATA, dataType);
|
|
29
|
+
if (metadata)
|
|
30
|
+
this.indexName = metadata.name;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
this.resourceName = options?.resourceName;
|
|
34
|
+
this.idGenerator = options?.idGenerator;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the index name.
|
|
38
|
+
*
|
|
39
|
+
* @protected
|
|
40
|
+
* @returns The index name.
|
|
41
|
+
* @throws {Error} If the index name is not defined.
|
|
42
|
+
*/
|
|
43
|
+
getIndexName() {
|
|
44
|
+
const out = typeof this.indexName === 'function' ? this.indexName(this) : this.indexName;
|
|
45
|
+
if (out)
|
|
46
|
+
return out;
|
|
47
|
+
throw new Error('indexName is not defined');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves the resource name.
|
|
51
|
+
*
|
|
52
|
+
* @protected
|
|
53
|
+
* @returns {string} The resource name.
|
|
54
|
+
* @throws {Error} If the resource name is not defined.
|
|
55
|
+
*/
|
|
56
|
+
getResourceName() {
|
|
57
|
+
const out = typeof this.resourceName === 'function' ? this.resourceName(this) : this.resourceName || this.getIndexName();
|
|
58
|
+
if (out)
|
|
59
|
+
return out;
|
|
60
|
+
throw new Error('resourceName is not defined');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Retrieves the OPRA data type
|
|
64
|
+
*
|
|
65
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
66
|
+
*/
|
|
67
|
+
get dataType() {
|
|
68
|
+
if (!this._dataType)
|
|
69
|
+
this._dataType = this.context.document.node.getComplexType(this._dataType_);
|
|
70
|
+
return this._dataType;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Adds a JSON document to the specified data stream or index and makes it searchable.
|
|
74
|
+
* If the target is an index and the document already exists,
|
|
75
|
+
* the request updates the document and increments its version.
|
|
76
|
+
*
|
|
77
|
+
* @param {ElasticEntityService.CreateCommand} command
|
|
78
|
+
* @protected
|
|
79
|
+
*/
|
|
80
|
+
async _create(command) {
|
|
81
|
+
const input = command.input;
|
|
82
|
+
isNotNullish(input, { label: 'input' });
|
|
83
|
+
isNotNullish(input._id, { label: 'input._id' });
|
|
84
|
+
const inputCodec = this._getInputCodec('create');
|
|
85
|
+
const doc = inputCodec(input);
|
|
86
|
+
const { options } = command;
|
|
87
|
+
const request = {
|
|
88
|
+
...options?.request,
|
|
89
|
+
index: this.getIndexName(),
|
|
90
|
+
id: input._id,
|
|
91
|
+
document: doc,
|
|
92
|
+
};
|
|
93
|
+
const client = this.getClient();
|
|
94
|
+
const r = await client.create(request, options?.transport);
|
|
95
|
+
/* istanbul ignore next */
|
|
96
|
+
if (!(r._id && (r.result === 'created' || r.result === 'updated'))) {
|
|
97
|
+
throw new InternalServerError(`Unknown error while creating document for "${this.getResourceName()}"`);
|
|
98
|
+
}
|
|
99
|
+
return r;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns the count of documents in the collection based on the provided options.
|
|
103
|
+
*
|
|
104
|
+
* @param {ElasticEntityService.CountCommand} command
|
|
105
|
+
* @protected
|
|
106
|
+
*/
|
|
107
|
+
async _count(command) {
|
|
108
|
+
const { options } = command;
|
|
109
|
+
const filterQuery = ElasticAdapter.prepareFilter([options?.filter, options?.request?.query]);
|
|
110
|
+
let query = {
|
|
111
|
+
...options?.request?.query,
|
|
112
|
+
...filterQuery,
|
|
113
|
+
};
|
|
114
|
+
if (!Object.keys(query).length)
|
|
115
|
+
query = undefined;
|
|
116
|
+
const request = {
|
|
117
|
+
index: this.getIndexName(),
|
|
118
|
+
...options?.request,
|
|
119
|
+
query,
|
|
120
|
+
};
|
|
121
|
+
const client = this.getClient();
|
|
122
|
+
return client.count(request, options?.transport);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Deletes a document from the collection.
|
|
126
|
+
*
|
|
127
|
+
* @param {ElasticEntityService.DeleteCommand} command
|
|
128
|
+
* @protected
|
|
129
|
+
*/
|
|
130
|
+
async _delete(command) {
|
|
131
|
+
isNotNullish(command.documentId, { label: 'documentId' });
|
|
132
|
+
const { options } = command;
|
|
133
|
+
const filterQuery = ElasticAdapter.prepareFilter([
|
|
134
|
+
{ ids: { values: [command.documentId] } },
|
|
135
|
+
options?.filter,
|
|
136
|
+
options?.request?.query,
|
|
137
|
+
]);
|
|
138
|
+
let query = {
|
|
139
|
+
...options?.request?.query,
|
|
140
|
+
...filterQuery,
|
|
141
|
+
};
|
|
142
|
+
if (!Object.keys(query).length)
|
|
143
|
+
query = { match_all: {} };
|
|
144
|
+
const request = {
|
|
145
|
+
index: this.getIndexName(),
|
|
146
|
+
...options?.request,
|
|
147
|
+
query,
|
|
148
|
+
};
|
|
149
|
+
const client = this.getClient();
|
|
150
|
+
return client.deleteByQuery(request, options?.transport);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Deletes multiple documents from the collection that meet the specified filter criteria.
|
|
154
|
+
*
|
|
155
|
+
* @param {ElasticEntityService.DeleteManyCommand} command
|
|
156
|
+
* @protected
|
|
157
|
+
*/
|
|
158
|
+
async _deleteMany(command) {
|
|
159
|
+
const { options } = command;
|
|
160
|
+
const filterQuery = ElasticAdapter.prepareFilter([options?.filter, options?.request?.query]);
|
|
161
|
+
let query = {
|
|
162
|
+
...options?.request?.query,
|
|
163
|
+
...filterQuery,
|
|
164
|
+
};
|
|
165
|
+
if (!Object.keys(query).length)
|
|
166
|
+
query = { match_all: {} };
|
|
167
|
+
const request = {
|
|
168
|
+
...options?.request,
|
|
169
|
+
index: this.getIndexName(),
|
|
170
|
+
query,
|
|
171
|
+
};
|
|
172
|
+
const client = this.getClient();
|
|
173
|
+
return client.deleteByQuery(request, options?.transport);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns search hits that match the query defined in the request
|
|
177
|
+
*
|
|
178
|
+
* @param {ElasticEntityService.SearchCommand} command
|
|
179
|
+
*/
|
|
180
|
+
async _search(command) {
|
|
181
|
+
const { options } = command;
|
|
182
|
+
const filterQuery = ElasticAdapter.prepareFilter([
|
|
183
|
+
command.documentId ? { ids: { values: [command.documentId] } } : undefined,
|
|
184
|
+
options?.filter,
|
|
185
|
+
options?.request?.query,
|
|
186
|
+
]);
|
|
187
|
+
let query = {
|
|
188
|
+
...options?.request?.query,
|
|
189
|
+
...filterQuery,
|
|
190
|
+
};
|
|
191
|
+
if (!Object.keys(query).length)
|
|
192
|
+
query = { match_all: {} };
|
|
193
|
+
const request = {
|
|
194
|
+
from: options?.skip,
|
|
195
|
+
size: options?.limit,
|
|
196
|
+
sort: options?.sort ? ElasticAdapter.prepareSort(options?.sort) : undefined,
|
|
197
|
+
_source: ElasticAdapter.prepareProjection(this.dataType, options?.projection),
|
|
198
|
+
index: this.getIndexName(),
|
|
199
|
+
...options?.request,
|
|
200
|
+
query,
|
|
201
|
+
};
|
|
202
|
+
const client = this.getClient();
|
|
203
|
+
return client.search(request, options?.transport);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Updates multiple documents in the collection based on the specified input and options.
|
|
207
|
+
*
|
|
208
|
+
* @param {ElasticEntityService.UpdateCommand<T>} command
|
|
209
|
+
*/
|
|
210
|
+
async _updateMany(command) {
|
|
211
|
+
if (command.byId)
|
|
212
|
+
isNotNullish(command.documentId, { label: 'documentId' });
|
|
213
|
+
const { options } = command;
|
|
214
|
+
const input = command.input;
|
|
215
|
+
const requestScript = command.options?.request?.script;
|
|
216
|
+
let script;
|
|
217
|
+
const inputKeysLen = Object.keys(input).length;
|
|
218
|
+
isNotNullish(inputKeysLen || script, { label: 'input' });
|
|
219
|
+
if (requestScript) {
|
|
220
|
+
script = typeof requestScript === 'string' ? { source: requestScript } : { ...requestScript };
|
|
221
|
+
script.lang = script.lang || 'painless';
|
|
222
|
+
if (inputKeysLen > 0 && script.lang !== 'painless') {
|
|
223
|
+
throw new TypeError(`You cannot provide 'input' and 'script' arguments at the same time unless the script lang is 'painless'`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (inputKeysLen) {
|
|
227
|
+
delete input._id;
|
|
228
|
+
const inputCodec = this._getInputCodec('update');
|
|
229
|
+
const doc = inputCodec(input);
|
|
230
|
+
const scr = ElasticAdapter.preparePatch(doc);
|
|
231
|
+
if (script) {
|
|
232
|
+
script.source = (script.source ? script.source + '\n' + script.source : '') + scr.source;
|
|
233
|
+
script.params = { ...script.params, ...scr.params };
|
|
234
|
+
}
|
|
235
|
+
else
|
|
236
|
+
script = scr;
|
|
237
|
+
}
|
|
238
|
+
script.source = script?.source || 'return;';
|
|
239
|
+
const filterQuery = ElasticAdapter.prepareFilter([
|
|
240
|
+
command.byId ? { ids: { values: [command.documentId] } } : undefined,
|
|
241
|
+
options?.filter,
|
|
242
|
+
options?.request?.query,
|
|
243
|
+
]);
|
|
244
|
+
let query = {
|
|
245
|
+
...options?.request?.query,
|
|
246
|
+
...filterQuery,
|
|
247
|
+
};
|
|
248
|
+
if (!Object.keys(query).length)
|
|
249
|
+
query = { match_all: {} };
|
|
250
|
+
const request = {
|
|
251
|
+
...options?.request,
|
|
252
|
+
index: this.getIndexName(),
|
|
253
|
+
script,
|
|
254
|
+
query,
|
|
255
|
+
};
|
|
256
|
+
const client = this.getClient();
|
|
257
|
+
return client.updateByQuery(request, options?.transport);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Generates an ID.
|
|
261
|
+
*
|
|
262
|
+
* @protected
|
|
263
|
+
* @returns The generated ID.
|
|
264
|
+
*/
|
|
265
|
+
_generateId(command) {
|
|
266
|
+
return typeof this.idGenerator === 'function' ? this.idGenerator(command, this) : undefined;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Retrieves the codec for the specified operation.
|
|
270
|
+
*
|
|
271
|
+
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
272
|
+
*/
|
|
273
|
+
_getInputCodec(operation) {
|
|
274
|
+
let validator = this._inputCodecs[operation];
|
|
275
|
+
if (validator)
|
|
276
|
+
return validator;
|
|
277
|
+
const options = { projection: '*' };
|
|
278
|
+
if (operation === 'update')
|
|
279
|
+
options.partial = 'deep';
|
|
280
|
+
const dataType = this.dataType;
|
|
281
|
+
validator = dataType.generateCodec('decode', options);
|
|
282
|
+
this._inputCodecs[operation] = validator;
|
|
283
|
+
return validator;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Retrieves the codec.
|
|
287
|
+
*/
|
|
288
|
+
_getOutputCodec(operation) {
|
|
289
|
+
let validator = this._outputCodecs[operation];
|
|
290
|
+
if (validator)
|
|
291
|
+
return validator;
|
|
292
|
+
const options = { projection: '*', partial: 'deep' };
|
|
293
|
+
const dataType = this.dataType;
|
|
294
|
+
validator = dataType.generateCodec('decode', options);
|
|
295
|
+
this._outputCodecs[operation] = validator;
|
|
296
|
+
return validator;
|
|
297
|
+
}
|
|
298
|
+
async _executeCommand(command, commandFn) {
|
|
299
|
+
try {
|
|
300
|
+
const result = await super._executeCommand(command, async () => {
|
|
301
|
+
/** Call before[X] hooks */
|
|
302
|
+
if (command.crud === 'create')
|
|
303
|
+
await this._beforeCreate(command);
|
|
304
|
+
else if (command.crud === 'update' && command.byId) {
|
|
305
|
+
await this._beforeUpdate(command);
|
|
306
|
+
}
|
|
307
|
+
else if (command.crud === 'update' && !command.byId) {
|
|
308
|
+
await this._beforeUpdateMany(command);
|
|
309
|
+
}
|
|
310
|
+
else if (command.crud === 'delete' && command.byId) {
|
|
311
|
+
await this._beforeDelete(command);
|
|
312
|
+
}
|
|
313
|
+
else if (command.crud === 'delete' && !command.byId) {
|
|
314
|
+
await this._beforeDeleteMany(command);
|
|
315
|
+
}
|
|
316
|
+
/** Call command function */
|
|
317
|
+
return commandFn();
|
|
318
|
+
});
|
|
319
|
+
/** Call after[X] hooks */
|
|
320
|
+
if (command.crud === 'create')
|
|
321
|
+
await this._afterCreate(command, result);
|
|
322
|
+
else if (command.crud === 'update' && command.byId) {
|
|
323
|
+
await this._afterUpdate(command, result);
|
|
324
|
+
}
|
|
325
|
+
else if (command.crud === 'update' && !command.byId) {
|
|
326
|
+
await this._afterUpdateMany(command, result);
|
|
327
|
+
}
|
|
328
|
+
else if (command.crud === 'delete' && command.byId) {
|
|
329
|
+
await this._afterDelete(command, result);
|
|
330
|
+
}
|
|
331
|
+
else if (command.crud === 'delete' && !command.byId) {
|
|
332
|
+
await this._afterDeleteMany(command, result);
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
catch (e) {
|
|
337
|
+
Error.captureStackTrace(e, this._executeCommand);
|
|
338
|
+
await this.onError?.(e, this);
|
|
339
|
+
throw e;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
343
|
+
async _beforeCreate(command) {
|
|
344
|
+
// Do nothing
|
|
345
|
+
}
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
347
|
+
async _beforeUpdate(command) {
|
|
348
|
+
// Do nothing
|
|
349
|
+
}
|
|
350
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
351
|
+
async _beforeUpdateMany(command) {
|
|
352
|
+
// Do nothing
|
|
353
|
+
}
|
|
354
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
355
|
+
async _beforeDelete(command) {
|
|
356
|
+
// Do nothing
|
|
357
|
+
}
|
|
358
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
359
|
+
async _beforeDeleteMany(command) {
|
|
360
|
+
// Do nothing
|
|
361
|
+
}
|
|
362
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
363
|
+
async _afterCreate(command, result) {
|
|
364
|
+
// Do nothing
|
|
365
|
+
}
|
|
366
|
+
async _afterUpdate(
|
|
367
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
368
|
+
command,
|
|
369
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
370
|
+
result) {
|
|
371
|
+
// Do nothing
|
|
372
|
+
}
|
|
373
|
+
async _afterUpdateMany(
|
|
374
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
375
|
+
command,
|
|
376
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
377
|
+
affected) {
|
|
378
|
+
// Do nothing
|
|
379
|
+
}
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
381
|
+
async _afterDelete(command, affected) {
|
|
382
|
+
// Do nothing
|
|
383
|
+
}
|
|
384
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
385
|
+
async _afterDeleteMany(command, affected) {
|
|
386
|
+
// Do nothing
|
|
387
|
+
}
|
|
388
|
+
}
|
package/esm/elastic-service.js
CHANGED
|
@@ -8,88 +8,14 @@ export class ElasticService extends ServiceBase {
|
|
|
8
8
|
/**
|
|
9
9
|
* Constructs a new instance
|
|
10
10
|
*
|
|
11
|
-
* @param dataType - The data type of the returning results
|
|
12
|
-
* @param indexName - The name of the index, or a function that returns the index name
|
|
13
11
|
* @param [options] - The options for the service
|
|
14
12
|
* @constructor
|
|
15
13
|
*/
|
|
16
|
-
constructor(
|
|
14
|
+
constructor(options) {
|
|
17
15
|
super();
|
|
18
|
-
this.
|
|
19
|
-
this._outputCodecs = {};
|
|
20
|
-
this._dataType_ = dataType;
|
|
16
|
+
this.interceptor = options?.interceptor;
|
|
21
17
|
this.client = options?.client;
|
|
22
|
-
this
|
|
23
|
-
this.$interceptor = this.$interceptor || options?.interceptor;
|
|
24
|
-
this.$indexName = indexName;
|
|
25
|
-
this.$resourceName = options?.resourceName;
|
|
26
|
-
this.$idGenerator = options?.idGenerator;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Retrieves the index name.
|
|
30
|
-
*
|
|
31
|
-
* @protected
|
|
32
|
-
* @returns The index name.
|
|
33
|
-
* @throws {Error} If the index name is not defined.
|
|
34
|
-
*/
|
|
35
|
-
getIndexName() {
|
|
36
|
-
const out = typeof this.$indexName === 'function' ? this.$indexName(this) : this.$indexName;
|
|
37
|
-
if (out)
|
|
38
|
-
return out;
|
|
39
|
-
throw new Error('indexName is not defined');
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Retrieves the resource name.
|
|
43
|
-
*
|
|
44
|
-
* @protected
|
|
45
|
-
* @returns {string} The resource name.
|
|
46
|
-
* @throws {Error} If the resource name is not defined.
|
|
47
|
-
*/
|
|
48
|
-
getResourceName() {
|
|
49
|
-
const out = typeof this.$resourceName === 'function' ? this.$resourceName(this) : this.$resourceName || this.getIndexName();
|
|
50
|
-
if (out)
|
|
51
|
-
return out;
|
|
52
|
-
throw new Error('resourceName is not defined');
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Retrieves the OPRA data type
|
|
56
|
-
*
|
|
57
|
-
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
58
|
-
*/
|
|
59
|
-
get dataType() {
|
|
60
|
-
if (!this._dataType)
|
|
61
|
-
this._dataType = this.context.document.node.getComplexType(this._dataType_);
|
|
62
|
-
return this._dataType;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Retrieves the codec for the specified operation.
|
|
66
|
-
*
|
|
67
|
-
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
68
|
-
*/
|
|
69
|
-
getInputCodec(operation) {
|
|
70
|
-
let validator = this._inputCodecs[operation];
|
|
71
|
-
if (validator)
|
|
72
|
-
return validator;
|
|
73
|
-
const options = { projection: '*' };
|
|
74
|
-
if (operation === 'update')
|
|
75
|
-
options.partial = 'deep';
|
|
76
|
-
const dataType = this.dataType;
|
|
77
|
-
validator = dataType.generateCodec('decode', options);
|
|
78
|
-
this._inputCodecs[operation] = validator;
|
|
79
|
-
return validator;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Retrieves the codec.
|
|
83
|
-
*/
|
|
84
|
-
getOutputCodec(operation) {
|
|
85
|
-
let validator = this._outputCodecs[operation];
|
|
86
|
-
if (validator)
|
|
87
|
-
return validator;
|
|
88
|
-
const options = { projection: '*', partial: 'deep' };
|
|
89
|
-
const dataType = this.dataType;
|
|
90
|
-
validator = dataType.generateCodec('decode', options);
|
|
91
|
-
this._outputCodecs[operation] = validator;
|
|
92
|
-
return validator;
|
|
18
|
+
this.onError = options?.onError;
|
|
93
19
|
}
|
|
94
20
|
/**
|
|
95
21
|
* Retrieves the ElasticSearch client.
|
|
@@ -105,35 +31,26 @@ export class ElasticService extends ServiceBase {
|
|
|
105
31
|
throw new Error(`Client not set!`);
|
|
106
32
|
return db;
|
|
107
33
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
* @returns {QueryDslQueryContainer | Promise<QueryDslQueryContainer> | undefined} The common filter or a Promise
|
|
123
|
-
* that resolves to the common filter, or undefined if not available.
|
|
124
|
-
*/
|
|
125
|
-
_getCommonFilter(info) {
|
|
126
|
-
return typeof this.$commonFilter === 'function' ? this.$commonFilter(info, this) : this.$commonFilter;
|
|
127
|
-
}
|
|
128
|
-
async _intercept(callback, info) {
|
|
34
|
+
async _executeCommand(command, commandFn) {
|
|
35
|
+
let proto;
|
|
36
|
+
const next = async () => {
|
|
37
|
+
proto = proto ? Object.getPrototypeOf(proto) : this;
|
|
38
|
+
while (proto) {
|
|
39
|
+
if (proto.interceptor && Object.prototype.hasOwnProperty.call(proto, 'interceptor')) {
|
|
40
|
+
return await proto.interceptor.call(this, next, command, this);
|
|
41
|
+
}
|
|
42
|
+
proto = Object.getPrototypeOf(proto);
|
|
43
|
+
if (!(proto instanceof ElasticService))
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
return commandFn();
|
|
47
|
+
};
|
|
129
48
|
try {
|
|
130
|
-
|
|
131
|
-
return this.$interceptor(callback, info, this);
|
|
132
|
-
return callback();
|
|
49
|
+
return await next();
|
|
133
50
|
}
|
|
134
51
|
catch (e) {
|
|
135
|
-
Error.captureStackTrace(e, this.
|
|
136
|
-
await this
|
|
52
|
+
Error.captureStackTrace(e, this._executeCommand);
|
|
53
|
+
await this.onError?.(e, this);
|
|
137
54
|
throw e;
|
|
138
55
|
}
|
|
139
56
|
}
|
package/esm/index.js
CHANGED
package/esm/package.json
ADDED
package/package.json
CHANGED
|
@@ -1,56 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/elastic",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "Opra Elastic Search adapter package",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/panates/opra.git",
|
|
10
|
-
"directory": "packages/elastic"
|
|
11
|
-
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"compile": "tsc",
|
|
14
|
-
"prebuild": "npm run lint && npm run clean",
|
|
15
|
-
"build": "npm run build:cjs && npm run build:esm",
|
|
16
|
-
"build:cjs": "tsc -b tsconfig-build-cjs.json",
|
|
17
|
-
"build:esm": "tsc -b tsconfig-build-esm.json",
|
|
18
|
-
"postbuild": "cp README.md package.json ../../LICENSE ../../build/elastic && cp ../../package.cjs.json ../../build/elastic/cjs/package.json",
|
|
19
|
-
"lint": "eslint . --max-warnings=0",
|
|
20
|
-
"lint:fix": "eslint . --max-warnings=0 --fix",
|
|
21
|
-
"format": "prettier . --write --log-level=warn",
|
|
22
|
-
"test": "jest --passWithNoTests",
|
|
23
|
-
"cover": "jest --passWithNoTests --collect-coverage",
|
|
24
|
-
"clean": "npm run clean:src && npm run clean:test && npm run clean:dist && npm run clean:cover",
|
|
25
|
-
"clean:src": "ts-cleanup -s src --all",
|
|
26
|
-
"clean:test": "ts-cleanup -s test --all",
|
|
27
|
-
"clean:dist": "rimraf ../../build/client",
|
|
28
|
-
"clean:cover": "rimraf ../../coverage/client"
|
|
29
|
-
},
|
|
30
7
|
"dependencies": {
|
|
8
|
+
"lodash.omit": "^4.5.0",
|
|
31
9
|
"putil-isplainobject": "^1.1.5",
|
|
32
|
-
"tslib": "^2.
|
|
33
|
-
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@elastic/elasticsearch": "^8.14.0",
|
|
36
|
-
"@faker-js/faker": "^8.4.1",
|
|
37
|
-
"ts-gems": "^3.4.0"
|
|
10
|
+
"tslib": "^2.7.0",
|
|
11
|
+
"valgen": "^5.9.0"
|
|
38
12
|
},
|
|
39
13
|
"peerDependencies": {
|
|
40
14
|
"@elastic/elasticsearch": ">=8.7.0",
|
|
41
|
-
"@opra/common": "^1.0.0-
|
|
42
|
-
"@opra/core": "^1.0.0-
|
|
15
|
+
"@opra/common": "^1.0.0-beta.1",
|
|
16
|
+
"@opra/core": "^1.0.0-beta.1"
|
|
43
17
|
},
|
|
44
18
|
"type": "module",
|
|
45
|
-
"
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./types/index.d.ts",
|
|
23
|
+
"default": "./esm/index.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./types/index.d.cts",
|
|
27
|
+
"default": "./cjs/index.js"
|
|
28
|
+
},
|
|
29
|
+
"default": "./esm/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
46
33
|
"main": "./cjs/index.js",
|
|
34
|
+
"module": "./esm/index.js",
|
|
47
35
|
"types": "./types/index.d.ts",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/panates/opra.git",
|
|
39
|
+
"directory": "packages/elastic"
|
|
40
|
+
},
|
|
48
41
|
"engines": {
|
|
49
42
|
"node": ">=16.0",
|
|
50
43
|
"npm": ">=7.0.0"
|
|
51
44
|
},
|
|
52
45
|
"files": [
|
|
53
|
-
"bin/",
|
|
54
46
|
"cjs/",
|
|
55
47
|
"esm/",
|
|
56
48
|
"types/",
|
|
@@ -63,4 +55,4 @@
|
|
|
63
55
|
"elasticsearch",
|
|
64
56
|
"adapter"
|
|
65
57
|
]
|
|
66
|
-
}
|
|
58
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import '@opra/core';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
|
3
|
+
import { type ElasticAdapter } from '../elastic-adapter.js';
|
|
4
|
+
export default function prepareFilter(filters: ElasticAdapter.FilterInput | ElasticAdapter.FilterInput[]): QueryDslQueryContainer | undefined;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ComplexType, FieldsProjection } from '@opra/common';
|
|
2
|
+
export interface ElasticProjection {
|
|
3
|
+
includes?: string[];
|
|
4
|
+
excludes?: string[];
|
|
5
|
+
}
|
|
6
|
+
export default function prepareProjection(dataType: ComplexType, projection?: string | string[]): ElasticProjection | undefined;
|
|
7
|
+
export declare function prepare(dataType: ComplexType, includes: string[], excludes: string[], curPath: string, projection?: FieldsProjection): void;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types.js';
|
|
2
|
+
import { OpraFilter } from '@opra/common';
|
|
1
3
|
import { HttpContext } from '@opra/core';
|
|
2
4
|
import _prepareFilter from './adapter-utils/prepare-filter.js';
|
|
3
|
-
import
|
|
5
|
+
import _preparePatch from './adapter-utils/prepare-patch.js';
|
|
6
|
+
import _prepareProjection from './adapter-utils/prepare-projection.js';
|
|
4
7
|
import _prepareSort from './adapter-utils/prepare-sort.js';
|
|
5
8
|
export declare namespace ElasticAdapter {
|
|
9
|
+
type FilterInput = OpraFilter.Expression | QueryDslQueryContainer | string | undefined;
|
|
6
10
|
const prepareFilter: typeof _prepareFilter;
|
|
7
|
-
const
|
|
11
|
+
const preparePatch: typeof _preparePatch;
|
|
12
|
+
const prepareProjection: typeof _prepareProjection;
|
|
8
13
|
const prepareSort: typeof _prepareSort;
|
|
9
14
|
interface TransformedRequest {
|
|
10
15
|
method: 'create' | 'delete' | 'deleteMany' | 'get' | 'findMany' | 'update' | 'updateMany';
|