options.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import { DeepRequired, ID, Product, ProductVariant } from '@vendure/core';
  2. import deepmerge from 'deepmerge';
  3. import { CustomMapping, ElasticSearchInput } from './types';
  4. /**
  5. * @description
  6. * Configuration options for the {@link ElasticsearchPlugin}.
  7. *
  8. * @docsCategory ElasticsearchPlugin
  9. * @docsPage ElasticsearchOptions
  10. */
  11. export interface ElasticsearchOptions {
  12. /**
  13. * @description
  14. * The host of the Elasticsearch server.
  15. */
  16. host: string;
  17. /**
  18. * @description
  19. * The port of the Elasticsearch server.
  20. */
  21. port: number;
  22. /**
  23. * @description
  24. * Prefix for the indices created by the plugin.
  25. *
  26. * @default
  27. * 'vendure-'
  28. */
  29. indexPrefix?: string;
  30. /**
  31. * @description
  32. * Batch size for bulk operations (e.g. when rebuilding the indices).
  33. *
  34. * @default
  35. * 2000
  36. */
  37. batchSize?: number;
  38. /**
  39. * @description
  40. * Configuration of the internal Elasticseach query.
  41. */
  42. searchConfig?: SearchConfig;
  43. /**
  44. * @description
  45. * Custom mappings may be defined which will add the defined data to the
  46. * Elasticsearch index and expose that data via the SearchResult GraphQL type,
  47. * adding a new `customMappings` field.
  48. *
  49. * The `graphQlType` property may be one of `String`, `Int`, `Float`, `Boolean` and
  50. * can be appended with a `!` to indicate non-nullable fields.
  51. *
  52. * This config option defines custom mappings which are accessible when the "groupByProduct"
  53. * input options is set to `true`.
  54. *
  55. * @example
  56. * ```TypeScript
  57. * customProductMappings: {
  58. * variantCount: {
  59. * graphQlType: 'Int!',
  60. * valueFn: (product, variants) => variants.length,
  61. * },
  62. * reviewRating: {
  63. * graphQlType: 'Float',
  64. * valueFn: product => (product.customFields as any).reviewRating,
  65. * },
  66. * }
  67. * ```
  68. *
  69. * @example
  70. * ```SDL
  71. * query SearchProducts($input: SearchInput!) {
  72. * search(input: $input) {
  73. * totalItems
  74. * items {
  75. * productId
  76. * productName
  77. * customMappings {
  78. * ...on CustomProductMappings {
  79. * variantCount
  80. * reviewRating
  81. * }
  82. * }
  83. * }
  84. * }
  85. * }
  86. * ```
  87. */
  88. customProductMappings?: {
  89. [fieldName: string]: CustomMapping<[Product, ProductVariant[]]>;
  90. };
  91. /**
  92. * @description
  93. * This config option defines custom mappings which are accessible when the "groupByProduct"
  94. * input options is set to `false`.
  95. *
  96. * @example
  97. * ```SDL
  98. * query SearchProducts($input: SearchInput!) {
  99. * search(input: $input) {
  100. * totalItems
  101. * items {
  102. * productId
  103. * productName
  104. * customMappings {
  105. * ...on CustomProductVariantMappings {
  106. * weight
  107. * }
  108. * }
  109. * }
  110. * }
  111. * }
  112. * ```
  113. */
  114. customProductVariantMappings?: {
  115. [fieldName: string]: CustomMapping<[ProductVariant]>;
  116. };
  117. }
  118. /**
  119. * @description
  120. * Configuration options for the internal Elasticsearch query which is generated when performing a search.
  121. *
  122. * @docsCategory ElasticsearchPlugin
  123. * @docsPage ElasticsearchOptions
  124. */
  125. export interface SearchConfig {
  126. /**
  127. * @description
  128. * The maximum number of FacetValues to return from the search query. Internally, this
  129. * value sets the "size" property of an Elasticsearch aggregation.
  130. *
  131. * @default
  132. * 50
  133. */
  134. facetValueMaxSize?: number;
  135. // prettier-ignore
  136. /**
  137. * @description
  138. * Defines the
  139. * [multi match type](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#multi-match-types)
  140. * used when matching against a search term.
  141. *
  142. * @default
  143. * 'best_fields'
  144. */
  145. multiMatchType?: 'best_fields' | 'most_fields' | 'cross_fields' | 'phrase' | 'phrase_prefix' | 'bool_prefix';
  146. /**
  147. * @description
  148. * Set custom boost values for particular fields when matching against a search term.
  149. */
  150. boostFields?: BoostFieldsConfig;
  151. /**
  152. * @description
  153. * The interval used to group search results into buckets according to price range. For example, setting this to
  154. * `2000` will group into buckets every $20.00:
  155. *
  156. * ```JSON
  157. * {
  158. * "data": {
  159. * "search": {
  160. * "totalItems": 32,
  161. * "priceRange": {
  162. * "buckets": [
  163. * {
  164. * "to": 2000,
  165. * "count": 21
  166. * },
  167. * {
  168. * "to": 4000,
  169. * "count": 7
  170. * },
  171. * {
  172. * "to": 6000,
  173. * "count": 3
  174. * },
  175. * {
  176. * "to": 12000,
  177. * "count": 1
  178. * }
  179. * ]
  180. * }
  181. * }
  182. * }
  183. * }
  184. * ```
  185. */
  186. priceRangeBucketInterval?: number;
  187. /**
  188. * @description
  189. * This config option allows the the modification of the whole (already built) search query. This allows
  190. * for e.g. wildcard / fuzzy searches on the index.
  191. *
  192. * @example
  193. * ```TypeScript
  194. * mapQuery: (query, input, searchConfig, channelId, enabledOnly){
  195. * if(query.bool.must){
  196. * delete query.bool.must;
  197. * }
  198. * query.bool.should = [
  199. * {
  200. * query_string: {
  201. * query: "*" + term + "*",
  202. * fields: [
  203. * `productName^${searchConfig.boostFields.productName}`,
  204. * `productVariantName^${searchConfig.boostFields.productVariantName}`,
  205. * ]
  206. * }
  207. * },
  208. * {
  209. * multi_match: {
  210. * query: term,
  211. * type: searchConfig.multiMatchType,
  212. * fields: [
  213. * `description^${searchConfig.boostFields.description}`,
  214. * `sku^${searchConfig.boostFields.sku}`,
  215. * ],
  216. * },
  217. * },
  218. * ];
  219. *
  220. * return query;
  221. * }
  222. * ```
  223. */
  224. mapQuery?: (
  225. query: any,
  226. input: ElasticSearchInput,
  227. searchConfig: DeepRequired<SearchConfig>,
  228. channelId: ID,
  229. enabledOnly: boolean,
  230. ) => any;
  231. }
  232. /**
  233. * @description
  234. * Configuration for [boosting](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#field-boost)
  235. * the scores of given fields when performing a search against a term.
  236. *
  237. * Boosting a field acts as a score multiplier for matches against that field.
  238. *
  239. * @docsCategory ElasticsearchPlugin
  240. * @docsPage ElasticsearchOptions
  241. */
  242. export interface BoostFieldsConfig {
  243. /**
  244. * @description
  245. * Defines the boost factor for the productName field.
  246. *
  247. * @default 1
  248. */
  249. productName?: number;
  250. /**
  251. * @description
  252. * Defines the boost factor for the productVariantName field.
  253. *
  254. * @default 1
  255. */
  256. productVariantName?: number;
  257. /**
  258. * @description
  259. * Defines the boost factor for the description field.
  260. *
  261. * @default 1
  262. */
  263. description?: number;
  264. /**
  265. * @description
  266. * Defines the boost factor for the sku field.
  267. *
  268. * @default 1
  269. */
  270. sku?: number;
  271. }
  272. export const defaultOptions: DeepRequired<ElasticsearchOptions> = {
  273. host: 'http://localhost',
  274. port: 9200,
  275. indexPrefix: 'vendure-',
  276. batchSize: 2000,
  277. searchConfig: {
  278. facetValueMaxSize: 50,
  279. multiMatchType: 'best_fields',
  280. boostFields: {
  281. productName: 1,
  282. productVariantName: 1,
  283. description: 1,
  284. sku: 1,
  285. },
  286. priceRangeBucketInterval: 1000,
  287. mapQuery: (query) => query,
  288. },
  289. customProductMappings: {},
  290. customProductVariantMappings: {},
  291. };
  292. export function mergeWithDefaults(userOptions: ElasticsearchOptions): DeepRequired<ElasticsearchOptions> {
  293. return deepmerge(defaultOptions, userOptions) as DeepRequired<ElasticsearchOptions>;
  294. }