@deenruv/elasticsearch-plugin 1.0.0

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.
Files changed (48) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +122 -0
  3. package/lib/index.d.ts +3 -0
  4. package/lib/index.js +20 -0
  5. package/lib/index.js.map +1 -0
  6. package/lib/src/api/api-extensions.d.ts +3 -0
  7. package/lib/src/api/api-extensions.js +148 -0
  8. package/lib/src/api/api-extensions.js.map +1 -0
  9. package/lib/src/api/custom-mappings.resolver.d.ts +12 -0
  10. package/lib/src/api/custom-mappings.resolver.js +47 -0
  11. package/lib/src/api/custom-mappings.resolver.js.map +1 -0
  12. package/lib/src/api/custom-script-fields.resolver.d.ts +12 -0
  13. package/lib/src/api/custom-script-fields.resolver.js +50 -0
  14. package/lib/src/api/custom-script-fields.resolver.js.map +1 -0
  15. package/lib/src/api/elasticsearch-resolver.d.ts +34 -0
  16. package/lib/src/api/elasticsearch-resolver.js +150 -0
  17. package/lib/src/api/elasticsearch-resolver.js.map +1 -0
  18. package/lib/src/build-elastic-body.d.ts +8 -0
  19. package/lib/src/build-elastic-body.js +173 -0
  20. package/lib/src/build-elastic-body.js.map +1 -0
  21. package/lib/src/constants.d.ts +3 -0
  22. package/lib/src/constants.js +7 -0
  23. package/lib/src/constants.js.map +1 -0
  24. package/lib/src/elasticsearch.health.d.ts +9 -0
  25. package/lib/src/elasticsearch.health.js +52 -0
  26. package/lib/src/elasticsearch.health.js.map +1 -0
  27. package/lib/src/elasticsearch.service.d.ts +56 -0
  28. package/lib/src/elasticsearch.service.js +469 -0
  29. package/lib/src/elasticsearch.service.js.map +1 -0
  30. package/lib/src/indexing/elasticsearch-index.service.d.ts +24 -0
  31. package/lib/src/indexing/elasticsearch-index.service.js +163 -0
  32. package/lib/src/indexing/elasticsearch-index.service.js.map +1 -0
  33. package/lib/src/indexing/indexer.controller.d.ts +98 -0
  34. package/lib/src/indexing/indexer.controller.js +790 -0
  35. package/lib/src/indexing/indexer.controller.js.map +1 -0
  36. package/lib/src/indexing/indexing-utils.d.ts +8 -0
  37. package/lib/src/indexing/indexing-utils.js +109 -0
  38. package/lib/src/indexing/indexing-utils.js.map +1 -0
  39. package/lib/src/options.d.ts +695 -0
  40. package/lib/src/options.js +59 -0
  41. package/lib/src/options.js.map +1 -0
  42. package/lib/src/plugin.d.ts +192 -0
  43. package/lib/src/plugin.js +371 -0
  44. package/lib/src/plugin.js.map +1 -0
  45. package/lib/src/types.d.ts +262 -0
  46. package/lib/src/types.js +17 -0
  47. package/lib/src/types.js.map +1 -0
  48. package/package.json +45 -0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ # License 1
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 2025-present Aexol
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12
+
13
+ # License 2
14
+
15
+ The MIT License
16
+
17
+ Copyright (c) 2018-2025 Michael Bromley
18
+
19
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @deenruv/elasticsearch-plugin
2
+
3
+ Replaces the default Deenruv product search with [Elasticsearch](https://www.elastic.co/elasticsearch/) for advanced full-text search, faceted filtering, price range queries, custom mappings, and geo-spatial search.
4
+
5
+ **Requires Elasticsearch v7.0 or higher** (below v7.10.2 due to client compatibility).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @deenruv/elasticsearch-plugin @elastic/elasticsearch
11
+ ```
12
+
13
+ Make sure to remove `DefaultSearchPlugin` from your config if present.
14
+
15
+ ## Configuration
16
+
17
+ ```typescript
18
+ import { ElasticsearchPlugin } from '@deenruv/elasticsearch-plugin';
19
+
20
+ const config = {
21
+ plugins: [
22
+ ElasticsearchPlugin.init({
23
+ host: 'http://localhost',
24
+ port: 9200,
25
+ }),
26
+ ],
27
+ };
28
+ ```
29
+
30
+ **Options:**
31
+
32
+ | Option | Type | Default | Description |
33
+ |--------|------|---------|-------------|
34
+ | `host` | `string` | `'http://localhost'` | Elasticsearch host |
35
+ | `port` | `number` | `9200` | Elasticsearch port |
36
+ | `clientOptions` | `ClientOptions` | - | Direct Elasticsearch client options (overrides host/port) |
37
+ | `connectionAttempts` | `number` | `10` | Max connection retry attempts on startup |
38
+ | `connectionAttemptInterval` | `number` | `5000` | Interval (ms) between connection attempts |
39
+ | `indexPrefix` | `string` | `'deenruv-'` | Prefix for Elasticsearch indices |
40
+ | `indexSettings` | `object` | `{}` | Elasticsearch index settings (analyzers, filters, etc.) |
41
+ | `indexMappingProperties` | `object` | `{}` | Custom index mapping properties |
42
+ | `customProductMappings` | `object` | `{}` | Custom fields indexed from Products |
43
+ | `customProductVariantMappings` | `object` | `{}` | Custom fields indexed from ProductVariants |
44
+ | `searchConfig` | `SearchConfig` | See below | Internal search query configuration |
45
+ | `bufferUpdates` | `boolean` | `false` | Buffer index updates for manual batch execution |
46
+ | `hydrateProductRelations` | `string[]` | `[]` | Additional Product relations to fetch during reindexing |
47
+ | `hydrateProductVariantRelations` | `string[]` | `[]` | Additional ProductVariant relations to fetch during reindexing |
48
+ | `extendSearchInputType` | `object` | `{}` | Add custom fields to the `SearchInput` GraphQL type |
49
+ | `extendSearchSortType` | `string[]` | `[]` | Add custom sort parameters |
50
+
51
+ **SearchConfig defaults:**
52
+
53
+ | Option | Default | Description |
54
+ |--------|---------|-------------|
55
+ | `facetValueMaxSize` | `50` | Max FacetValues returned per search |
56
+ | `collectionMaxSize` | `50` | Max Collections returned per search |
57
+ | `totalItemsMaxSize` | `10000` | Max total items tracked |
58
+ | `multiMatchType` | `'best_fields'` | Elasticsearch multi-match query type |
59
+ | `priceRangeBucketInterval` | `1000` | Price range bucket interval (in cents) |
60
+ | `mapQuery` | identity | Custom query transformation function |
61
+ | `mapSort` | identity | Custom sort transformation function |
62
+ | `scriptFields` | `{}` | Computed script fields (e.g. geo-distance) |
63
+
64
+ ## Features
65
+
66
+ - Drop-in replacement for `DefaultSearchPlugin`
67
+ - Price range filtering and price bucket aggregations
68
+ - Custom product/variant field mappings exposed via GraphQL
69
+ - Script fields for computed values (e.g. geo-spatial distance)
70
+ - Configurable search boosting per field
71
+ - Custom index analyzers and mapping properties
72
+ - Buffered updates for high-volume environments
73
+ - Health check integration
74
+ - Automatic index management and reindexing on entity changes
75
+ - Supports fuzzy search, wildcard queries via `mapQuery`
76
+ - Custom sort parameters via `mapSort`
77
+
78
+ ## Admin UI
79
+
80
+ Server-only plugin. No Admin UI extensions.
81
+
82
+ ## API Extensions
83
+
84
+ Both Admin and Shop APIs are extended with price range data and custom mapping fields:
85
+
86
+ ```graphql
87
+ extend type SearchResponse {
88
+ prices: SearchResponsePriceData!
89
+ }
90
+
91
+ type SearchResponsePriceData {
92
+ range: PriceRange!
93
+ rangeWithTax: PriceRange!
94
+ buckets: [PriceRangeBucket!]!
95
+ bucketsWithTax: [PriceRangeBucket!]!
96
+ }
97
+
98
+ type PriceRangeBucket {
99
+ to: Int!
100
+ count: Int!
101
+ }
102
+
103
+ extend input SearchInput {
104
+ priceRange: PriceRangeInput
105
+ priceRangeWithTax: PriceRangeInput
106
+ }
107
+
108
+ input PriceRangeInput {
109
+ min: Int!
110
+ max: Int!
111
+ }
112
+ ```
113
+
114
+ When `customProductMappings` or `customProductVariantMappings` are defined, additional fields are exposed on `SearchResult`:
115
+
116
+ ```graphql
117
+ type SearchResult {
118
+ customProductMappings: CustomProductMappings
119
+ customProductVariantMappings: CustomProductVariantMappings
120
+ customMappings: CustomMappings # Union of both
121
+ }
122
+ ```
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./src/plugin";
2
+ export * from "./src/options";
3
+ export * from "./src/types";
package/lib/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src/plugin"), exports);
18
+ __exportStar(require("./src/options"), exports);
19
+ __exportStar(require("./src/types"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,gDAA8B;AAC9B,8CAA4B"}
@@ -0,0 +1,3 @@
1
+ import { DocumentNode } from "graphql";
2
+ import { ElasticsearchOptions } from "../options";
3
+ export declare function generateSchemaExtensions(options: ElasticsearchOptions): DocumentNode;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSchemaExtensions = void 0;
4
+ const graphql_tag_1 = require("graphql-tag");
5
+ function generateSchemaExtensions(options) {
6
+ const customMappingTypes = generateCustomMappingTypes(options);
7
+ const inputExtensions = Object.entries(options.extendSearchInputType || {});
8
+ const sortExtensions = options.extendSearchSortType || [];
9
+ const sortExtensionGql = `
10
+ extend input SearchResultSortParameter {
11
+ ${sortExtensions.map((key) => `${key}: SortOrder`).join("\n ")}
12
+ }`;
13
+ return (0, graphql_tag_1.gql) `
14
+ extend type SearchResponse {
15
+ prices: SearchResponsePriceData!
16
+ }
17
+
18
+ extend type SearchResult {
19
+ inStock: Boolean
20
+ }
21
+
22
+ type SearchResponsePriceData {
23
+ range: PriceRange!
24
+ rangeWithTax: PriceRange!
25
+ buckets: [PriceRangeBucket!]!
26
+ bucketsWithTax: [PriceRangeBucket!]!
27
+ }
28
+
29
+ type PriceRangeBucket {
30
+ to: Int!
31
+ count: Int!
32
+ }
33
+
34
+ extend input SearchInput {
35
+ priceRange: PriceRangeInput
36
+ priceRangeWithTax: PriceRangeInput
37
+ inStock: Boolean
38
+ ${inputExtensions.map(([name, type]) => `${name}: ${type}`).join("\n ")}
39
+ }
40
+
41
+ ${sortExtensions.length > 0 ? sortExtensionGql : ""}
42
+
43
+ input PriceRangeInput {
44
+ min: Int!
45
+ max: Int!
46
+ }
47
+
48
+ ${customMappingTypes ? customMappingTypes : ""}
49
+ `;
50
+ }
51
+ exports.generateSchemaExtensions = generateSchemaExtensions;
52
+ function generateCustomMappingTypes(options) {
53
+ var _a, _b;
54
+ const productMappings = Object.entries(options.customProductMappings || {}).filter(([, value]) => { var _a; return (_a = value.public) !== null && _a !== void 0 ? _a : true; });
55
+ const variantMappings = Object.entries(options.customProductVariantMappings || {}).filter(([, value]) => { var _a; return (_a = value.public) !== null && _a !== void 0 ? _a : true; });
56
+ const searchInputTypeExtensions = Object.entries(options.extendSearchInputType || {});
57
+ const scriptProductFields = Object.entries(((_a = options.searchConfig) === null || _a === void 0 ? void 0 : _a.scriptFields) || {}).filter(([, scriptField]) => scriptField.context !== "variant");
58
+ const scriptVariantFields = Object.entries(((_b = options.searchConfig) === null || _b === void 0 ? void 0 : _b.scriptFields) || {}).filter(([, scriptField]) => scriptField.context !== "product");
59
+ let sdl = "";
60
+ if (scriptProductFields.length || scriptVariantFields.length) {
61
+ if (scriptProductFields.length) {
62
+ sdl += `
63
+ type CustomProductScriptFields {
64
+ ${scriptProductFields.map(([name, def]) => `${name}: ${def.graphQlType}`).join("\n")}
65
+ }
66
+ `;
67
+ }
68
+ if (scriptVariantFields.length) {
69
+ sdl += `
70
+ type CustomProductVariantScriptFields {
71
+ ${scriptVariantFields.map(([name, def]) => `${name}: ${def.graphQlType}`).join("\n")}
72
+ }
73
+ `;
74
+ }
75
+ if (scriptProductFields.length && scriptVariantFields.length) {
76
+ sdl += `
77
+ union CustomScriptFields = CustomProductScriptFields | CustomProductVariantScriptFields
78
+
79
+ extend type SearchResult {
80
+ customScriptFields: CustomScriptFields!
81
+ }
82
+ `;
83
+ }
84
+ else if (scriptProductFields.length) {
85
+ sdl += `
86
+ extend type SearchResult {
87
+ customScriptFields: CustomProductScriptFields!
88
+ }
89
+ `;
90
+ }
91
+ else if (scriptVariantFields.length) {
92
+ sdl += `
93
+ extend type SearchResult {
94
+ customScriptFields: CustomProductVariantScriptFields!
95
+ }
96
+ `;
97
+ }
98
+ }
99
+ if (productMappings.length || variantMappings.length) {
100
+ if (productMappings.length) {
101
+ sdl += `
102
+ type CustomProductMappings {
103
+ ${productMappings.map(([name, def]) => `${name}: ${def.graphQlType}`).join("\n")}
104
+ }
105
+ `;
106
+ }
107
+ if (variantMappings.length) {
108
+ sdl += `
109
+ type CustomProductVariantMappings {
110
+ ${variantMappings.map(([name, def]) => `${name}: ${def.graphQlType}`).join("\n")}
111
+ }
112
+ `;
113
+ }
114
+ if (productMappings.length && variantMappings.length) {
115
+ sdl += `
116
+ union CustomMappings = CustomProductMappings | CustomProductVariantMappings
117
+
118
+ extend type SearchResult {
119
+ customMappings: CustomMappings! @deprecated(reason: "Use customProductMappings or customProductVariantMappings")
120
+ customProductMappings: CustomProductMappings!
121
+ customProductVariantMappings: CustomProductVariantMappings!
122
+ }
123
+ `;
124
+ }
125
+ else if (productMappings.length) {
126
+ sdl += `
127
+ extend type SearchResult {
128
+ customMappings: CustomProductMappings! @deprecated(reason: "Use customProductMappings or customProductVariantMappings")
129
+ customProductMappings: CustomProductMappings!
130
+ }
131
+ `;
132
+ }
133
+ else if (variantMappings.length) {
134
+ sdl += `
135
+ extend type SearchResult {
136
+ customMappings: CustomProductVariantMappings! @deprecated(reason: "Use customProductMappings or customProductVariantMappings")
137
+ customProductVariantMappings: CustomProductVariantMappings!
138
+ }
139
+ `;
140
+ }
141
+ }
142
+ return sdl.length
143
+ ? (0, graphql_tag_1.gql) `
144
+ ${sdl}
145
+ `
146
+ : undefined;
147
+ }
148
+ //# sourceMappingURL=api-extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-extensions.js","sourceRoot":"","sources":["../../../src/api/api-extensions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { DeepRequired } from "@deenruv/common/lib/shared-types";
2
+ import { ElasticsearchOptions } from "../options";
3
+ /**
4
+ * This resolver is only required if both customProductMappings and customProductVariantMappings are
5
+ * defined, since this particular configuration will result in a union type for the
6
+ * `SearchResult.customMappings` GraphQL field.
7
+ */
8
+ export declare class CustomMappingsResolver {
9
+ private options;
10
+ constructor(options: DeepRequired<ElasticsearchOptions>);
11
+ __resolveType(value: any): string;
12
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CustomMappingsResolver = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const graphql_1 = require("@nestjs/graphql");
18
+ const constants_1 = require("../constants");
19
+ /**
20
+ * This resolver is only required if both customProductMappings and customProductVariantMappings are
21
+ * defined, since this particular configuration will result in a union type for the
22
+ * `SearchResult.customMappings` GraphQL field.
23
+ */
24
+ let CustomMappingsResolver = class CustomMappingsResolver {
25
+ constructor(options) {
26
+ this.options = options;
27
+ }
28
+ __resolveType(value) {
29
+ const productPropertyNames = Object.keys(this.options.customProductMappings);
30
+ return Object.keys(value).every((k) => productPropertyNames.includes(k))
31
+ ? "CustomProductMappings"
32
+ : "CustomProductVariantMappings";
33
+ }
34
+ };
35
+ exports.CustomMappingsResolver = CustomMappingsResolver;
36
+ __decorate([
37
+ (0, graphql_1.ResolveField)(),
38
+ __metadata("design:type", Function),
39
+ __metadata("design:paramtypes", [Object]),
40
+ __metadata("design:returntype", String)
41
+ ], CustomMappingsResolver.prototype, "__resolveType", null);
42
+ exports.CustomMappingsResolver = CustomMappingsResolver = __decorate([
43
+ (0, graphql_1.Resolver)("CustomMappings"),
44
+ __param(0, (0, common_1.Inject)(constants_1.ELASTIC_SEARCH_OPTIONS)),
45
+ __metadata("design:paramtypes", [Object])
46
+ ], CustomMappingsResolver);
47
+ //# sourceMappingURL=custom-mappings.resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-mappings.resolver.js","sourceRoot":"","sources":["../../../src/api/custom-mappings.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,6CAAyD;AAGzD,4CAAsD;AAGtD;;;;GAIG;AAEI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,YAEU,OAA2C;QAA3C,YAAO,GAAP,OAAO,CAAoC;IAClD,CAAC;IAGJ,aAAa,CAAC,KAAU;QACtB,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CACtC,IAAI,CAAC,OAAO,CAAC,qBAAqB,CACnC,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC,CAAC,uBAAuB;YACzB,CAAC,CAAC,8BAA8B,CAAC;IACrC,CAAC;CACF,CAAA;AAfY,wDAAsB;AAOjC;IADC,IAAA,sBAAY,GAAE;;;;2DAQd;iCAdU,sBAAsB;IADlC,IAAA,kBAAQ,EAAC,gBAAgB,CAAC;IAGtB,WAAA,IAAA,eAAM,EAAC,kCAAsB,CAAC,CAAA;;GAFtB,sBAAsB,CAelC"}
@@ -0,0 +1,12 @@
1
+ import { DeepRequired } from "@deenruv/common/lib/shared-types";
2
+ import { ElasticsearchOptions } from "../options";
3
+ /**
4
+ * This resolver is only required if scriptFields are defined for both products and product variants.
5
+ * This particular configuration will result in a union type for the
6
+ * `SearchResult.customScriptFields` GraphQL field.
7
+ */
8
+ export declare class CustomScriptFieldsResolver {
9
+ private options;
10
+ constructor(options: DeepRequired<ElasticsearchOptions>);
11
+ __resolveType(value: any): string;
12
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CustomScriptFieldsResolver = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const graphql_1 = require("@nestjs/graphql");
18
+ const constants_1 = require("../constants");
19
+ /**
20
+ * This resolver is only required if scriptFields are defined for both products and product variants.
21
+ * This particular configuration will result in a union type for the
22
+ * `SearchResult.customScriptFields` GraphQL field.
23
+ */
24
+ let CustomScriptFieldsResolver = class CustomScriptFieldsResolver {
25
+ constructor(options) {
26
+ this.options = options;
27
+ }
28
+ __resolveType(value) {
29
+ var _a;
30
+ const productScriptFields = Object.entries(((_a = this.options.searchConfig) === null || _a === void 0 ? void 0 : _a.scriptFields) || {})
31
+ .filter(([, scriptField]) => scriptField.context !== "variant")
32
+ .map(([k]) => k);
33
+ return Object.keys(value).every((k) => productScriptFields.includes(k))
34
+ ? "CustomProductScriptFields"
35
+ : "CustomProductVariantScriptFields";
36
+ }
37
+ };
38
+ exports.CustomScriptFieldsResolver = CustomScriptFieldsResolver;
39
+ __decorate([
40
+ (0, graphql_1.ResolveField)(),
41
+ __metadata("design:type", Function),
42
+ __metadata("design:paramtypes", [Object]),
43
+ __metadata("design:returntype", String)
44
+ ], CustomScriptFieldsResolver.prototype, "__resolveType", null);
45
+ exports.CustomScriptFieldsResolver = CustomScriptFieldsResolver = __decorate([
46
+ (0, graphql_1.Resolver)("CustomScriptFields"),
47
+ __param(0, (0, common_1.Inject)(constants_1.ELASTIC_SEARCH_OPTIONS)),
48
+ __metadata("design:paramtypes", [Object])
49
+ ], CustomScriptFieldsResolver);
50
+ //# sourceMappingURL=custom-script-fields.resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-script-fields.resolver.js","sourceRoot":"","sources":["../../../src/api/custom-script-fields.resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,6CAAyD;AAGzD,4CAAsD;AAGtD;;;;GAIG;AAEI,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IACrC,YAEU,OAA2C;QAA3C,YAAO,GAAP,OAAO,CAAoC;IAClD,CAAC;IAGJ,aAAa,CAAC,KAAU;;QACtB,MAAM,mBAAmB,GAAG,MAAM,CAAC,OAAO,CACxC,CAAA,MAAA,IAAI,CAAC,OAAO,CAAC,YAAY,0CAAE,YAAY,KAAI,EAAE,CAC9C;aACE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,KAAK,SAAS,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACnB,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACrE,CAAC,CAAC,2BAA2B;YAC7B,CAAC,CAAC,kCAAkC,CAAC;IACzC,CAAC;CACF,CAAA;AAjBY,gEAA0B;AAOrC;IADC,IAAA,sBAAY,GAAE;;;;+DAUd;qCAhBU,0BAA0B;IADtC,IAAA,kBAAQ,EAAC,oBAAoB,CAAC;IAG1B,WAAA,IAAA,eAAM,EAAC,kCAAsB,CAAC,CAAA;;GAFtB,0BAA0B,CAiBtC"}
@@ -0,0 +1,34 @@
1
+ import { Job as GraphQLJob, QuerySearchArgs, SearchResponse } from "@deenruv/common/lib/generated-types";
2
+ import { Omit } from "@deenruv/common/lib/omit";
3
+ import { Collection, FacetValue, RequestContext, SearchJobBufferService, SearchResolver } from "@deenruv/core";
4
+ import { ElasticsearchService } from "../elasticsearch.service";
5
+ import { ElasticSearchInput, SearchPriceData } from "../types";
6
+ export declare class ShopElasticSearchResolver implements Pick<SearchResolver, "search"> {
7
+ private elasticsearchService;
8
+ constructor(elasticsearchService: ElasticsearchService);
9
+ search(ctx: RequestContext, args: QuerySearchArgs): Promise<Omit<SearchResponse, "facetValues" | "collections">>;
10
+ prices(ctx: RequestContext, parent: {
11
+ input: ElasticSearchInput;
12
+ }): Promise<SearchPriceData>;
13
+ }
14
+ export declare class AdminElasticSearchResolver implements Pick<SearchResolver, "search" | "reindex"> {
15
+ private elasticsearchService;
16
+ private searchJobBufferService;
17
+ constructor(elasticsearchService: ElasticsearchService, searchJobBufferService: SearchJobBufferService);
18
+ search(ctx: RequestContext, args: QuerySearchArgs): Promise<Omit<SearchResponse, "facetValues" | "collections">>;
19
+ reindex(ctx: RequestContext): Promise<GraphQLJob>;
20
+ pendingSearchIndexUpdates(...args: any[]): Promise<any>;
21
+ runPendingSearchIndexUpdates(...args: any[]): Promise<any>;
22
+ }
23
+ export declare class EntityElasticSearchResolver implements Pick<SearchResolver, "facetValues" | "collections"> {
24
+ private elasticsearchService;
25
+ constructor(elasticsearchService: ElasticsearchService);
26
+ facetValues(ctx: RequestContext, parent: Omit<SearchResponse, "facetValues" | "collections">): Promise<Array<{
27
+ facetValue: FacetValue;
28
+ count: number;
29
+ }>>;
30
+ collections(ctx: RequestContext, parent: Omit<SearchResponse, "facetValues" | "collections">): Promise<Array<{
31
+ collection: Collection;
32
+ count: number;
33
+ }>>;
34
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EntityElasticSearchResolver = exports.AdminElasticSearchResolver = exports.ShopElasticSearchResolver = void 0;
16
+ const graphql_1 = require("@nestjs/graphql");
17
+ const generated_types_1 = require("@deenruv/common/lib/generated-types");
18
+ const core_1 = require("@deenruv/core");
19
+ const elasticsearch_service_1 = require("../elasticsearch.service");
20
+ let ShopElasticSearchResolver = class ShopElasticSearchResolver {
21
+ constructor(elasticsearchService) {
22
+ this.elasticsearchService = elasticsearchService;
23
+ }
24
+ async search(ctx, args) {
25
+ const result = await this.elasticsearchService.search(ctx, args.input, true);
26
+ // ensure the facetValues property resolver has access to the input args
27
+ result.input = args.input;
28
+ return result;
29
+ }
30
+ async prices(ctx, parent) {
31
+ return this.elasticsearchService.priceRange(ctx, parent.input);
32
+ }
33
+ };
34
+ exports.ShopElasticSearchResolver = ShopElasticSearchResolver;
35
+ __decorate([
36
+ (0, graphql_1.Query)(),
37
+ (0, core_1.Allow)(generated_types_1.Permission.Public),
38
+ __param(0, (0, core_1.Ctx)()),
39
+ __param(1, (0, graphql_1.Args)()),
40
+ __metadata("design:type", Function),
41
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
42
+ __metadata("design:returntype", Promise)
43
+ ], ShopElasticSearchResolver.prototype, "search", null);
44
+ __decorate([
45
+ (0, graphql_1.ResolveField)(),
46
+ __param(0, (0, core_1.Ctx)()),
47
+ __param(1, (0, graphql_1.Parent)()),
48
+ __metadata("design:type", Function),
49
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
50
+ __metadata("design:returntype", Promise)
51
+ ], ShopElasticSearchResolver.prototype, "prices", null);
52
+ exports.ShopElasticSearchResolver = ShopElasticSearchResolver = __decorate([
53
+ (0, graphql_1.Resolver)("SearchResponse"),
54
+ __metadata("design:paramtypes", [elasticsearch_service_1.ElasticsearchService])
55
+ ], ShopElasticSearchResolver);
56
+ let AdminElasticSearchResolver = class AdminElasticSearchResolver {
57
+ constructor(elasticsearchService, searchJobBufferService) {
58
+ this.elasticsearchService = elasticsearchService;
59
+ this.searchJobBufferService = searchJobBufferService;
60
+ }
61
+ async search(ctx, args) {
62
+ const result = await this.elasticsearchService.search(ctx, args.input, false);
63
+ // ensure the facetValues property resolver has access to the input args
64
+ result.input = args.input;
65
+ return result;
66
+ }
67
+ async reindex(ctx) {
68
+ return this.elasticsearchService.reindex(ctx);
69
+ }
70
+ async pendingSearchIndexUpdates(...args) {
71
+ return this.searchJobBufferService.getPendingSearchUpdates();
72
+ }
73
+ async runPendingSearchIndexUpdates(...args) {
74
+ // Intentionally not awaiting this method call
75
+ void this.searchJobBufferService.runPendingSearchUpdates();
76
+ return { success: true };
77
+ }
78
+ };
79
+ exports.AdminElasticSearchResolver = AdminElasticSearchResolver;
80
+ __decorate([
81
+ (0, graphql_1.Query)(),
82
+ (0, core_1.Allow)(generated_types_1.Permission.ReadCatalog, generated_types_1.Permission.ReadProduct),
83
+ __param(0, (0, core_1.Ctx)()),
84
+ __param(1, (0, graphql_1.Args)()),
85
+ __metadata("design:type", Function),
86
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
87
+ __metadata("design:returntype", Promise)
88
+ ], AdminElasticSearchResolver.prototype, "search", null);
89
+ __decorate([
90
+ (0, graphql_1.Mutation)(),
91
+ (0, core_1.Allow)(generated_types_1.Permission.UpdateCatalog, generated_types_1.Permission.UpdateProduct),
92
+ __param(0, (0, core_1.Ctx)()),
93
+ __metadata("design:type", Function),
94
+ __metadata("design:paramtypes", [core_1.RequestContext]),
95
+ __metadata("design:returntype", Promise)
96
+ ], AdminElasticSearchResolver.prototype, "reindex", null);
97
+ __decorate([
98
+ (0, graphql_1.Query)(),
99
+ (0, core_1.Allow)(generated_types_1.Permission.ReadCatalog, generated_types_1.Permission.ReadProduct),
100
+ __metadata("design:type", Function),
101
+ __metadata("design:paramtypes", [Object]),
102
+ __metadata("design:returntype", Promise)
103
+ ], AdminElasticSearchResolver.prototype, "pendingSearchIndexUpdates", null);
104
+ __decorate([
105
+ (0, graphql_1.Mutation)(),
106
+ (0, core_1.Allow)(generated_types_1.Permission.UpdateCatalog, generated_types_1.Permission.UpdateProduct),
107
+ __metadata("design:type", Function),
108
+ __metadata("design:paramtypes", [Object]),
109
+ __metadata("design:returntype", Promise)
110
+ ], AdminElasticSearchResolver.prototype, "runPendingSearchIndexUpdates", null);
111
+ exports.AdminElasticSearchResolver = AdminElasticSearchResolver = __decorate([
112
+ (0, graphql_1.Resolver)("SearchResponse"),
113
+ __metadata("design:paramtypes", [elasticsearch_service_1.ElasticsearchService,
114
+ core_1.SearchJobBufferService])
115
+ ], AdminElasticSearchResolver);
116
+ let EntityElasticSearchResolver = class EntityElasticSearchResolver {
117
+ constructor(elasticsearchService) {
118
+ this.elasticsearchService = elasticsearchService;
119
+ }
120
+ async facetValues(ctx, parent) {
121
+ const facetValues = await this.elasticsearchService.facetValues(ctx, parent.input, true);
122
+ return facetValues.filter((i) => !i.facetValue.facet.isPrivate);
123
+ }
124
+ async collections(ctx, parent) {
125
+ const collections = await this.elasticsearchService.collections(ctx, parent.input, true);
126
+ return collections.filter((i) => !i.collection.isPrivate);
127
+ }
128
+ };
129
+ exports.EntityElasticSearchResolver = EntityElasticSearchResolver;
130
+ __decorate([
131
+ (0, graphql_1.ResolveField)(),
132
+ __param(0, (0, core_1.Ctx)()),
133
+ __param(1, (0, graphql_1.Parent)()),
134
+ __metadata("design:type", Function),
135
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
136
+ __metadata("design:returntype", Promise)
137
+ ], EntityElasticSearchResolver.prototype, "facetValues", null);
138
+ __decorate([
139
+ (0, graphql_1.ResolveField)(),
140
+ __param(0, (0, core_1.Ctx)()),
141
+ __param(1, (0, graphql_1.Parent)()),
142
+ __metadata("design:type", Function),
143
+ __metadata("design:paramtypes", [core_1.RequestContext, Object]),
144
+ __metadata("design:returntype", Promise)
145
+ ], EntityElasticSearchResolver.prototype, "collections", null);
146
+ exports.EntityElasticSearchResolver = EntityElasticSearchResolver = __decorate([
147
+ (0, graphql_1.Resolver)("SearchResponse"),
148
+ __metadata("design:paramtypes", [elasticsearch_service_1.ElasticsearchService])
149
+ ], EntityElasticSearchResolver);
150
+ //# sourceMappingURL=elasticsearch-resolver.js.map