@travetto/model-elasticsearch 8.0.0-alpha.16 → 8.0.0-alpha.17
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 +2 -2
- package/package.json +6 -6
- package/src/index-manager.ts +3 -3
- package/src/internal/query.ts +8 -2
- package/src/service.ts +51 -26
package/README.md
CHANGED
|
@@ -13,13 +13,13 @@ 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#L41) 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/types/crud.ts#L11)
|
|
20
20
|
* [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#L64)
|
|
21
21
|
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10)
|
|
22
|
-
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#
|
|
22
|
+
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L16)
|
|
23
23
|
* [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/types/crud.ts#L11)
|
|
24
24
|
* [Facet](https://github.com/travetto/travetto/tree/main/module/model-query/src/types/facet.ts#L14)
|
|
25
25
|
* [Query](https://github.com/travetto/travetto/tree/main/module/model-query/src/types/query.ts#L10)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-elasticsearch",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.",
|
|
6
6
|
"keywords": [
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@elastic/elasticsearch": "^9.3.4",
|
|
32
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
33
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
34
|
-
"@travetto/model-indexed": "^8.0.0-alpha.
|
|
35
|
-
"@travetto/model-query": "^8.0.0-alpha.
|
|
32
|
+
"@travetto/config": "^8.0.0-alpha.15",
|
|
33
|
+
"@travetto/model": "^8.0.0-alpha.15",
|
|
34
|
+
"@travetto/model-indexed": "^8.0.0-alpha.17",
|
|
35
|
+
"@travetto/model-query": "^8.0.0-alpha.16"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
38
|
+
"@travetto/cli": "^8.0.0-alpha.20"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/cli": {
|
package/src/index-manager.ts
CHANGED
|
@@ -144,8 +144,8 @@ export class IndexManager implements ModelStorageSupport {
|
|
|
144
144
|
|
|
145
145
|
async deleteStorage(): Promise<void> {
|
|
146
146
|
console.debug('Deleting storage', { idx: this.getNamespacedIndex('*') });
|
|
147
|
-
await this.#client.indices.delete({
|
|
148
|
-
|
|
149
|
-
});
|
|
147
|
+
// await this.#client.indices.delete({
|
|
148
|
+
// index: this.getNamespacedIndex('*')
|
|
149
|
+
// });
|
|
150
150
|
}
|
|
151
151
|
}
|
package/src/internal/query.ts
CHANGED
|
@@ -141,7 +141,13 @@ export class ElasticsearchQueryUtil {
|
|
|
141
141
|
}
|
|
142
142
|
case '$regex': {
|
|
143
143
|
const pattern = DataUtil.toRegex(castTo(value));
|
|
144
|
-
if (pattern.source.startsWith('
|
|
144
|
+
if (pattern.source.startsWith('^')) { // We have a prefix query
|
|
145
|
+
if (/^\^[A-Za-z0-9_\-]+/.test(pattern.source)) {
|
|
146
|
+
items.push({ prefix: { [subPath]: pattern.source.substring(1) } });
|
|
147
|
+
} else {
|
|
148
|
+
items.push({ regexp: { [subPath]: pattern.source.substring(1) } });
|
|
149
|
+
}
|
|
150
|
+
} else if (pattern.source.startsWith('\\b') && pattern.source.endsWith('.*') && declaredSchema.specifiers?.includes('text')) {
|
|
145
151
|
const textField = !pattern.flags.includes('i') && config && config.caseSensitive ?
|
|
146
152
|
`${subPath}.text_cs` :
|
|
147
153
|
`${subPath}.text`;
|
|
@@ -198,7 +204,7 @@ export class ElasticsearchQueryUtil {
|
|
|
198
204
|
if (ModelQueryUtil.has$And(clause)) {
|
|
199
205
|
return { bool: { must: clause.$and.map(item => this.extractWhereQuery<T>(cls, item, config)) } };
|
|
200
206
|
} else if (ModelQueryUtil.has$Or(clause)) {
|
|
201
|
-
return { bool: { should: clause.$or.map(item => this.extractWhereQuery<T>(cls, item, config)),
|
|
207
|
+
return { bool: { should: clause.$or.map(item => this.extractWhereQuery<T>(cls, item, config)), minimum_should_match: 1 } };
|
|
202
208
|
} else if (ModelQueryUtil.has$Not(clause)) {
|
|
203
209
|
return { bool: { ['must_not']: this.extractWhereQuery<T>(cls, clause.$not, config) } };
|
|
204
210
|
} else {
|
package/src/service.ts
CHANGED
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
type ModelQuery, type ModelQueryCrudSupport, type ModelQueryFacetSupport, type ModelQuerySupport, type PageableModelQuery,
|
|
15
15
|
type Query, type ValidStringFields, QueryVerifier, type ModelQuerySuggestSupport, ModelQueryUtil, ModelQuerySuggestUtil,
|
|
16
16
|
ModelQueryCrudUtil, type ModelQueryFacet,
|
|
17
|
+
type WhereClause,
|
|
17
18
|
} from '@travetto/model-query';
|
|
18
19
|
import {
|
|
19
20
|
type ModelIndexedSupport, type KeyedIndexSelection, type KeyedIndexBody, type ModelPageOptions, ModelIndexedUtil,
|
|
20
21
|
type SingleItemIndex, type SortedIndexSelection, type ModelPageResult, type SortedIndex, type FullKeyedIndexBody,
|
|
21
|
-
type FullKeyedIndexWithPartialBody, ModelIndexedComputedIndex
|
|
22
|
+
type FullKeyedIndexWithPartialBody, ModelIndexedComputedIndex, type ModelIndexedSearchOptions, type SortedIndexSelectionType
|
|
22
23
|
} from '@travetto/model-indexed';
|
|
23
24
|
|
|
24
25
|
import type { ElasticsearchModelConfig } from './config.ts';
|
|
@@ -119,27 +120,32 @@ export class ElasticsearchModelService implements
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
async * #scrollIndex<
|
|
123
|
-
T extends ModelType,
|
|
124
|
-
K extends KeyedIndexSelection<T>,
|
|
125
|
-
S extends SortedIndexSelection<T>
|
|
126
|
-
>(
|
|
123
|
+
async * #scrollIndex<T extends ModelType>(
|
|
127
124
|
cls: Class<T>,
|
|
128
|
-
idx: SortedIndex<T
|
|
129
|
-
body: KeyedIndexBody<T
|
|
130
|
-
options?: ModelPageOptions<estypes.SortResults> & ModelListOptions
|
|
125
|
+
idx: SortedIndex<T>,
|
|
126
|
+
body: KeyedIndexBody<T>,
|
|
127
|
+
options?: ModelPageOptions<estypes.SortResults> & ModelListOptions,
|
|
128
|
+
transformWhere?: (where: WhereClause<T>) => WhereClause<T>
|
|
131
129
|
): AsyncIterable<{
|
|
132
130
|
items: T[];
|
|
133
131
|
nextOffset?: estypes.SortResults | undefined;
|
|
134
132
|
}> {
|
|
135
133
|
const computed = ModelIndexedComputedIndex.get(idx, body).validate();
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
let whereClause: WhereClause<T> = castTo(computed.project());
|
|
135
|
+
if (transformWhere) {
|
|
136
|
+
whereClause = transformWhere(whereClause);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
yield* this.#scrollCollection(cls, (offset) => {
|
|
140
|
+
const result = {
|
|
141
|
+
query: ElasticsearchQueryUtil.getSearchQuery(cls,
|
|
142
|
+
ElasticsearchQueryUtil.extractWhereQuery(cls, whereClause)
|
|
143
|
+
),
|
|
144
|
+
...(offset ? { search_after: offset } : {}),
|
|
145
|
+
sort: ElasticsearchQueryUtil.getSort(idx)
|
|
146
|
+
};
|
|
147
|
+
return result;
|
|
148
|
+
}, options);
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
@PostConstruct()
|
|
@@ -415,19 +421,19 @@ export class ElasticsearchModelService implements
|
|
|
415
421
|
S extends SortedIndexSelection<T>
|
|
416
422
|
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<T> {
|
|
417
423
|
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
424
|
+
const projected = computed.project({
|
|
425
|
+
sort: true,
|
|
426
|
+
includeId: true,
|
|
427
|
+
emptyValue: { $exists: true }
|
|
428
|
+
});
|
|
418
429
|
|
|
419
430
|
const result = await this.execSearch<T>(cls, {
|
|
420
431
|
query: ElasticsearchQueryUtil.getSearchQuery(cls,
|
|
421
|
-
ElasticsearchQueryUtil.extractWhereTermQuery(cls,
|
|
422
|
-
sort: true,
|
|
423
|
-
includeId: true,
|
|
424
|
-
emptyValue: { $exists: true }
|
|
425
|
-
}))
|
|
426
|
-
|
|
432
|
+
ElasticsearchQueryUtil.extractWhereTermQuery(cls, projected)
|
|
427
433
|
)
|
|
428
434
|
});
|
|
429
435
|
if (!result.hits.hits.length) {
|
|
430
|
-
throw new NotFoundError(`${cls.name}: ${idx}`, computed.getKey({ sort: true }));
|
|
436
|
+
throw new NotFoundError(`${cls.name}: ${idx.name}`, computed.getKey({ sort: true }));
|
|
431
437
|
}
|
|
432
438
|
return this.#postLoad(cls, result.hits.hits[0]);
|
|
433
439
|
|
|
@@ -515,6 +521,25 @@ export class ElasticsearchModelService implements
|
|
|
515
521
|
}
|
|
516
522
|
}
|
|
517
523
|
|
|
524
|
+
async suggestByIndex<
|
|
525
|
+
T extends ModelType,
|
|
526
|
+
S extends SortedIndexSelection<T>,
|
|
527
|
+
K extends KeyedIndexSelection<T>,
|
|
528
|
+
B extends SortedIndexSelectionType<T, S> & string
|
|
529
|
+
>(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>, prefix: B, options?: ModelIndexedSearchOptions): Promise<T[]> {
|
|
530
|
+
const items: T[] = [];
|
|
531
|
+
for await (const { items: fetched } of this.#scrollIndex(cls, idx, body, { limit: 10, ...options },
|
|
532
|
+
where => castTo({
|
|
533
|
+
$and: [where, {
|
|
534
|
+
[idx.sortTemplate[0].path.join('.')]: { $regex: ModelIndexedUtil.getSuggestRegex(prefix) }
|
|
535
|
+
}]
|
|
536
|
+
})
|
|
537
|
+
)) {
|
|
538
|
+
items.push(...fetched);
|
|
539
|
+
}
|
|
540
|
+
return items;
|
|
541
|
+
}
|
|
542
|
+
|
|
518
543
|
// Query
|
|
519
544
|
async query<T extends ModelType>(cls: Class<T>, query: PageableModelQuery<T>): Promise<T[]> {
|
|
520
545
|
await QueryVerifier.verify(cls, query);
|
|
@@ -618,7 +643,7 @@ export class ElasticsearchModelService implements
|
|
|
618
643
|
}
|
|
619
644
|
|
|
620
645
|
// Query Facet
|
|
621
|
-
async
|
|
646
|
+
async suggestByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Promise<T[]> {
|
|
622
647
|
await QueryVerifier.verify(cls, query);
|
|
623
648
|
|
|
624
649
|
const resolvedQuery = ModelQuerySuggestUtil.getSuggestQuery<T>(cls, field, prefix, query);
|
|
@@ -628,7 +653,7 @@ export class ElasticsearchModelService implements
|
|
|
628
653
|
return ModelQuerySuggestUtil.combineSuggestResults(cls, field, prefix, all, (_, value) => value, query && query.limit);
|
|
629
654
|
}
|
|
630
655
|
|
|
631
|
-
async
|
|
656
|
+
async suggestValuesByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Promise<string[]> {
|
|
632
657
|
await QueryVerifier.verify(cls, query);
|
|
633
658
|
|
|
634
659
|
const resolvedQuery = ModelQuerySuggestUtil.getSuggestQuery<T>(cls, field, prefix, {
|
|
@@ -642,7 +667,7 @@ export class ElasticsearchModelService implements
|
|
|
642
667
|
}
|
|
643
668
|
|
|
644
669
|
// Facet
|
|
645
|
-
async
|
|
670
|
+
async facetByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<ModelQueryFacet[]> {
|
|
646
671
|
await QueryVerifier.verify(cls, query);
|
|
647
672
|
|
|
648
673
|
const resolvedSearch = ElasticsearchQueryUtil.getSearchObject(cls, query ?? {}, this.config.schemaConfig);
|