| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- import { ClientOptions } from '@elastic/elasticsearch';
- import { DeepRequired, ID, LanguageCode, Product, ProductVariant } from '@vendure/core';
- import deepmerge from 'deepmerge';
- import { CustomMapping, ElasticSearchInput } from './types';
- /**
- * @description
- * Configuration options for the {@link ElasticsearchPlugin}.
- *
- * @docsCategory ElasticsearchPlugin
- * @docsPage ElasticsearchOptions
- */
- export interface ElasticsearchOptions {
- /**
- * @description
- * The host of the Elasticsearch server. May also be specified in `clientOptions.node`.
- *
- * @default 'http://localhost'
- */
- host?: string;
- /**
- * @description
- * The port of the Elasticsearch server. May also be specified in `clientOptions.node`.
- *
- * @default 9200
- */
- port?: number;
- /**
- * @description
- * Maximum amount of attempts made to connect to the ElasticSearch server on startup.
- *
- * @default 10
- */
- connectionAttempts?: number;
- /**
- * @description
- * Interval in milliseconds between attempts to connect to the ElasticSearch server on startup.
- *
- * @default 5000
- */
- connectionAttemptInterval?: number;
- /**
- * @description
- * Options to pass directly to the
- * [Elasticsearch Node.js client](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html). For example, to
- * set authentication or other more advanced options.
- * Note that if the `node` or `nodes` option is specified, it will override the values provided in the `host` and `port` options.
- */
- clientOptions?: ClientOptions;
- /**
- * @description
- * Prefix for the indices created by the plugin.
- *
- * @default
- * 'vendure-'
- */
- indexPrefix?: string;
- /**
- * @description
- * [These options](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html#index-modules-settings)
- * are directly passed to index settings. To apply some settings indices will be recreated.
- *
- * @example
- * ```TypeScript
- * // Configuring an English stemmer
- * indexSettings: {
- * analysis: {
- * analyzer: {
- * custom_analyzer: {
- * tokenizer: 'standard',
- * filter: [
- * 'lowercase',
- * 'english_stemmer'
- * ]
- * }
- * },
- * filter : {
- * english_stemmer : {
- * type : 'stemmer',
- * name : 'english'
- * }
- * }
- * }
- * },
- * ```
- *
- * @since 1.2.0
- * @default
- * {}
- */
- indexSettings?: object;
- /**
- * @description
- * This option allow to redefine or define new properties in mapping. More about elastic
- * [mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html)
- * After changing this option indices will be recreated.
- *
- * @example
- * ```TypeScript
- * // Configuring custom analyzer for the `productName` field.
- * indexMappingProperties: {
- * productName: {
- * type: 'text',
- * analyzer:'custom_analyzer',
- * fields: {
- * keyword: {
- * type: 'keyword',
- * ignore_above: 256,
- * }
- * }
- * }
- * }
- * ```
- *
- * @since 1.2.0
- * @default
- * {}
- */
- indexMappingProperties?: object;
- /**
- * @description
- * Batch size for bulk operations (e.g. when rebuilding the indices).
- *
- * @default
- * 2000
- */
- batchSize?: number;
- /**
- * @description
- * Configuration of the internal Elasticseach query.
- */
- searchConfig?: SearchConfig;
- /**
- * @description
- * Custom mappings may be defined which will add the defined data to the
- * Elasticsearch index and expose that data via the SearchResult GraphQL type,
- * adding a new `customMappings` field.
- *
- * The `graphQlType` property may be one of `String`, `Int`, `Float`, `Boolean` and
- * can be appended with a `!` to indicate non-nullable fields.
- *
- * This config option defines custom mappings which are accessible when the "groupByProduct"
- * input options is set to `true`.
- *
- * @example
- * ```TypeScript
- * customProductMappings: {
- * variantCount: {
- * graphQlType: 'Int!',
- * valueFn: (product, variants) => variants.length,
- * },
- * reviewRating: {
- * graphQlType: 'Float',
- * valueFn: product => (product.customFields as any).reviewRating,
- * },
- * }
- * ```
- *
- * @example
- * ```SDL
- * query SearchProducts($input: SearchInput!) {
- * search(input: $input) {
- * totalItems
- * items {
- * productId
- * productName
- * customMappings {
- * ...on CustomProductMappings {
- * variantCount
- * reviewRating
- * }
- * }
- * }
- * }
- * }
- * ```
- */
- customProductMappings?: {
- [fieldName: string]: CustomMapping<[Product, ProductVariant[], LanguageCode]>;
- };
- /**
- * @description
- * This config option defines custom mappings which are accessible when the "groupByProduct"
- * input options is set to `false`.
- *
- * @example
- * ```SDL
- * query SearchProducts($input: SearchInput!) {
- * search(input: $input) {
- * totalItems
- * items {
- * productId
- * productName
- * customMappings {
- * ...on CustomProductVariantMappings {
- * weight
- * }
- * }
- * }
- * }
- * }
- * ```
- */
- customProductVariantMappings?: {
- [fieldName: string]: CustomMapping<[ProductVariant, LanguageCode]>;
- };
- }
- /**
- * @description
- * Configuration options for the internal Elasticsearch query which is generated when performing a search.
- *
- * @docsCategory ElasticsearchPlugin
- * @docsPage ElasticsearchOptions
- */
- export interface SearchConfig {
- /**
- * @description
- * The maximum number of FacetValues to return from the search query. Internally, this
- * value sets the "size" property of an Elasticsearch aggregation.
- *
- * @default
- * 50
- */
- facetValueMaxSize?: number;
- /**
- * @description
- * The maximum number of Collections to return from the search query. Internally, this
- * value sets the "size" property of an Elasticsearch aggregation.
- *
- * @since 1.1.0
- * @default
- * 50
- */
- collectionMaxSize?: number;
- /**
- * @description
- * The maximum number of totalItems to return from the search query. Internally, this
- * value sets the "track_total_hits" property of an Elasticsearch query.
- * If this parameter is set to "True", accurate count of totalItems will be returned.
- * If this parameter is set to "False", totalItems will be returned as 0.
- * If this parameter is set to integer, accurate count of totalItems will be returned not bigger than integer.
- *
- * @since 1.2.0
- * @default
- * 10000
- */
- totalItemsMaxSize?: number | boolean;
- // prettier-ignore
- /**
- * @description
- * Defines the
- * [multi match type](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#multi-match-types)
- * used when matching against a search term.
- *
- * @default
- * 'best_fields'
- */
- multiMatchType?: 'best_fields' | 'most_fields' | 'cross_fields' | 'phrase' | 'phrase_prefix' | 'bool_prefix';
- /**
- * @description
- * Set custom boost values for particular fields when matching against a search term.
- */
- boostFields?: BoostFieldsConfig;
- /**
- * @description
- * The interval used to group search results into buckets according to price range. For example, setting this to
- * `2000` will group into buckets every $20.00:
- *
- * ```JSON
- * {
- * "data": {
- * "search": {
- * "totalItems": 32,
- * "priceRange": {
- * "buckets": [
- * {
- * "to": 2000,
- * "count": 21
- * },
- * {
- * "to": 4000,
- * "count": 7
- * },
- * {
- * "to": 6000,
- * "count": 3
- * },
- * {
- * "to": 12000,
- * "count": 1
- * }
- * ]
- * }
- * }
- * }
- * }
- * ```
- */
- priceRangeBucketInterval?: number;
- /**
- * @description
- * This config option allows the the modification of the whole (already built) search query. This allows
- * for e.g. wildcard / fuzzy searches on the index.
- *
- * @example
- * ```TypeScript
- * mapQuery: (query, input, searchConfig, channelId, enabledOnly){
- * if(query.bool.must){
- * delete query.bool.must;
- * }
- * query.bool.should = [
- * {
- * query_string: {
- * query: "*" + term + "*",
- * fields: [
- * `productName^${searchConfig.boostFields.productName}`,
- * `productVariantName^${searchConfig.boostFields.productVariantName}`,
- * ]
- * }
- * },
- * {
- * multi_match: {
- * query: term,
- * type: searchConfig.multiMatchType,
- * fields: [
- * `description^${searchConfig.boostFields.description}`,
- * `sku^${searchConfig.boostFields.sku}`,
- * ],
- * },
- * },
- * ];
- *
- * return query;
- * }
- * ```
- */
- mapQuery?: (
- query: any,
- input: ElasticSearchInput,
- searchConfig: DeepRequired<SearchConfig>,
- channelId: ID,
- enabledOnly: boolean,
- ) => any;
- }
- /**
- * @description
- * Configuration for [boosting](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#field-boost)
- * the scores of given fields when performing a search against a term.
- *
- * Boosting a field acts as a score multiplier for matches against that field.
- *
- * @docsCategory ElasticsearchPlugin
- * @docsPage ElasticsearchOptions
- */
- export interface BoostFieldsConfig {
- /**
- * @description
- * Defines the boost factor for the productName field.
- *
- * @default 1
- */
- productName?: number;
- /**
- * @description
- * Defines the boost factor for the productVariantName field.
- *
- * @default 1
- */
- productVariantName?: number;
- /**
- * @description
- * Defines the boost factor for the description field.
- *
- * @default 1
- */
- description?: number;
- /**
- * @description
- * Defines the boost factor for the sku field.
- *
- * @default 1
- */
- sku?: number;
- }
- export type ElasticsearchRuntimeOptions = DeepRequired<Omit<ElasticsearchOptions, 'clientOptions'>> & {
- clientOptions?: ClientOptions;
- };
- export const defaultOptions: ElasticsearchRuntimeOptions = {
- host: 'http://localhost',
- port: 9200,
- connectionAttempts: 10,
- connectionAttemptInterval: 5000,
- indexPrefix: 'vendure-',
- indexSettings: {},
- indexMappingProperties: {},
- batchSize: 2000,
- searchConfig: {
- facetValueMaxSize: 50,
- collectionMaxSize: 50,
- totalItemsMaxSize: 10000,
- multiMatchType: 'best_fields',
- boostFields: {
- productName: 1,
- productVariantName: 1,
- description: 1,
- sku: 1,
- },
- priceRangeBucketInterval: 1000,
- mapQuery: query => query,
- },
- customProductMappings: {},
- customProductVariantMappings: {},
- };
- export function mergeWithDefaults(userOptions: ElasticsearchOptions): ElasticsearchRuntimeOptions {
- const { clientOptions, ...pluginOptions } = userOptions;
- const merged = deepmerge(defaultOptions, pluginOptions) as ElasticsearchRuntimeOptions;
- return { ...merged, clientOptions };
- }
|