@vegan-friendly/strapi-plugin-elasticsearch 0.2.1 → 0.2.3

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.
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  export declare const SubNavigation: ({ activeUrl }: {
3
- activeUrl: any;
3
+ activeUrl?: string | null;
4
4
  }) => React.JSX.Element;
@@ -10,7 +10,7 @@ const design_system_1 = require("@strapi/design-system");
10
10
  const design_system_2 = require("@strapi/design-system");
11
11
  const react_router_dom_1 = require("react-router-dom");
12
12
  const pluginId_1 = __importDefault(require("../../pluginId"));
13
- const SubNavigation = ({ activeUrl }) => {
13
+ const SubNavigation = ({ activeUrl = null }) => {
14
14
  const links = [
15
15
  {
16
16
  id: 1,
@@ -110,7 +110,7 @@ const ConfigureCollection = () => {
110
110
  const updateCollectionsConfig = ({ index, config }) => {
111
111
  setCollectionConfig({
112
112
  collectionName: collectionConfig.collectionName,
113
- attributes: collectionConfig.attributes.map((e, idx) => index === idx ? config : e)
113
+ attributes: collectionConfig.attributes.map((e, idx) => (index === idx ? config : e)),
114
114
  });
115
115
  };
116
116
  const saveCollectionConfig = () => {
@@ -125,12 +125,16 @@ const ConfigureCollection = () => {
125
125
  saveConfigForCollection(collectionConfig.collectionName, data)
126
126
  .then((resp) => {
127
127
  toggleNotification({
128
- type: "success", message: "The collection configuration is saved.", timeout: 5000
128
+ type: 'success',
129
+ message: 'The collection configuration is saved.',
130
+ timeout: 5000,
129
131
  });
130
132
  })
131
133
  .catch((err) => {
132
134
  toggleNotification({
133
- type: "warning", message: err.message || "An error was encountered.", timeout: 5000
135
+ type: 'warning',
136
+ message: err.message || 'An error was encountered.',
137
+ timeout: 5000,
134
138
  });
135
139
  console.log(err);
136
140
  })
@@ -147,7 +151,9 @@ const ConfigureCollection = () => {
147
151
  .then((resp) => {
148
152
  if (Object.keys(resp).length === 0) {
149
153
  toggleNotification({
150
- type: "warning", message: 'No collection with the selected name exists.', timeout: 5000
154
+ type: 'warning',
155
+ message: 'No collection with the selected name exists.',
156
+ timeout: 5000,
151
157
  });
152
158
  }
153
159
  else {
@@ -162,7 +168,9 @@ const ConfigureCollection = () => {
162
168
  })
163
169
  .catch((err) => {
164
170
  toggleNotification({
165
- type: "warning", message: err.message || "An error was encountered.", timeout: 5000
171
+ type: 'warning',
172
+ message: err.message || 'An error was encountered.',
173
+ timeout: 5000,
166
174
  });
167
175
  console.log(err);
168
176
  });
@@ -51,7 +51,8 @@ const design_system_2 = require("@strapi/design-system");
51
51
  const design_system_3 = require("@strapi/design-system");
52
52
  const helper_plugin_1 = require("@strapi/helper-plugin");
53
53
  const loadRecentIndexingRuns = () => {
54
- return axiosInstance_1.default.get(apiUrls_1.apiFetchRecentIndexingRunLog)
54
+ return axiosInstance_1.default
55
+ .get(apiUrls_1.apiFetchRecentIndexingRunLog)
55
56
  .then((resp) => resp.data)
56
57
  .then((data) => {
57
58
  return data;
@@ -71,17 +72,17 @@ const formattedDate = (dateString) => {
71
72
  const parts = dateTimeFormat.formatToParts(date);
72
73
  let formattedDate = '';
73
74
  parts.forEach((part) => {
74
- if (part.type === "weekday")
75
+ if (part.type === 'weekday')
75
76
  formattedDate += `${part.value}, `;
76
- if (part.type === "day")
77
+ if (part.type === 'day')
77
78
  formattedDate += `${part.value}/`;
78
- if (part.type === "month")
79
+ if (part.type === 'month')
79
80
  formattedDate += `${part.value}/`;
80
- if (part.type === "year")
81
+ if (part.type === 'year')
81
82
  formattedDate += `${part.value} `;
82
- if (part.type === "hour")
83
+ if (part.type === 'hour')
83
84
  formattedDate += `${part.value}:`;
84
- if (part.type === "minute")
85
+ if (part.type === 'minute')
85
86
  formattedDate += `${part.value}`;
86
87
  });
87
88
  return formattedDate;
@@ -89,8 +90,7 @@ const formattedDate = (dateString) => {
89
90
  const ViewIndexingRunLog = () => {
90
91
  const [logTable, setLogTable] = (0, react_1.useState)(null);
91
92
  (0, react_1.useEffect)(() => {
92
- loadRecentIndexingRuns()
93
- .then(setLogTable);
93
+ loadRecentIndexingRuns().then(setLogTable);
94
94
  }, []);
95
95
  if (logTable === null)
96
96
  return react_1.default.createElement(helper_plugin_1.LoadingIndicatorPage, null);
@@ -12,11 +12,9 @@ const instance = axios_1.default.create({
12
12
  baseURL: process.env.STRAPI_ADMIN_BACKEND_URL,
13
13
  });
14
14
  instance.interceptors.request.use(async (config) => {
15
- config.headers = {
16
- 'Authorization': `Bearer ${helper_plugin_1.auth.getToken()}`,
17
- 'Accept': 'application/json',
18
- 'Content-Type': 'application/json',
19
- };
15
+ config.headers.set('Authorization', `Bearer ${helper_plugin_1.auth.getToken()}`);
16
+ config.headers.set('Accept', 'application/json');
17
+ config.headers.set('Content-Type', 'application/json');
20
18
  return config;
21
19
  }, error => {
22
20
  Promise.reject(error);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegan-friendly/strapi-plugin-elasticsearch",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A Strapi plugin to enable using Elasticsearch with Strapi CMS.",
5
5
  "homepage": "https://github.com/vegan-friendly/strapi-plugin-elasticsearch",
6
6
  "strapi": {
@@ -12,7 +12,7 @@ exports.default = async ({ strapi }) => {
12
12
  await configureIndexingService.initializeStrapiElasticsearch();
13
13
  if (!Object.keys(pluginConfig).includes('indexingCronSchedule'))
14
14
  console.warn('The plugin strapi-plugin-elasticsearch is enabled but the indexingCronSchedule is not configured.');
15
- else if (!Object.keys(pluginConfig).includes('searchConnector'))
15
+ if (!Object.keys(pluginConfig).includes('searchConnector'))
16
16
  console.warn('The plugin strapi-plugin-elasticsearch is enabled but the searchConnector is not configured.');
17
17
  else {
18
18
  const connector = pluginConfig['searchConnector'];
@@ -163,38 +163,7 @@ declare const _default: {
163
163
  };
164
164
  helper: ({ strapi }: {
165
165
  strapi: any;
166
- }) => {
167
- getElasticsearchInfo(): Promise<{
168
- indexingCronSchedule: any;
169
- elasticHost: any;
170
- elasticUserName: any;
171
- elasticCertificate: any;
172
- elasticIndexAlias: any;
173
- connected: any;
174
- initialized: any;
175
- }>;
176
- isCollectionDraftPublish({ collectionName }: {
177
- collectionName: any;
178
- }): boolean;
179
- getPopulateAttribute({ collectionName }: {
180
- collectionName: any;
181
- }): true | {
182
- populate: never;
183
- } | undefined;
184
- getIndexItemId({ collectionName, itemId }: {
185
- collectionName: any;
186
- itemId: any;
187
- }): string;
188
- getCurrentIndexName(): Promise<string>;
189
- getIncrementedIndexName(): Promise<string>;
190
- storeCurrentIndexName(indexName: any): Promise<void>;
191
- modifySubfieldsConfigForExtractor(collectionConfig: any): any;
192
- extractDataToIndex({ collectionName, data, collectionConfig }: {
193
- collectionName: any;
194
- data: any;
195
- collectionConfig: any;
196
- }): {};
197
- };
166
+ }) => import("./types/helper-service.type").HelperService;
198
167
  transformContent: {
199
168
  transform({ content, from }: {
200
169
  content: any;
@@ -29,13 +29,14 @@ exports.default = ({ strapi }) => ({
29
29
  throw err;
30
30
  }
31
31
  },
32
- async createIndex(indexName) {
32
+ async createIndex(indexName, mappings) {
33
33
  try {
34
34
  const exists = await client.indices.exists({ index: indexName });
35
35
  if (!exists) {
36
36
  console.log('strapi-plugin-elasticsearch : Search index ', indexName, ' does not exist. Creating index.');
37
37
  await client.indices.create({
38
38
  index: indexName,
39
+ mappings: mappings ?? undefined,
39
40
  });
40
41
  }
41
42
  }
@@ -67,10 +68,10 @@ exports.default = ({ strapi }) => ({
67
68
  }
68
69
  }
69
70
  },
70
- async attachAliasToIndex(indexName) {
71
+ async attachAliasToIndex(indexName, optionalAliasName, mappings) {
71
72
  try {
72
73
  const pluginConfig = await strapi.config.get('plugin.elasticsearch');
73
- const aliasName = pluginConfig.indexAliasName;
74
+ const aliasName = optionalAliasName ? optionalAliasName : pluginConfig.indexAliasName;
74
75
  const aliasExists = await client.indices.existsAlias({ name: aliasName });
75
76
  if (aliasExists) {
76
77
  console.log('strapi-plugin-elasticsearch : Alias with this name already exists, removing it.');
@@ -78,7 +79,7 @@ exports.default = ({ strapi }) => ({
78
79
  }
79
80
  const indexExists = await client.indices.exists({ index: indexName });
80
81
  if (!indexExists)
81
- await this.createIndex(indexName);
82
+ await this.createIndex(indexName, mappings);
82
83
  console.log('strapi-plugin-elasticsearch : Attaching the alias ', aliasName, ' to index : ', indexName);
83
84
  await client.indices.putAlias({ index: indexName, name: aliasName });
84
85
  }
@@ -157,4 +158,11 @@ exports.default = ({ strapi }) => ({
157
158
  throw err;
158
159
  }
159
160
  },
161
+ async listIndicesByPattern(pattern = 'restaurants*') {
162
+ const results = await client.cat.indices({
163
+ index: pattern,
164
+ format: 'json',
165
+ });
166
+ return results.map((index) => index.index).filter((index) => index != null);
167
+ },
160
168
  });
@@ -1,35 +1,5 @@
1
+ import { HelperService } from '../types/helper-service.type';
1
2
  declare const _default: ({ strapi }: {
2
3
  strapi: any;
3
- }) => {
4
- getElasticsearchInfo(): Promise<{
5
- indexingCronSchedule: any;
6
- elasticHost: any;
7
- elasticUserName: any;
8
- elasticCertificate: any;
9
- elasticIndexAlias: any;
10
- connected: any;
11
- initialized: any;
12
- }>;
13
- isCollectionDraftPublish({ collectionName }: {
14
- collectionName: any;
15
- }): boolean;
16
- getPopulateAttribute({ collectionName }: {
17
- collectionName: any;
18
- }): true | {
19
- populate: never;
20
- } | undefined;
21
- getIndexItemId({ collectionName, itemId }: {
22
- collectionName: any;
23
- itemId: any;
24
- }): string;
25
- getCurrentIndexName(): Promise<string>;
26
- getIncrementedIndexName(): Promise<string>;
27
- storeCurrentIndexName(indexName: any): Promise<void>;
28
- modifySubfieldsConfigForExtractor(collectionConfig: any): any;
29
- extractDataToIndex({ collectionName, data, collectionConfig }: {
30
- collectionName: any;
31
- data: any;
32
- collectionConfig: any;
33
- }): {};
34
- };
4
+ }) => HelperService;
35
5
  export default _default;
@@ -6,15 +6,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const fp_1 = require("lodash/fp");
8
8
  const transform_content_1 = __importDefault(require("./transform-content"));
9
- const getPluginStore = () => {
10
- return strapi.store({
11
- environment: '',
12
- type: 'plugin',
13
- name: 'elasticsearch',
14
- });
15
- };
9
+ const defaultIndexPrefix = 'strapi-plugin-elasticsearch-index';
16
10
  const getModelPopulationAttributes = (model) => {
17
- if (model.uid === "plugin::upload.file") {
11
+ if (model.uid === 'plugin::upload.file') {
18
12
  const { related, ...attributes } = model.attributes;
19
13
  return attributes;
20
14
  }
@@ -164,110 +158,117 @@ function extractSubfieldData({ config, data }) {
164
158
  }
165
159
  return returnData;
166
160
  }
167
- exports.default = ({ strapi }) => ({
168
- async getElasticsearchInfo() {
169
- const configureService = strapi.plugins['elasticsearch'].services.configureIndexing;
170
- const esInterface = strapi.plugins['elasticsearch'].services.esInterface;
171
- const pluginConfig = await strapi.config.get('plugin.elasticsearch');
172
- const connected = pluginConfig.searchConnector && pluginConfig.searchConnector.host ? await esInterface.checkESConnection() : false;
173
- return {
174
- indexingCronSchedule: pluginConfig.indexingCronSchedule || 'Not configured',
175
- elasticHost: pluginConfig.searchConnector ? pluginConfig.searchConnector.host || 'Not configured' : 'Not configured',
176
- elasticUserName: pluginConfig.searchConnector ? pluginConfig.searchConnector.username || 'Not configured' : 'Not configured',
177
- elasticCertificate: pluginConfig.searchConnector ? pluginConfig.searchConnector.certificate || 'Not configured' : 'Not configured',
178
- elasticIndexAlias: pluginConfig.indexAliasName || 'Not configured',
179
- connected: connected,
180
- initialized: configureService.isInitialized(),
181
- };
182
- },
183
- isCollectionDraftPublish({ collectionName }) {
184
- const model = strapi.getModel(collectionName);
185
- return model.attributes.publishedAt ? true : false;
186
- },
187
- getPopulateAttribute({ collectionName }) {
188
- //TODO : We currently have set populate to upto 4 levels, should
189
- //this be configurable or a different default value?
190
- return getFullPopulateObject(collectionName, 4, []);
191
- },
192
- getIndexItemId({ collectionName, itemId }) {
193
- return collectionName + '::' + itemId;
194
- },
195
- async getCurrentIndexName() {
196
- const pluginStore = getPluginStore();
197
- const settings = (await pluginStore.get({ key: 'configsettings' }));
198
- let indexName = 'strapi-plugin-elasticsearch-index_000001';
199
- if (settings) {
200
- const objSettings = JSON.parse(settings);
201
- if (Object.keys(objSettings).includes('indexConfig')) {
202
- const idxConfig = objSettings['indexConfig'];
203
- indexName = idxConfig['name'];
161
+ exports.default = ({ strapi }) => {
162
+ const getEsInterface = () => strapi.plugins['elasticsearch'].services.esInterface;
163
+ return {
164
+ async getElasticsearchInfo() {
165
+ const configureService = strapi.plugins['elasticsearch'].services.configureIndexing;
166
+ const pluginConfig = await strapi.config.get('plugin.elasticsearch');
167
+ const connected = pluginConfig.searchConnector && pluginConfig.searchConnector.host ? await getEsInterface().checkESConnection() : false;
168
+ return {
169
+ indexingCronSchedule: pluginConfig.indexingCronSchedule || 'Not configured',
170
+ elasticHost: pluginConfig.searchConnector ? pluginConfig.searchConnector.host || 'Not configured' : 'Not configured',
171
+ elasticUserName: pluginConfig.searchConnector ? pluginConfig.searchConnector.username || 'Not configured' : 'Not configured',
172
+ elasticCertificate: pluginConfig.searchConnector ? pluginConfig.searchConnector.certificate || 'Not configured' : 'Not configured',
173
+ elasticIndexAlias: pluginConfig.indexAliasName || 'Not configured',
174
+ connected: connected,
175
+ initialized: configureService.isInitialized(),
176
+ };
177
+ },
178
+ isCollectionDraftPublish({ collectionName }) {
179
+ const model = strapi.getModel(collectionName);
180
+ return model.attributes.publishedAt ? true : false;
181
+ },
182
+ getPopulateAttribute({ collectionName }) {
183
+ //TODO : We currently have set populate to upto 4 levels, should
184
+ //this be configurable or a different default value?
185
+ return getFullPopulateObject(collectionName, 4, []);
186
+ },
187
+ getIndexItemId({ collectionName, itemId }) {
188
+ return collectionName + '::' + itemId;
189
+ },
190
+ async getCurrentIndexName(indexPrefix = defaultIndexPrefix) {
191
+ const indices = await getEsInterface().listIndicesByPattern(indexPrefix + '_*');
192
+ if (indices.length === 0) {
193
+ return indexPrefix + '_000000';
204
194
  }
205
- }
206
- return indexName;
207
- },
208
- async getIncrementedIndexName() {
209
- const currentIndexName = await this.getCurrentIndexName();
210
- const number = parseInt(currentIndexName.split('index_')[1]);
211
- return 'strapi-plugin-elasticsearch-index_' + String(number + 1).padStart(6, '0');
212
- },
213
- async storeCurrentIndexName(indexName) {
214
- const pluginStore = getPluginStore();
215
- const settings = (await pluginStore.get({ key: 'configsettings' }));
216
- if (settings) {
217
- const objSettings = JSON.parse(settings);
218
- objSettings['indexConfig'] = { name: indexName };
219
- await pluginStore.set({ key: 'configsettings', value: JSON.stringify(objSettings) });
220
- }
221
- else {
222
- const newSettings = JSON.stringify({ indexConfig: { name: indexName } });
223
- await pluginStore.set({ key: 'configsettings', value: newSettings });
224
- }
225
- },
226
- modifySubfieldsConfigForExtractor(collectionConfig) {
227
- const collectionName = Object.keys(collectionConfig)[0];
228
- const attributes = Object.keys(collectionConfig[collectionName]);
229
- for (let r = 0; r < attributes.length; r++) {
230
- const attr = attributes[r];
231
- const attribFields = Object.keys(collectionConfig[collectionName][attr]);
232
- if (attribFields.includes('subfields')) {
233
- const subfielddata = collectionConfig[collectionName][attr]['subfields'];
234
- if (subfielddata.length > 0) {
235
- try {
236
- const subfieldjson = JSON.parse(subfielddata);
237
- if (Object.keys(subfieldjson).includes('subfields'))
238
- collectionConfig[collectionName][attr]['subfields'] = subfieldjson['subfields'];
239
- }
240
- catch (err) {
241
- continue;
195
+ const sortedIndices = indices.sort(sortByIndexNumber(indexPrefix)).reverse();
196
+ return sortedIndices[0];
197
+ },
198
+ async getIncrementedIndexName(indexPrefix = defaultIndexPrefix) {
199
+ const currentIndexName = await this.getCurrentIndexName(indexPrefix);
200
+ const number = parseInt(currentIndexName.split(indexPrefix + '_')[1]);
201
+ return indexPrefix + '_' + String(number + 1).padStart(6, '0');
202
+ },
203
+ async deleteOldIndices(indexPrefix = defaultIndexPrefix) {
204
+ const esInterface = getEsInterface();
205
+ const indices = await esInterface.listIndicesByPattern(indexPrefix + '_*');
206
+ const sortedIndices = indices.sort(sortByIndexNumber(indexPrefix)).reverse();
207
+ const indicesToDelete = sortedIndices.slice(1);
208
+ for (let index of indicesToDelete) {
209
+ await esInterface.deleteIndex(index);
210
+ }
211
+ return indicesToDelete;
212
+ },
213
+ modifySubfieldsConfigForExtractor(collectionConfig) {
214
+ const collectionName = Object.keys(collectionConfig)[0];
215
+ const attributes = Object.keys(collectionConfig[collectionName]);
216
+ for (let r = 0; r < attributes.length; r++) {
217
+ const attr = attributes[r];
218
+ const attribFields = Object.keys(collectionConfig[collectionName][attr]);
219
+ if (attribFields.includes('subfields')) {
220
+ const subfielddata = collectionConfig[collectionName][attr]['subfields'];
221
+ if (subfielddata.length > 0) {
222
+ try {
223
+ const subfieldjson = JSON.parse(subfielddata);
224
+ if (Object.keys(subfieldjson).includes('subfields'))
225
+ collectionConfig[collectionName][attr]['subfields'] = subfieldjson['subfields'];
226
+ }
227
+ catch (err) {
228
+ continue;
229
+ }
242
230
  }
243
231
  }
244
232
  }
245
- }
246
- return collectionConfig;
247
- },
248
- extractDataToIndex({ collectionName, data, collectionConfig }) {
249
- collectionConfig = this.modifySubfieldsConfigForExtractor(collectionConfig);
250
- const fti = Object.keys(collectionConfig[collectionName]);
251
- const document = {};
252
- for (let k = 0; k < fti.length; k++) {
253
- const fieldConfig = collectionConfig[collectionName][fti[k]];
254
- if (fieldConfig.index) {
255
- let val = null;
256
- if (Object.keys(fieldConfig).includes('subfields')) {
257
- val = extractSubfieldData({ config: fieldConfig['subfields'], data: data[fti[k]] });
258
- val = val ? val.trim() : val;
259
- }
260
- else {
261
- val = data[fti[k]];
262
- if (Object.keys(fieldConfig).includes('transform') && fieldConfig['transform'] === 'markdown')
263
- val = transform_content_1.default.transform({ content: val, from: 'markdown' });
233
+ return collectionConfig;
234
+ },
235
+ extractDataToIndex({ collectionName, data, collectionConfig }) {
236
+ collectionConfig = this.modifySubfieldsConfigForExtractor(collectionConfig);
237
+ const fti = Object.keys(collectionConfig[collectionName]);
238
+ const document = {};
239
+ for (let k = 0; k < fti.length; k++) {
240
+ const fieldConfig = collectionConfig[collectionName][fti[k]];
241
+ if (fieldConfig.index) {
242
+ let val = null;
243
+ if (Object.keys(fieldConfig).includes('subfields')) {
244
+ val = extractSubfieldData({ config: fieldConfig['subfields'], data: data[fti[k]] });
245
+ val = val ? val.trim() : val;
246
+ }
247
+ else {
248
+ val = data[fti[k]];
249
+ if (Object.keys(fieldConfig).includes('transform') && fieldConfig['transform'] === 'markdown')
250
+ val = transform_content_1.default.transform({ content: val, from: 'markdown' });
251
+ }
252
+ if (Object.keys(fieldConfig).includes('searchFieldName'))
253
+ document[fieldConfig['searchFieldName']] = val;
254
+ else
255
+ document[fti[k]] = val;
264
256
  }
265
- if (Object.keys(fieldConfig).includes('searchFieldName'))
266
- document[fieldConfig['searchFieldName']] = val;
267
- else
268
- document[fti[k]] = val;
269
257
  }
270
- }
271
- return document;
272
- },
273
- });
258
+ return document;
259
+ },
260
+ };
261
+ };
262
+ /**
263
+ * sort index names by index number, e.g:
264
+ * restaurants_000001, restaurants_000002, restaurants_000003
265
+ * @param indexPrefix index alias, usually
266
+ * @returns
267
+ */
268
+ function sortByIndexNumber(indexPrefix) {
269
+ return (a, b) => {
270
+ const numA = parseInt(a.split(indexPrefix + '_')[1]);
271
+ const numB = parseInt(b.split(indexPrefix + '_')[1]);
272
+ return numA - numB;
273
+ };
274
+ }
@@ -59,38 +59,7 @@ declare const _default: {
59
59
  };
60
60
  helper: ({ strapi }: {
61
61
  strapi: any;
62
- }) => {
63
- getElasticsearchInfo(): Promise<{
64
- indexingCronSchedule: any;
65
- elasticHost: any;
66
- elasticUserName: any;
67
- elasticCertificate: any;
68
- elasticIndexAlias: any;
69
- connected: any;
70
- initialized: any;
71
- }>;
72
- isCollectionDraftPublish({ collectionName }: {
73
- collectionName: any;
74
- }): boolean;
75
- getPopulateAttribute({ collectionName }: {
76
- collectionName: any;
77
- }): true | {
78
- populate: never;
79
- } | undefined;
80
- getIndexItemId({ collectionName, itemId }: {
81
- collectionName: any;
82
- itemId: any;
83
- }): string;
84
- getCurrentIndexName(): Promise<string>;
85
- getIncrementedIndexName(): Promise<string>;
86
- storeCurrentIndexName(indexName: any): Promise<void>;
87
- modifySubfieldsConfigForExtractor(collectionConfig: any): any;
88
- extractDataToIndex({ collectionName, data, collectionConfig }: {
89
- collectionName: any;
90
- data: any;
91
- collectionConfig: any;
92
- }): {};
93
- };
62
+ }) => import("../types/helper-service.type").HelperService;
94
63
  transformContent: {
95
64
  transform({ content, from }: {
96
65
  content: any;
@@ -31,11 +31,9 @@ exports.default = ({ strapi }) => ({
31
31
  //Step 4 : Move the alias to this new index
32
32
  await esInterface.attachAliasToIndex(newIndexName);
33
33
  console.log('strapi-plugin-elasticsearch : Attaching the newly created index to the alias.');
34
- //Step 3 : Update the search-indexing-name
35
- await helper.storeCurrentIndexName(newIndexName);
36
- console.log('strapi-plugin-elasticsearch : Deleting the previous index : ', oldIndexName);
34
+ console.log('strapi-plugin-elasticsearch : Deleting the previous indices');
37
35
  //Step 5 : Delete the previous index
38
- await esInterface.deleteIndex(oldIndexName);
36
+ await helper.deleteOldIndices();
39
37
  await logIndexingService.recordIndexingPass('Request to immediately re-index site-wide content completed successfully.');
40
38
  return true;
41
39
  }
@@ -27,44 +27,49 @@ exports.default = ({ strapi }) => {
27
27
  strapi.log.warn(`No data extracted for ${collectionName} with ID ${itemId}`);
28
28
  return null;
29
29
  }
30
- const data = results[0];
31
- const indexData = collection.mapToIndex ? data : data;
32
- const esService = getElasticsearchService();
30
+ const itemData = results[0];
31
+ const esInterface = getElasticsearchService();
33
32
  const helper = getHelperService();
34
- const indexItemId = helper.getIndexItemId(collectionName, itemId);
35
- await esService.indexDataToSpecificIndex({ itemId: indexItemId, itemData: indexData }, collection.indexName);
33
+ const indexItemId = helper.getIndexItemId({ collectionName, itemId });
34
+ const indexName = await helper.getCurrentIndexName(collection.indexAlias);
35
+ await esInterface.indexDataToSpecificIndex({ itemId: indexItemId, itemData }, indexName);
36
36
  strapi.log.debug(`Indexed virtual item: ${collectionName}:${itemId}`);
37
- return indexData;
37
+ return itemData;
38
38
  }
39
39
  catch (error) {
40
40
  strapi.log.error(`Error indexing ${collectionName}:${itemId}: ${error?.message}`);
41
41
  throw error;
42
42
  }
43
43
  },
44
- async reindexAll(indexName) {
44
+ async reindexAll() {
45
+ const timestamp = Date.now();
45
46
  const registry = getRegistryService();
46
47
  const collections = registry.getAll();
47
48
  let totalIndexed = 0;
48
49
  for (const collection of collections) {
49
- totalIndexed += await this.reindex(collection.collectionName, indexName);
50
+ totalIndexed += await this.reindex(collection);
50
51
  }
51
- console.log(`strapi-plugin-elasticsearch : Reindexed ${totalIndexed} items across all ${collections.length} virtual collections`);
52
+ strapi.log.info(`strapi-plugin-elasticsearch : Reindexed ${totalIndexed} items across all ${collections.length} virtual collections. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)} `);
52
53
  return totalIndexed;
53
54
  },
54
55
  /**
55
56
  * Reindex all items in a virtual collection
56
57
  */
57
- async reindex(collectionName, indexName) {
58
- const registry = getRegistryService();
59
- const collection = registry.get(collectionName);
60
- if (!collection) {
61
- throw new Error(`Virtual collection not found: ${collectionName}`);
62
- }
63
- const timestamp = Date.now();
58
+ async reindex(collection) {
59
+ const collectionName = collection.collectionName;
60
+ const privateIndexAlias = collection.indexAlias;
61
+ const helper = getHelperService();
62
+ let timestamp = Date.now();
64
63
  try {
65
- const esService = getElasticsearchService();
66
- const helper = getHelperService();
67
- // await esService.createIndex(tempIndexName);
64
+ const esInterface = getElasticsearchService();
65
+ let indexName;
66
+ if (privateIndexAlias) {
67
+ indexName = await helper.getIncrementedIndexName(privateIndexAlias);
68
+ await esInterface.createIndex(indexName, collection.mappings);
69
+ }
70
+ else {
71
+ indexName = await helper.getCurrentIndexName();
72
+ }
68
73
  let page = 0;
69
74
  let hasMoreData = true;
70
75
  let totalIndexed = 0;
@@ -75,18 +80,25 @@ exports.default = ({ strapi }) => {
75
80
  break;
76
81
  }
77
82
  const operations = [];
78
- for (const item of pageData) {
79
- const itemId = helper.getIndexItemId({ collectionName, itemId: item.id });
80
- const itemData = collection.mapToIndex ? item : item;
83
+ for (const itemData of pageData) {
84
+ const itemId = helper.getIndexItemId({ collectionName, itemId: itemData.id });
81
85
  operations.push({ itemId, itemData });
82
86
  }
83
87
  if (operations.length > 0) {
84
- await Promise.all(operations.map((op) => esService.indexDataToSpecificIndex(op, indexName)));
88
+ await Promise.all(operations.map((op) => esInterface.indexDataToSpecificIndex(op, indexName)));
85
89
  }
86
90
  totalIndexed += pageData.length;
87
91
  page++;
88
92
  }
89
- strapi.log.info(`Reindexed ${totalIndexed} items for virtual collection: ${collectionName}. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}`);
93
+ strapi.log.info(`Reindexed ${totalIndexed} items for virtual collection: ${collectionName}. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}. now updating alias.`);
94
+ if (privateIndexAlias) {
95
+ timestamp = Date.now();
96
+ await esInterface.attachAliasToIndex(indexName, privateIndexAlias, collection.mappings);
97
+ strapi.log.info(`Done attachAliasToIndex alias ${privateIndexAlias} to index ${indexName}. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}`);
98
+ timestamp = Date.now();
99
+ const oldIndicesDeleted = await helper.deleteOldIndices(privateIndexAlias);
100
+ strapi.log.info(`Done deleting ${oldIndicesDeleted.length} old indices: ${oldIndicesDeleted}. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}`);
101
+ }
90
102
  return totalIndexed;
91
103
  }
92
104
  catch (error) {
@@ -125,8 +137,8 @@ exports.default = ({ strapi }) => {
125
137
  throw new Error(`Virtual collection not found: ${collectionName}`);
126
138
  }
127
139
  try {
128
- const esService = getElasticsearchService();
129
- await esService.removeItemFromIndex({ itemId });
140
+ const esInterface = getElasticsearchService();
141
+ await esInterface.removeItemFromIndex({ itemId });
130
142
  strapi.log.debug(`Deleted indexed item: ${collectionName}:${itemId}`);
131
143
  return true;
132
144
  }
@@ -1,3 +1,4 @@
1
+ import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types';
1
2
  export interface EsInterfaceService {
2
3
  /**
3
4
  * Initializes the search engine connection.
@@ -15,7 +16,7 @@ export interface EsInterfaceService {
15
16
  * @param indexName - The name of the index to create.
16
17
  * @returns A promise that resolves when the index is created.
17
18
  */
18
- createIndex(indexName: string): Promise<void>;
19
+ createIndex(indexName: string, mappings?: MappingTypeMapping): Promise<void>;
19
20
  /**
20
21
  * Deletes an index from the search engine.
21
22
  * @param indexName - The name of the index to delete.
@@ -25,9 +26,10 @@ export interface EsInterfaceService {
25
26
  /**
26
27
  * Attaches an alias to a specific index.
27
28
  * @param indexName - The index to which the alias should be attached.
29
+ * @param aliasName - Alias to attach. If not provided, alias from configuration 'indexAliasName' will be used.
28
30
  * @returns A promise that resolves when the alias is set.
29
31
  */
30
- attachAliasToIndex(indexName: string): Promise<void>;
32
+ attachAliasToIndex(indexName: string, aliasName?: string, mappings?: MappingTypeMapping): Promise<void>;
31
33
  /**
32
34
  * Checks the connection status of the search engine.
33
35
  * @returns A promise that resolves with the connection status.
@@ -67,4 +69,5 @@ export interface EsInterfaceService {
67
69
  * @returns A promise that resolves with the search results.
68
70
  */
69
71
  searchData(searchQuery: any): Promise<any>;
72
+ listIndicesByPattern(pattern: string): Promise<string[]>;
70
73
  }
@@ -0,0 +1,33 @@
1
+ export type ElasticsearchInfo = {
2
+ indexingCronSchedule: string;
3
+ elasticHost: string;
4
+ elasticUserName: string;
5
+ elasticCertificate: string;
6
+ elasticIndexAlias: number;
7
+ connected: string;
8
+ initialized: boolean;
9
+ };
10
+ export interface HelperService {
11
+ getElasticsearchInfo(): Promise<ElasticsearchInfo>;
12
+ isCollectionDraftPublish(args: {
13
+ collectionName: string;
14
+ }): boolean;
15
+ getPopulateAttribute(args: {
16
+ collectionName: string;
17
+ }): true | {
18
+ populate: object;
19
+ } | undefined;
20
+ getIndexItemId(args: {
21
+ collectionName: string;
22
+ itemId: number;
23
+ }): string;
24
+ getCurrentIndexName(indexAlias?: string): Promise<string>;
25
+ getIncrementedIndexName(indexPrefix: string): Promise<string>;
26
+ deleteOldIndices(indexAlias?: string): Promise<string[]>;
27
+ modifySubfieldsConfigForExtractor(collectionConfig: object): object;
28
+ extractDataToIndex(args: {
29
+ collectionName: string;
30
+ data: object;
31
+ collectionConfig: object;
32
+ }): any;
33
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ let a = {
4
+ a: 1,
5
+ };
6
+ const example = { foo: "bar" };
7
+ a = example;
@@ -1,5 +1,16 @@
1
- export type VirtualCollectionConfig<T extends {}> = {
2
- indexName: string;
1
+ import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types';
2
+ export type VirtualCollectionConfig<T extends StrapiEntity> = {
3
+ /**
4
+ * Optional -
5
+ * The alias of the latest index in Elasticsearch.
6
+ * It also serves as a prefix to actual indexes created in Elasticsearch.
7
+ * The actuall index name will be the `${indexNameBase}_${ind}`, e.g `restaurants_000001` and so on.
8
+ * and the alias will be the `indexNameBase`.
9
+ *
10
+ * If you don't provide this, the default index name will be used as the alias.
11
+ * Omit this property if you want to use the default index for all collections.
12
+ */
13
+ indexAlias?: string;
3
14
  collectionName: string;
4
15
  extractData: (page: number, pageSize?: number) => Promise<T[]>;
5
16
  extractById: (ids: number[]) => Promise<T[]>;
@@ -7,7 +18,11 @@ export type VirtualCollectionConfig<T extends {}> = {
7
18
  collection: string;
8
19
  getIdsToReindex: (result: any) => Promise<number[]>;
9
20
  }>;
10
- mapToIndex?: (item: T) => Promise<object>;
21
+ /**
22
+ * Optional schema to be sent to Elasticsearch when creating the index.
23
+ * If you don't include this, ElasticSearch will automatically create a schema for you.
24
+ */
25
+ mappings?: MappingTypeMapping;
11
26
  };
12
27
  export interface VirtualCollectionsRegistryService {
13
28
  /**
@@ -15,7 +30,7 @@ export interface VirtualCollectionsRegistryService {
15
30
  * @param config - The configuration for the virtual collection
16
31
  * @returns The current instance of the registry
17
32
  */
18
- register<T extends {}>(config: VirtualCollectionConfig<T>): this;
33
+ register<T extends StrapiEntity>(config: VirtualCollectionConfig<T>): this;
19
34
  /**
20
35
  * get all registered virtual collections
21
36
  * @returns An array of all registered virtual collections
@@ -24,6 +39,10 @@ export interface VirtualCollectionsRegistryService {
24
39
  get(collectionName: string): VirtualCollectionConfig<any> | null;
25
40
  findTriggersByCollection(collectionUID: string): Array<VirtualCollectionConfig<any>>;
26
41
  }
42
+ export type StrapiEntity = {
43
+ id: number;
44
+ [key: string]: any;
45
+ };
27
46
  export interface VirtualCollectionsIndexerService {
28
47
  /**
29
48
  * Index a single item from a virtual collection.
@@ -33,15 +52,13 @@ export interface VirtualCollectionsIndexerService {
33
52
  indexItem(collectionName: string, itemId: number): Promise<any>;
34
53
  /**
35
54
  * Reindex all items in a virtual collection index.
36
- * @param indexName - The target index name.
37
55
  */
38
- reindexAll(indexName: string): Promise<any>;
56
+ reindexAll(): Promise<any>;
39
57
  /**
40
58
  * Reindex all items in a virtual collection.
41
- * @param collectionName - The name of the virtual collection.
42
- * @param indexName - The target index name.
59
+ * @param collection - The virtual collection config.
43
60
  */
44
- reindex(collectionName: string, indexName: string): Promise<any>;
61
+ reindex<T extends StrapiEntity>(collection: VirtualCollectionConfig<T>): Promise<any>;
45
62
  /**
46
63
  * Handle a trigger event from a collection.
47
64
  * @param event - The trigger event.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegan-friendly/strapi-plugin-elasticsearch",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A Strapi plugin to enable using Elasticsearch with Strapi CMS.",
5
5
  "homepage": "https://github.com/vegan-friendly/strapi-plugin-elasticsearch",
6
6
  "strapi": {
@@ -61,4 +61,4 @@
61
61
  "ts-node": "^10.9.2",
62
62
  "typescript": "^5.8.3"
63
63
  }
64
- }
64
+ }