-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
fulltext-search.service.ts
128 lines (119 loc) · 5.09 KB
/
fulltext-search.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { Inject, Injectable } from '@nestjs/common';
import { SearchInput, SearchResponse } from '@vendure/common/lib/generated-types';
import { Omit } from '@vendure/common/lib/omit';
import { RequestContext } from '../../api/common/request-context';
import { InternalServerError } from '../../common/error/errors';
import { TransactionalConnection } from '../../connection/transactional-connection';
import { Collection, FacetValue } from '../../entity';
import { EventBus } from '../../event-bus/event-bus';
import { Job } from '../../job-queue/job';
import { CollectionService } from '../../service/services/collection.service';
import { FacetValueService } from '../../service/services/facet-value.service';
import { ProductVariantService } from '../../service/services/product-variant.service';
import { SearchService } from '../../service/services/search.service';
import { PLUGIN_INIT_OPTIONS } from './constants';
import { SearchIndexService } from './indexer/search-index.service';
import { MysqlSearchStrategy } from './search-strategy/mysql-search-strategy';
import { PostgresSearchStrategy } from './search-strategy/postgres-search-strategy';
import { SearchStrategy } from './search-strategy/search-strategy';
import { SqliteSearchStrategy } from './search-strategy/sqlite-search-strategy';
import { DefaultSearchPluginInitOptions } from './types';
/**
* Search indexing and full-text search for supported databases. See the various
* SearchStrategy implementations for db-specific code.
*/
@Injectable()
export class FulltextSearchService {
private searchStrategy: SearchStrategy;
private readonly minTermLength = 2;
constructor(
private connection: TransactionalConnection,
private eventBus: EventBus,
private facetValueService: FacetValueService,
private collectionService: CollectionService,
private productVariantService: ProductVariantService,
private searchIndexService: SearchIndexService,
private searchService: SearchService,
@Inject(PLUGIN_INIT_OPTIONS) private options: DefaultSearchPluginInitOptions,
) {
this.searchService.adopt(this);
this.setSearchStrategy();
}
/**
* Perform a fulltext search according to the provided input arguments.
*/
async search(
ctx: RequestContext,
input: SearchInput,
enabledOnly: boolean = false,
): Promise<Omit<Omit<SearchResponse, 'facetValues'>, 'collections'>> {
const items = await this.searchStrategy.getSearchResults(ctx, input, enabledOnly);
const totalItems = await this.searchStrategy.getTotalCount(ctx, input, enabledOnly);
return {
items,
totalItems,
};
}
/**
* Return a list of all FacetValues which appear in the result set.
*/
async facetValues(
ctx: RequestContext,
input: SearchInput,
enabledOnly: boolean = false,
): Promise<Array<{ facetValue: FacetValue; count: number }>> {
const facetValueIdsMap = await this.searchStrategy.getFacetValueIds(ctx, input, enabledOnly);
const facetValues = await this.facetValueService.findByIds(ctx, Array.from(facetValueIdsMap.keys()));
return facetValues.map((facetValue, index) => {
return {
facetValue,
count: facetValueIdsMap.get(facetValue.id.toString()) as number,
};
});
}
/**
* Return a list of all Collections which appear in the result set.
*/
async collections(
ctx: RequestContext,
input: SearchInput,
enabledOnly: boolean = false,
): Promise<Array<{ collection: Collection; count: number }>> {
const collectionIdsMap = await this.searchStrategy.getCollectionIds(ctx, input, enabledOnly);
const collections = await this.collectionService.findByIds(ctx, Array.from(collectionIdsMap.keys()));
return collections.map((collection, index) => {
return {
collection,
count: collectionIdsMap.get(collection.id.toString()) as number,
};
});
}
/**
* Rebuilds the full search index.
*/
async reindex(ctx: RequestContext): Promise<Job> {
const job = await this.searchIndexService.reindex(ctx);
return job as any;
}
/**
* Sets the SearchStrategy appropriate to th configured database type.
*/
private setSearchStrategy() {
switch (this.connection.rawConnection.options.type) {
case 'mysql':
case 'mariadb':
this.searchStrategy = new MysqlSearchStrategy(this.connection, this.options);
break;
case 'sqlite':
case 'sqljs':
case 'better-sqlite3':
this.searchStrategy = new SqliteSearchStrategy(this.connection, this.options);
break;
case 'postgres':
this.searchStrategy = new PostgresSearchStrategy(this.connection, this.options);
break;
default:
throw new InternalServerError(`error.database-not-supported-by-default-search-plugin`);
}
}
}