@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.
- package/dist/admin/src/components/SubNavigation/index.d.ts +1 -1
- package/dist/admin/src/components/SubNavigation/index.js +1 -1
- package/dist/admin/src/pages/ConfigureCollection/index.js +13 -5
- package/dist/admin/src/pages/ViewIndexingRunLog/index.js +9 -9
- package/dist/admin/src/utils/axiosInstance.js +3 -5
- package/dist/package.json +1 -1
- package/dist/server/bootstrap.js +1 -1
- package/dist/server/index.d.ts +1 -32
- package/dist/server/services/es-interface.js +12 -4
- package/dist/server/services/helper.d.ts +2 -32
- package/dist/server/services/helper.js +110 -109
- package/dist/server/services/index.d.ts +1 -32
- package/dist/server/services/perform-indexing.js +2 -4
- package/dist/server/services/virtual-collections-indexer.js +38 -26
- package/dist/server/types/esInterface.type.d.ts +5 -2
- package/dist/server/types/helper-service.type.d.ts +33 -0
- package/dist/server/types/helper-service.type.js +7 -0
- package/dist/server/types/virtual-collections.type.d.ts +26 -9
- package/package.json +2 -2
@@ -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:
|
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:
|
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:
|
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:
|
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
|
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 ===
|
75
|
+
if (part.type === 'weekday')
|
75
76
|
formattedDate += `${part.value}, `;
|
76
|
-
if (part.type ===
|
77
|
+
if (part.type === 'day')
|
77
78
|
formattedDate += `${part.value}/`;
|
78
|
-
if (part.type ===
|
79
|
+
if (part.type === 'month')
|
79
80
|
formattedDate += `${part.value}/`;
|
80
|
-
if (part.type ===
|
81
|
+
if (part.type === 'year')
|
81
82
|
formattedDate += `${part.value} `;
|
82
|
-
if (part.type ===
|
83
|
+
if (part.type === 'hour')
|
83
84
|
formattedDate += `${part.value}:`;
|
84
|
-
if (part.type ===
|
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
|
-
|
17
|
-
|
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.
|
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": {
|
package/dist/server/bootstrap.js
CHANGED
@@ -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
|
-
|
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'];
|
package/dist/server/index.d.ts
CHANGED
@@ -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
|
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 ===
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
const
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
31
|
-
const
|
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
|
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
|
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(
|
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
|
50
|
+
totalIndexed += await this.reindex(collection);
|
50
51
|
}
|
51
|
-
|
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(
|
58
|
-
const
|
59
|
-
const
|
60
|
-
|
61
|
-
|
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
|
66
|
-
|
67
|
-
|
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
|
79
|
-
const itemId = helper.getIndexItemId({ collectionName, itemId:
|
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) =>
|
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
|
129
|
-
await
|
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
|
+
}
|
@@ -1,5 +1,16 @@
|
|
1
|
-
|
2
|
-
|
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
|
-
|
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
|
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(
|
56
|
+
reindexAll(): Promise<any>;
|
39
57
|
/**
|
40
58
|
* Reindex all items in a virtual collection.
|
41
|
-
* @param
|
42
|
-
* @param indexName - The target index name.
|
59
|
+
* @param collection - The virtual collection config.
|
43
60
|
*/
|
44
|
-
reindex
|
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.
|
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
|
+
}
|