@travetto/model-elasticsearch 5.0.18 → 5.0.20
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/README.md +6 -1
- package/package.json +5 -5
- package/src/config.ts +5 -0
- package/src/index-manager.ts +2 -3
- package/src/internal/query.ts +9 -46
- package/src/internal/schema.ts +9 -9
- package/src/service.ts +61 -39
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @travetto/model-elasticsearch
|
|
|
13
13
|
yarn add @travetto/model-elasticsearch
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
This module provides an [elasticsearch](https://elastic.co)-based implementation of the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [elasticsearch](https://elastic.co). In development mode, [ElasticsearchModelService](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch/src/service.ts#
|
|
16
|
+
This module provides an [elasticsearch](https://elastic.co)-based implementation of the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [elasticsearch](https://elastic.co). In development mode, [ElasticsearchModelService](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch/src/service.ts#L37) will also modify the [elasticsearch](https://elastic.co) schema in real time to minimize impact to development.
|
|
17
17
|
|
|
18
18
|
Supported features:
|
|
19
19
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11)
|
|
@@ -76,6 +76,11 @@ export class ElasticsearchModelConfig {
|
|
|
76
76
|
* Auto-create, disabled in prod by default
|
|
77
77
|
*/
|
|
78
78
|
autoCreate?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Should we store the id as a string in the document
|
|
81
|
+
*/
|
|
82
|
+
storeId?: boolean;
|
|
83
|
+
|
|
79
84
|
/**
|
|
80
85
|
* Base schema config for elasticsearch
|
|
81
86
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-elasticsearch",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.20",
|
|
4
4
|
"description": "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"elasticsearch",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@elastic/elasticsearch": "^8.17.0",
|
|
31
|
-
"@travetto/cli": "^5.0.
|
|
32
|
-
"@travetto/config": "^5.0.
|
|
33
|
-
"@travetto/model": "^5.0.
|
|
34
|
-
"@travetto/model-query": "^5.0.
|
|
31
|
+
"@travetto/cli": "^5.0.19",
|
|
32
|
+
"@travetto/config": "^5.0.16",
|
|
33
|
+
"@travetto/model": "^5.0.17",
|
|
34
|
+
"@travetto/model-query": "^5.0.17"
|
|
35
35
|
},
|
|
36
36
|
"travetto": {
|
|
37
37
|
"displayName": "Elasticsearch Model Source"
|
package/src/config.ts
CHANGED
|
@@ -29,6 +29,11 @@ export class ElasticsearchModelConfig {
|
|
|
29
29
|
* Auto-create, disabled in prod by default
|
|
30
30
|
*/
|
|
31
31
|
autoCreate?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Should we store the id as a string in the document
|
|
34
|
+
*/
|
|
35
|
+
storeId?: boolean;
|
|
36
|
+
|
|
32
37
|
/**
|
|
33
38
|
* Base schema config for elasticsearch
|
|
34
39
|
*/
|
package/src/index-manager.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Client } from '@elastic/elasticsearch';
|
|
2
|
-
import { ReindexRequest } from '@elastic/elasticsearch/lib/api/types';
|
|
1
|
+
import { Client, estypes } from '@elastic/elasticsearch';
|
|
3
2
|
|
|
4
3
|
import { Class } from '@travetto/runtime';
|
|
5
4
|
import { ModelRegistry, ModelType } from '@travetto/model';
|
|
@@ -164,7 +163,7 @@ export class IndexManager implements ModelStorageSupport {
|
|
|
164
163
|
|
|
165
164
|
const allChange = removes.concat(fieldChanges);
|
|
166
165
|
|
|
167
|
-
const reindexBody: ReindexRequest = {
|
|
166
|
+
const reindexBody: estypes.ReindexRequest = {
|
|
168
167
|
source: { index: curr },
|
|
169
168
|
dest: { index: next },
|
|
170
169
|
script: {
|
package/src/internal/query.ts
CHANGED
|
@@ -78,6 +78,10 @@ export class ElasticsearchQueryUtil {
|
|
|
78
78
|
((key === 'id' && !path) ? '_id' : `${path}${key}`) :
|
|
79
79
|
`${path}${key}`;
|
|
80
80
|
|
|
81
|
+
const sPathQuery = (val: unknown): {} => (key === 'id' && !path) ?
|
|
82
|
+
{ ids: { values: Array.isArray(val) ? val : [val] } } :
|
|
83
|
+
{ [Array.isArray(val) ? 'terms' : 'term']: { [sPath]: val } };
|
|
84
|
+
|
|
81
85
|
if (DataUtil.isPlainObject(top)) {
|
|
82
86
|
const subKey = Object.keys(top)[0];
|
|
83
87
|
if (!subKey.startsWith('$')) {
|
|
@@ -100,29 +104,19 @@ export class ElasticsearchQueryUtil {
|
|
|
100
104
|
break;
|
|
101
105
|
}
|
|
102
106
|
case '$in': {
|
|
103
|
-
items.push(
|
|
107
|
+
items.push(sPathQuery(Array.isArray(v) ? v : [v]));
|
|
104
108
|
break;
|
|
105
109
|
}
|
|
106
110
|
case '$nin': {
|
|
107
|
-
items.push({
|
|
108
|
-
bool: {
|
|
109
|
-
['must_not']: [{
|
|
110
|
-
terms: {
|
|
111
|
-
[sPath]: Array.isArray(v) ? v : [v]
|
|
112
|
-
}
|
|
113
|
-
}]
|
|
114
|
-
}
|
|
115
|
-
});
|
|
111
|
+
items.push({ bool: { ['must_not']: [sPathQuery(Array.isArray(v) ? v : [v])] } });
|
|
116
112
|
break;
|
|
117
113
|
}
|
|
118
114
|
case '$eq': {
|
|
119
|
-
items.push(
|
|
115
|
+
items.push(sPathQuery(v));
|
|
120
116
|
break;
|
|
121
117
|
}
|
|
122
118
|
case '$ne': {
|
|
123
|
-
items.push({
|
|
124
|
-
bool: { ['must_not']: [{ term: { [sPath]: v } }] }
|
|
125
|
-
});
|
|
119
|
+
items.push({ bool: { ['must_not']: [sPathQuery(v)] } });
|
|
126
120
|
break;
|
|
127
121
|
}
|
|
128
122
|
case '$exists': {
|
|
@@ -183,11 +177,7 @@ export class ElasticsearchQueryUtil {
|
|
|
183
177
|
}
|
|
184
178
|
// Handle operations
|
|
185
179
|
} else {
|
|
186
|
-
items.push(
|
|
187
|
-
[Array.isArray(top) ? 'terms' : 'term']: {
|
|
188
|
-
[(key === 'id' && !path) ? '_id' : `${path}${key}`]: top
|
|
189
|
-
}
|
|
190
|
-
});
|
|
180
|
+
items.push(sPathQuery(top));
|
|
191
181
|
}
|
|
192
182
|
}
|
|
193
183
|
if (items.length === 1) {
|
|
@@ -281,31 +271,4 @@ export class ElasticsearchQueryUtil {
|
|
|
281
271
|
|
|
282
272
|
return search;
|
|
283
273
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Safely load the data, excluding ids if needed
|
|
288
|
-
*/
|
|
289
|
-
static cleanIdRemoval<T>(req: estypes.SearchRequest, results: estypes.SearchResponse<T>): T[] {
|
|
290
|
-
const out: T[] = [];
|
|
291
|
-
|
|
292
|
-
const toArr = <V>(x: V | V[] | undefined): V[] => (x ? (Array.isArray(x) ? x : [x]) : []);
|
|
293
|
-
|
|
294
|
-
// determine if id
|
|
295
|
-
const select = [
|
|
296
|
-
toArr(req._source_includes),
|
|
297
|
-
toArr(req._source_excludes)
|
|
298
|
-
];
|
|
299
|
-
const includeId = select[0].includes('_id') || (select[0].length === 0 && !select[1].includes('_id'));
|
|
300
|
-
|
|
301
|
-
for (const r of results.hits.hits) {
|
|
302
|
-
const obj = r._source!;
|
|
303
|
-
if (includeId) {
|
|
304
|
-
castTo<{ _id: string }>(obj)._id = r._id!;
|
|
305
|
-
}
|
|
306
|
-
out.push(obj);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return out;
|
|
310
|
-
}
|
|
311
274
|
}
|
package/src/internal/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { estypes } from '@elastic/elasticsearch';
|
|
2
2
|
|
|
3
3
|
import { Class } from '@travetto/runtime';
|
|
4
4
|
import { ModelRegistry } from '@travetto/model';
|
|
@@ -15,8 +15,8 @@ export class ElasticsearchSchemaUtil {
|
|
|
15
15
|
/**
|
|
16
16
|
* Build the update script for a given object
|
|
17
17
|
*/
|
|
18
|
-
static generateUpdateScript(o: Record<string, unknown>):
|
|
19
|
-
const out:
|
|
18
|
+
static generateUpdateScript(o: Record<string, unknown>): estypes.Script {
|
|
19
|
+
const out: estypes.Script = {
|
|
20
20
|
lang: 'painless',
|
|
21
21
|
source: `
|
|
22
22
|
for (entry in params.body.entrySet()) {
|
|
@@ -42,7 +42,7 @@ export class ElasticsearchSchemaUtil {
|
|
|
42
42
|
* @param o
|
|
43
43
|
* @returns
|
|
44
44
|
*/
|
|
45
|
-
static generateReplaceScript(o: Record<string, unknown>):
|
|
45
|
+
static generateReplaceScript(o: Record<string, unknown>): estypes.Script {
|
|
46
46
|
return {
|
|
47
47
|
lang: 'painless',
|
|
48
48
|
source: 'ctx._source.clear(); ctx._source.putAll(params.body)',
|
|
@@ -53,7 +53,7 @@ export class ElasticsearchSchemaUtil {
|
|
|
53
53
|
/**
|
|
54
54
|
* Build one or more mappings depending on the polymorphic state
|
|
55
55
|
*/
|
|
56
|
-
static generateSchemaMapping(cls: Class, config?: EsSchemaConfig): MappingTypeMapping {
|
|
56
|
+
static generateSchemaMapping(cls: Class, config?: EsSchemaConfig): estypes.MappingTypeMapping {
|
|
57
57
|
return ModelRegistry.get(cls).baseType ?
|
|
58
58
|
this.generateAllMapping(cls, config) :
|
|
59
59
|
this.generateSingleMapping(cls, config);
|
|
@@ -62,9 +62,9 @@ export class ElasticsearchSchemaUtil {
|
|
|
62
62
|
/**
|
|
63
63
|
* Generate all mappings
|
|
64
64
|
*/
|
|
65
|
-
static generateAllMapping(cls: Class, config?: EsSchemaConfig): MappingTypeMapping {
|
|
65
|
+
static generateAllMapping(cls: Class, config?: EsSchemaConfig): estypes.MappingTypeMapping {
|
|
66
66
|
const allTypes = ModelRegistry.getClassesByBaseType(cls);
|
|
67
|
-
return allTypes.reduce<MappingTypeMapping>((acc, schemaCls) => {
|
|
67
|
+
return allTypes.reduce<estypes.MappingTypeMapping>((acc, schemaCls) => {
|
|
68
68
|
DataUtil.deepAssign(acc, this.generateSingleMapping(schemaCls, config));
|
|
69
69
|
return acc;
|
|
70
70
|
}, { properties: {}, dynamic: false });
|
|
@@ -73,10 +73,10 @@ export class ElasticsearchSchemaUtil {
|
|
|
73
73
|
/**
|
|
74
74
|
* Build a mapping for a given class
|
|
75
75
|
*/
|
|
76
|
-
static generateSingleMapping<T>(cls: Class<T>, config?: EsSchemaConfig): MappingTypeMapping {
|
|
76
|
+
static generateSingleMapping<T>(cls: Class<T>, config?: EsSchemaConfig): estypes.MappingTypeMapping {
|
|
77
77
|
const schema = SchemaRegistry.getViewSchema(cls);
|
|
78
78
|
|
|
79
|
-
const props: Record<string, MappingProperty> = {};
|
|
79
|
+
const props: Record<string, estypes.MappingProperty> = {};
|
|
80
80
|
|
|
81
81
|
for (const field of schema.fields) {
|
|
82
82
|
const conf = schema.schema[field];
|
package/src/service.ts
CHANGED
|
@@ -30,10 +30,6 @@ import { ElasticsearchQueryUtil } from './internal/query';
|
|
|
30
30
|
import { ElasticsearchSchemaUtil } from './internal/schema';
|
|
31
31
|
import { IndexManager } from './index-manager';
|
|
32
32
|
|
|
33
|
-
type WithId<T> = T & { _id?: string };
|
|
34
|
-
|
|
35
|
-
const isWithId = <T extends ModelType>(o: T): o is WithId<T> => !o && '_id' in o;
|
|
36
|
-
|
|
37
33
|
/**
|
|
38
34
|
* Elasticsearch model source.
|
|
39
35
|
*/
|
|
@@ -74,13 +70,34 @@ export class ElasticsearchModelService implements
|
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
|
|
73
|
+
preUpdate(o: { id: string }): string;
|
|
74
|
+
preUpdate(o: {}): undefined;
|
|
75
|
+
preUpdate(o: { id?: string }): string | undefined {
|
|
76
|
+
if ('id' in o && typeof o.id === 'string') {
|
|
77
|
+
const id = o.id;
|
|
78
|
+
if (!this.config.storeId) {
|
|
79
|
+
delete o.id;
|
|
80
|
+
}
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
postUpdate<T extends ModelType>(o: T, id?: string): T {
|
|
87
|
+
if (!this.config.storeId) {
|
|
88
|
+
o.id = id!;
|
|
89
|
+
}
|
|
90
|
+
return o;
|
|
91
|
+
}
|
|
92
|
+
|
|
77
93
|
/**
|
|
78
94
|
* Convert _id to id
|
|
79
95
|
*/
|
|
80
|
-
async postLoad<T extends ModelType>(cls: Class<T>,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
async postLoad<T extends ModelType>(cls: Class<T>, inp: estypes.SearchHit<T> | estypes.GetGetResult<T>): Promise<T> {
|
|
97
|
+
let item = {
|
|
98
|
+
...(inp._id ? { id: inp._id } : {}),
|
|
99
|
+
...inp._source!,
|
|
100
|
+
};
|
|
84
101
|
|
|
85
102
|
item = await ModelCrudUtil.load(cls, item);
|
|
86
103
|
|
|
@@ -120,8 +137,8 @@ export class ElasticsearchModelService implements
|
|
|
120
137
|
|
|
121
138
|
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
122
139
|
try {
|
|
123
|
-
const res = await this.client.get({ ...this.manager.getIdentity(cls), id });
|
|
124
|
-
return this.postLoad(cls,
|
|
140
|
+
const res = await this.client.get<T>({ ...this.manager.getIdentity(cls), id });
|
|
141
|
+
return this.postLoad(cls, res);
|
|
125
142
|
} catch {
|
|
126
143
|
throw new NotFoundError(cls, id);
|
|
127
144
|
}
|
|
@@ -150,7 +167,7 @@ export class ElasticsearchModelService implements
|
|
|
150
167
|
async create<T extends ModelType>(cls: Class<T>, o: OptionalId<T>): Promise<T> {
|
|
151
168
|
try {
|
|
152
169
|
const clean = await ModelCrudUtil.preStore(cls, o, this);
|
|
153
|
-
const id = clean
|
|
170
|
+
const id = this.preUpdate(clean);
|
|
154
171
|
|
|
155
172
|
await this.client.index({
|
|
156
173
|
...this.manager.getIdentity(cls),
|
|
@@ -159,7 +176,7 @@ export class ElasticsearchModelService implements
|
|
|
159
176
|
body: clean
|
|
160
177
|
});
|
|
161
178
|
|
|
162
|
-
return clean;
|
|
179
|
+
return this.postUpdate(clean, id);
|
|
163
180
|
} catch (err) {
|
|
164
181
|
console.error(err);
|
|
165
182
|
throw err;
|
|
@@ -171,7 +188,7 @@ export class ElasticsearchModelService implements
|
|
|
171
188
|
|
|
172
189
|
o = await ModelCrudUtil.preStore(cls, o, this);
|
|
173
190
|
|
|
174
|
-
const id = o
|
|
191
|
+
const id = this.preUpdate(o);
|
|
175
192
|
|
|
176
193
|
if (ModelRegistry.get(cls).expiresAt) {
|
|
177
194
|
await this.get(cls, id);
|
|
@@ -185,18 +202,18 @@ export class ElasticsearchModelService implements
|
|
|
185
202
|
body: o
|
|
186
203
|
});
|
|
187
204
|
|
|
188
|
-
o
|
|
189
|
-
return o;
|
|
205
|
+
return this.postUpdate(o, id);
|
|
190
206
|
}
|
|
191
207
|
|
|
192
208
|
async upsert<T extends ModelType>(cls: Class<T>, o: OptionalId<T>): Promise<T> {
|
|
193
209
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
194
210
|
|
|
195
211
|
const item = await ModelCrudUtil.preStore(cls, o, this);
|
|
212
|
+
const id = this.preUpdate(item);
|
|
196
213
|
|
|
197
214
|
await this.client.update({
|
|
198
215
|
...this.manager.getIdentity(cls),
|
|
199
|
-
id
|
|
216
|
+
id,
|
|
200
217
|
refresh: true,
|
|
201
218
|
body: {
|
|
202
219
|
doc: item,
|
|
@@ -204,18 +221,15 @@ export class ElasticsearchModelService implements
|
|
|
204
221
|
}
|
|
205
222
|
});
|
|
206
223
|
|
|
207
|
-
return item;
|
|
224
|
+
return this.postUpdate(item, id);
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
async updatePartial<T extends ModelType>(cls: Class<T>, data: Partial<T> & { id: string }, view?: string): Promise<T> {
|
|
211
228
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
212
229
|
|
|
213
|
-
const
|
|
214
|
-
|
|
230
|
+
const id = data.id;
|
|
231
|
+
const item = castTo<typeof data>(await ModelCrudUtil.prePartialUpdate(cls, data, view));
|
|
215
232
|
const script = ElasticsearchSchemaUtil.generateUpdateScript(item);
|
|
216
|
-
const id = item.id!;
|
|
217
|
-
|
|
218
|
-
console.debug('Partial Script', { script });
|
|
219
233
|
|
|
220
234
|
try {
|
|
221
235
|
await this.client.update({
|
|
@@ -246,7 +260,7 @@ export class ElasticsearchModelService implements
|
|
|
246
260
|
while (search.hits.hits.length > 0) {
|
|
247
261
|
for (const el of search.hits.hits) {
|
|
248
262
|
try {
|
|
249
|
-
yield this.postLoad(cls, el
|
|
263
|
+
yield this.postLoad(cls, el);
|
|
250
264
|
} catch (err) {
|
|
251
265
|
if (!(err instanceof NotFoundError)) {
|
|
252
266
|
throw err;
|
|
@@ -272,11 +286,14 @@ export class ElasticsearchModelService implements
|
|
|
272
286
|
if (op.delete) {
|
|
273
287
|
acc.push({ delete: { ...ident, _id: op.delete.id } });
|
|
274
288
|
} else if (op.insert) {
|
|
275
|
-
|
|
289
|
+
const id = this.preUpdate(op.insert);
|
|
290
|
+
acc.push({ create: { ...ident, _id: id } }, castTo(op.insert));
|
|
276
291
|
} else if (op.upsert) {
|
|
277
|
-
|
|
292
|
+
const id = this.preUpdate(op.upsert);
|
|
293
|
+
acc.push({ index: { ...ident, _id: id } }, castTo(op.upsert));
|
|
278
294
|
} else if (op.update) {
|
|
279
|
-
|
|
295
|
+
const id = this.preUpdate(op.update);
|
|
296
|
+
acc.push({ update: { ...ident, _id: id } }, { doc: op.update });
|
|
280
297
|
}
|
|
281
298
|
return acc;
|
|
282
299
|
}, []);
|
|
@@ -350,7 +367,7 @@ export class ElasticsearchModelService implements
|
|
|
350
367
|
if (!res.hits.hits.length) {
|
|
351
368
|
throw new NotFoundError(`${cls.name}: ${idx}`, key);
|
|
352
369
|
}
|
|
353
|
-
return this.postLoad(cls, res.hits.hits[0]
|
|
370
|
+
return this.postLoad(cls, res.hits.hits[0]);
|
|
354
371
|
}
|
|
355
372
|
|
|
356
373
|
async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void> {
|
|
@@ -388,7 +405,7 @@ export class ElasticsearchModelService implements
|
|
|
388
405
|
while (search.hits.hits.length > 0) {
|
|
389
406
|
for (const el of search.hits.hits) {
|
|
390
407
|
try {
|
|
391
|
-
yield this.postLoad(cls, el
|
|
408
|
+
yield this.postLoad(cls, el);
|
|
392
409
|
} catch (err) {
|
|
393
410
|
if (!(err instanceof NotFoundError)) {
|
|
394
411
|
throw err;
|
|
@@ -408,8 +425,13 @@ export class ElasticsearchModelService implements
|
|
|
408
425
|
|
|
409
426
|
const req = ElasticsearchQueryUtil.getSearchObject(cls, query, this.config.schemaConfig);
|
|
410
427
|
const results = await this.execSearch(cls, req);
|
|
411
|
-
const
|
|
412
|
-
return Promise.all(
|
|
428
|
+
const shouldRemoveIds = query.select && 'id' in query.select && !query.select.id;
|
|
429
|
+
return Promise.all(results.hits.hits.map(m => this.postLoad(cls, m).then(v => {
|
|
430
|
+
if (shouldRemoveIds) {
|
|
431
|
+
delete castTo<OptionalId<T>>(v).id;
|
|
432
|
+
}
|
|
433
|
+
return v;
|
|
434
|
+
})));
|
|
413
435
|
}
|
|
414
436
|
|
|
415
437
|
async queryOne<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany: boolean = true): Promise<T> {
|
|
@@ -431,10 +453,12 @@ export class ElasticsearchModelService implements
|
|
|
431
453
|
await QueryVerifier.verify(cls, query);
|
|
432
454
|
|
|
433
455
|
const item = await ModelCrudUtil.preStore(cls, data, this);
|
|
434
|
-
const id = item
|
|
456
|
+
const id = this.preUpdate(item);
|
|
435
457
|
|
|
436
458
|
const where = ModelQueryUtil.getWhereClause(cls, query.where);
|
|
437
|
-
|
|
459
|
+
if (id) {
|
|
460
|
+
where.id = id;
|
|
461
|
+
}
|
|
438
462
|
query.where = where;
|
|
439
463
|
|
|
440
464
|
if (ModelRegistry.get(cls).expiresAt) {
|
|
@@ -465,7 +489,7 @@ export class ElasticsearchModelService implements
|
|
|
465
489
|
}
|
|
466
490
|
}
|
|
467
491
|
|
|
468
|
-
return item;
|
|
492
|
+
return this.postUpdate(item, id);
|
|
469
493
|
}
|
|
470
494
|
|
|
471
495
|
async deleteByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T> = {}): Promise<number> {
|
|
@@ -484,7 +508,6 @@ export class ElasticsearchModelService implements
|
|
|
484
508
|
await QueryVerifier.verify(cls, query);
|
|
485
509
|
|
|
486
510
|
const item = await ModelCrudUtil.prePartialUpdate(cls, data);
|
|
487
|
-
|
|
488
511
|
const script = ElasticsearchSchemaUtil.generateUpdateScript(item);
|
|
489
512
|
|
|
490
513
|
const search = ElasticsearchQueryUtil.getSearchObject(cls, query, this.config.schemaConfig);
|
|
@@ -505,9 +528,8 @@ export class ElasticsearchModelService implements
|
|
|
505
528
|
const q = ModelQuerySuggestUtil.getSuggestQuery<T>(cls, field, prefix, query);
|
|
506
529
|
const search = ElasticsearchQueryUtil.getSearchObject(cls, q);
|
|
507
530
|
const res = await this.execSearch(cls, search);
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
return Promise.all(combined.map(m => this.postLoad(cls, m)));
|
|
531
|
+
const all = await Promise.all(res.hits.hits.map(x => this.postLoad(cls, x)));
|
|
532
|
+
return ModelQuerySuggestUtil.combineSuggestResults(cls, field, prefix, all, (x, v) => v, query && query.limit);
|
|
511
533
|
}
|
|
512
534
|
|
|
513
535
|
async suggestValues<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Promise<string[]> {
|
|
@@ -519,8 +541,8 @@ export class ElasticsearchModelService implements
|
|
|
519
541
|
});
|
|
520
542
|
const search = ElasticsearchQueryUtil.getSearchObject(cls, q);
|
|
521
543
|
const res = await this.execSearch(cls, search);
|
|
522
|
-
const
|
|
523
|
-
return ModelQuerySuggestUtil.combineSuggestResults(cls, field, prefix,
|
|
544
|
+
const all = await Promise.all(res.hits.hits.map(x => castTo<T>(({ [field]: field === 'id' ? x._id : x._source![field] }))));
|
|
545
|
+
return ModelQuerySuggestUtil.combineSuggestResults(cls, field, prefix, all, x => x, query && query.limit);
|
|
524
546
|
}
|
|
525
547
|
|
|
526
548
|
// Facet
|