@vegan-friendly/strapi-plugin-elasticsearch 0.2.9 → 0.3.1
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/package.json +2 -2
- package/dist/server/bootstrap.js +3 -0
- package/dist/server/content-types/index.d.ts +4 -0
- package/dist/server/content-types/tasks.d.ts +4 -0
- package/dist/server/content-types/tasks.js +5 -1
- package/dist/server/index.d.ts +7 -3
- package/dist/server/services/index.d.ts +3 -3
- package/dist/server/services/perform-indexing.d.ts +1 -1
- package/dist/server/services/perform-indexing.js +20 -17
- package/dist/server/services/schedule-indexing.d.ts +2 -2
- package/dist/server/services/schedule-indexing.js +23 -4
- package/dist/server/services/virtual-collections-indexer.js +23 -11
- package/dist/server/services/virtual-collections-registry.js +0 -1
- package/dist/server/types/virtual-collections.type.d.ts +0 -5
- package/package.json +3 -3
package/dist/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vegan-friendly/strapi-plugin-elasticsearch",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.1",
|
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": {
|
@@ -54,7 +54,7 @@
|
|
54
54
|
}
|
55
55
|
],
|
56
56
|
"engines": {
|
57
|
-
"node": ">=16.0.0
|
57
|
+
"node": ">=16.0.0",
|
58
58
|
"npm": ">=6.0.0"
|
59
59
|
},
|
60
60
|
"license": "MIT",
|
package/dist/server/bootstrap.js
CHANGED
@@ -156,6 +156,9 @@ exports.default = async ({ strapi }) => {
|
|
156
156
|
},
|
157
157
|
});
|
158
158
|
});
|
159
|
+
// clean up old indexing tasks, as server is booting.
|
160
|
+
// allow 60 seconds, in case strapi is being run in cluster mode and this is the second instance
|
161
|
+
await scheduleIndexingService.getActiveFullIndexingTasks(60);
|
159
162
|
configureIndexingService.markInitialized();
|
160
163
|
}
|
161
164
|
catch (err) {
|
@@ -30,10 +30,14 @@ exports.default = {
|
|
30
30
|
},
|
31
31
|
indexing_status: {
|
32
32
|
type: 'enumeration',
|
33
|
-
enum: ['to-be-done', 'in-progress', 'done'],
|
33
|
+
enum: ['to-be-done', 'in-progress', 'done', 'failed'],
|
34
34
|
required: true,
|
35
35
|
default: 'to-be-done',
|
36
36
|
},
|
37
|
+
error_message: {
|
38
|
+
type: 'string',
|
39
|
+
required: false,
|
40
|
+
},
|
37
41
|
full_site_indexing: {
|
38
42
|
type: 'boolean',
|
39
43
|
},
|
package/dist/server/index.d.ts
CHANGED
@@ -142,9 +142,9 @@ declare const _default: {
|
|
142
142
|
recordId: any;
|
143
143
|
}): Promise<void>;
|
144
144
|
getItemsPendingToBeIndexed(): Promise<any>;
|
145
|
-
markIndexingTaskComplete(recId: any): Promise<void>;
|
145
|
+
markIndexingTaskComplete(recId: any, error?: string | null): Promise<void>;
|
146
146
|
markIndexingTaskInProgress(recId: any): Promise<void>;
|
147
|
-
|
147
|
+
getActiveFullIndexingTasks(staleTaskThresholdSeconds?: number): Promise<any[]>;
|
148
148
|
};
|
149
149
|
esInterface: ({ strapi }: {
|
150
150
|
strapi: any;
|
@@ -152,7 +152,7 @@ declare const _default: {
|
|
152
152
|
indexer: ({ strapi }: {
|
153
153
|
strapi: any;
|
154
154
|
}) => {
|
155
|
-
rebuildIndex(
|
155
|
+
rebuildIndex(task?: any): Promise<boolean>;
|
156
156
|
indexCollection(collectionName: any, indexName?: string | null): Promise<number>;
|
157
157
|
indexPendingData(): Promise<boolean>;
|
158
158
|
};
|
@@ -215,6 +215,10 @@ declare const _default: {
|
|
215
215
|
required: boolean;
|
216
216
|
default: string;
|
217
217
|
};
|
218
|
+
error_message: {
|
219
|
+
type: string;
|
220
|
+
required: boolean;
|
221
|
+
};
|
218
222
|
full_site_indexing: {
|
219
223
|
type: string;
|
220
224
|
};
|
@@ -38,9 +38,9 @@ declare const _default: {
|
|
38
38
|
recordId: any;
|
39
39
|
}): Promise<void>;
|
40
40
|
getItemsPendingToBeIndexed(): Promise<any>;
|
41
|
-
markIndexingTaskComplete(recId: any): Promise<void>;
|
41
|
+
markIndexingTaskComplete(recId: any, error?: string | null): Promise<void>;
|
42
42
|
markIndexingTaskInProgress(recId: any): Promise<void>;
|
43
|
-
|
43
|
+
getActiveFullIndexingTasks(staleTaskThresholdSeconds?: number): Promise<any[]>;
|
44
44
|
};
|
45
45
|
esInterface: ({ strapi }: {
|
46
46
|
strapi: any;
|
@@ -48,7 +48,7 @@ declare const _default: {
|
|
48
48
|
indexer: ({ strapi }: {
|
49
49
|
strapi: any;
|
50
50
|
}) => {
|
51
|
-
rebuildIndex(
|
51
|
+
rebuildIndex(task?: any): Promise<boolean>;
|
52
52
|
indexCollection(collectionName: any, indexName?: string | null): Promise<number>;
|
53
53
|
indexPendingData(): Promise<boolean>;
|
54
54
|
};
|
@@ -1,7 +1,7 @@
|
|
1
1
|
declare const _default: ({ strapi }: {
|
2
2
|
strapi: any;
|
3
3
|
}) => {
|
4
|
-
rebuildIndex(
|
4
|
+
rebuildIndex(task?: any): Promise<boolean>;
|
5
5
|
indexCollection(collectionName: any, indexName?: string | null): Promise<number>;
|
6
6
|
indexPendingData(): Promise<boolean>;
|
7
7
|
};
|
@@ -1,17 +1,19 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.default = ({ strapi }) => ({
|
4
|
-
async rebuildIndex(
|
5
|
-
const
|
6
|
-
const
|
7
|
-
const
|
8
|
-
const
|
9
|
-
const
|
10
|
-
const
|
11
|
-
const
|
4
|
+
async rebuildIndex(task = null) {
|
5
|
+
const pluginServices = strapi.plugins['elasticsearch'].services;
|
6
|
+
const helper = pluginServices.helper;
|
7
|
+
const esInterface = pluginServices.esInterface;
|
8
|
+
const scheduleIndexingService = pluginServices.scheduleIndexing;
|
9
|
+
const configureIndexingService = pluginServices.configureIndexing;
|
10
|
+
const logIndexingService = pluginServices.logIndexing;
|
11
|
+
const virtualCollectionsIndexer = pluginServices.virtualCollectionsIndexer;
|
12
|
+
const virtualCollectionsRegistry = pluginServices.virtualCollectionsRegistry;
|
13
|
+
let taskError = null;
|
12
14
|
try {
|
13
15
|
console.log('strapi-plugin-elasticsearch : Request to rebuild the index received.');
|
14
|
-
const fullIndexingInProgress = await scheduleIndexingService.
|
16
|
+
const fullIndexingInProgress = await scheduleIndexingService.getActiveFullIndexingTasks();
|
15
17
|
if (fullIndexingInProgress.length > 0) {
|
16
18
|
const msg = `Indexing is already in progress - see tasks ${fullIndexingInProgress.map((t) => t.id)}. This request is ignored and marked as failed.`;
|
17
19
|
console.log('strapi-plugin-elasticsearch : ' + msg);
|
@@ -35,19 +37,19 @@ exports.default = ({ strapi }) => ({
|
|
35
37
|
}
|
36
38
|
//Step 2 : Index all the stuff on this new index
|
37
39
|
console.log('strapi-plugin-elasticsearch : Starting to index all data into the new index.');
|
38
|
-
if (
|
39
|
-
|
40
|
+
if (task == null) {
|
41
|
+
task = await scheduleIndexingService.addFullSiteIndexingTask();
|
40
42
|
}
|
41
|
-
if (
|
42
|
-
await scheduleIndexingService.markIndexingTaskInProgress(
|
43
|
+
if (task?.id) {
|
44
|
+
await scheduleIndexingService.markIndexingTaskInProgress(task.id);
|
43
45
|
let entitiesIndexed = 0;
|
44
46
|
for (let r = 0; r < cols.length; r++) {
|
45
47
|
entitiesIndexed += await this.indexCollection(cols[r], newIndexName);
|
46
48
|
}
|
47
49
|
// Indexing the virtual collections
|
48
|
-
console.log('strapi-plugin-elasticsearch : Starting to index virtual collections. task id : ',
|
50
|
+
console.log('strapi-plugin-elasticsearch : Starting to index virtual collections. task id : ', task.id);
|
49
51
|
const virtualEntriesIndexed = await virtualCollectionsIndexer.reindexAll(newIndexName);
|
50
|
-
await scheduleIndexingService.markIndexingTaskComplete(
|
52
|
+
await scheduleIndexingService.markIndexingTaskComplete(task.id);
|
51
53
|
console.log('strapi-plugin-elasticsearch : Indexing of data into the new index complete.');
|
52
54
|
//Step 4 : Move the alias to this new index
|
53
55
|
await esInterface.attachAliasToIndex(newIndexName);
|
@@ -64,14 +66,15 @@ exports.default = ({ strapi }) => ({
|
|
64
66
|
}
|
65
67
|
}
|
66
68
|
catch (err) {
|
69
|
+
taskError = err.message || String(err);
|
67
70
|
console.log('strapi-plugin-elasticsearch : searchController : An error was encountered while re-indexing.');
|
68
71
|
console.log(err);
|
69
72
|
await logIndexingService.recordIndexingFail(err);
|
70
73
|
throw err;
|
71
74
|
}
|
72
75
|
finally {
|
73
|
-
if (
|
74
|
-
await scheduleIndexingService.markIndexingTaskComplete(
|
76
|
+
if (task?.id) {
|
77
|
+
await scheduleIndexingService.markIndexingTaskComplete(task.id, taskError);
|
75
78
|
}
|
76
79
|
}
|
77
80
|
},
|
@@ -14,8 +14,8 @@ declare const _default: ({ strapi }: {
|
|
14
14
|
recordId: any;
|
15
15
|
}): Promise<void>;
|
16
16
|
getItemsPendingToBeIndexed(): Promise<any>;
|
17
|
-
markIndexingTaskComplete(recId: any): Promise<void>;
|
17
|
+
markIndexingTaskComplete(recId: any, error?: string | null): Promise<void>;
|
18
18
|
markIndexingTaskInProgress(recId: any): Promise<void>;
|
19
|
-
|
19
|
+
getActiveFullIndexingTasks(staleTaskThresholdSeconds?: number): Promise<any[]>;
|
20
20
|
};
|
21
21
|
export default _default;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const STUCK_THRESHOLD_SECONDS = 60 * 60; // 1 hour
|
3
4
|
exports.default = ({ strapi }) => ({
|
4
5
|
async addFullSiteIndexingTask() {
|
5
6
|
const data = await strapi.entityService.create('plugin::elasticsearch.task', {
|
@@ -52,10 +53,12 @@ exports.default = ({ strapi }) => ({
|
|
52
53
|
});
|
53
54
|
return entries;
|
54
55
|
},
|
55
|
-
async markIndexingTaskComplete(recId) {
|
56
|
+
async markIndexingTaskComplete(recId, error = null) {
|
57
|
+
const status = error ? 'failed' : 'done';
|
56
58
|
const entries = await strapi.entityService.update('plugin::elasticsearch.task', recId, {
|
57
59
|
data: {
|
58
|
-
indexing_status:
|
60
|
+
indexing_status: status,
|
61
|
+
error_message: error,
|
59
62
|
},
|
60
63
|
});
|
61
64
|
},
|
@@ -66,13 +69,29 @@ exports.default = ({ strapi }) => ({
|
|
66
69
|
},
|
67
70
|
});
|
68
71
|
},
|
69
|
-
async
|
72
|
+
async getActiveFullIndexingTasks(staleTaskThresholdSeconds) {
|
73
|
+
if (!staleTaskThresholdSeconds) {
|
74
|
+
staleTaskThresholdSeconds = Number(strapi.config.get('plugin.elasticsearch').staleTaskThresholdSeconds) || STUCK_THRESHOLD_SECONDS;
|
75
|
+
}
|
76
|
+
staleTaskThresholdSeconds *= 1000;
|
70
77
|
const entries = await strapi.entityService.findMany('plugin::elasticsearch.task', {
|
71
78
|
filters: {
|
72
79
|
indexing_status: 'in-progress',
|
73
80
|
full_site_indexing: true,
|
74
81
|
},
|
75
82
|
});
|
76
|
-
|
83
|
+
const now = Date.now();
|
84
|
+
const activeTasks = [];
|
85
|
+
for (const t of entries) {
|
86
|
+
const updatedAt = new Date(t.updatedAt || t.createdAt).getTime();
|
87
|
+
if (now - updatedAt > staleTaskThresholdSeconds) {
|
88
|
+
await strapi.plugins['elasticsearch'].services.logIndexing.recordIndexingFail(`Task ${t.id} was stuck in-progress for too long. Marking as failed.`);
|
89
|
+
await this.markIndexingTaskComplete(t.id, 'Stuck in-progress');
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
activeTasks.push(t);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return activeTasks;
|
77
96
|
},
|
78
97
|
});
|
@@ -24,7 +24,9 @@ exports.default = ({ strapi }) => {
|
|
24
24
|
try {
|
25
25
|
const results = await collection.extractByIds([itemId]);
|
26
26
|
if (!results || !Array.isArray(results) || results.length === 0) {
|
27
|
-
|
27
|
+
// item does not exit - delete it from index
|
28
|
+
await this.deleteItem(collectionName, itemId);
|
29
|
+
strapi.log.debug(`Deleted virtual item: ${collectionName}:${itemId}`);
|
28
30
|
return null;
|
29
31
|
}
|
30
32
|
const itemData = results[0];
|
@@ -58,9 +60,11 @@ exports.default = ({ strapi }) => {
|
|
58
60
|
async reindex(collection) {
|
59
61
|
const collectionName = collection.collectionName;
|
60
62
|
const privateIndexAlias = collection.indexAlias;
|
63
|
+
const pageSize = 100;
|
61
64
|
const helper = getHelperService();
|
62
65
|
let timestamp = Date.now();
|
63
66
|
let indexName = '';
|
67
|
+
let errors = 0;
|
64
68
|
try {
|
65
69
|
const esInterface = getElasticsearchService();
|
66
70
|
if (privateIndexAlias) {
|
@@ -75,7 +79,16 @@ exports.default = ({ strapi }) => {
|
|
75
79
|
let totalIndexed = 0;
|
76
80
|
const pageLimit = 10000;
|
77
81
|
while (page <= pageLimit) {
|
78
|
-
|
82
|
+
let pageData;
|
83
|
+
try {
|
84
|
+
pageData = await collection.extractData(page, pageSize);
|
85
|
+
}
|
86
|
+
catch (error) {
|
87
|
+
strapi.log.error(`Error extracting data for page ${page} of ${collectionName}: ${error.message}`);
|
88
|
+
errors += pageSize;
|
89
|
+
page++;
|
90
|
+
continue;
|
91
|
+
}
|
79
92
|
strapi.log.debug(`Extracted ${pageData.length} items from ${collectionName} for page ${page}`);
|
80
93
|
if (!Array.isArray(pageData) || pageData.length === 0) {
|
81
94
|
break;
|
@@ -97,12 +110,18 @@ exports.default = ({ strapi }) => {
|
|
97
110
|
operations.push({ itemId, itemData });
|
98
111
|
}
|
99
112
|
if (operations.length > 0) {
|
100
|
-
await Promise.all(operations.map((op) => esInterface.indexDataToSpecificIndex(op, indexName))
|
113
|
+
await Promise.all(operations.map((op) => esInterface.indexDataToSpecificIndex(op, indexName).catch((err) => {
|
114
|
+
strapi.log.error(`Failed to index item ${op.itemId} in ${collectionName}: ${err}`);
|
115
|
+
errors++;
|
116
|
+
})));
|
101
117
|
}
|
102
118
|
totalIndexed += pageData.length;
|
103
119
|
prevPageData = pageData;
|
104
120
|
page++;
|
105
121
|
}
|
122
|
+
if (errors > 0) {
|
123
|
+
throw new Error(`Failed to index ${errors} of ${totalIndexed} items for virtual collection ${collectionName}. Errors were logged. Alias was not updated. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}`);
|
124
|
+
}
|
106
125
|
strapi.log.info(`Reindexed ${totalIndexed} items for virtual collection: ${collectionName}. took ${(0, humanize_duration_1.default)(Date.now() - timestamp)}. now updating alias.`);
|
107
126
|
if (privateIndexAlias) {
|
108
127
|
timestamp = Date.now();
|
@@ -138,14 +157,7 @@ exports.default = ({ strapi }) => {
|
|
138
157
|
const idsToReindex = await trigger.getIdsToReindex(result);
|
139
158
|
// Reindex each item
|
140
159
|
for (const id of idsToReindex) {
|
141
|
-
|
142
|
-
if (isDelete) {
|
143
|
-
//delete the item from the index, if the item being delete is the one being reindexed
|
144
|
-
await this.deleteItem(collection.collectionName, id);
|
145
|
-
}
|
146
|
-
else {
|
147
|
-
await this.indexItem(collection.collectionName, id);
|
148
|
-
}
|
160
|
+
await this.indexItem(collection.collectionName, id);
|
149
161
|
}
|
150
162
|
}
|
151
163
|
},
|
@@ -45,11 +45,6 @@ export type VirtualCollectionConfig = {
|
|
45
45
|
* @returns ids of the items to be reindexed.
|
46
46
|
*/
|
47
47
|
getIdsToReindex: GetIdsToIndexFunction;
|
48
|
-
/**
|
49
|
-
* if true, and the trigger is a delete event, the item of the virtual collection will be deleted as well if the id returned from getIdsToReindex match.
|
50
|
-
* defaults to false.
|
51
|
-
*/
|
52
|
-
alsoTriggerDelete?: boolean;
|
53
48
|
}>;
|
54
49
|
/**
|
55
50
|
* Optional schema to be sent to Elasticsearch when creating the index.
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vegan-friendly/strapi-plugin-elasticsearch",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.1",
|
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": {
|
@@ -54,7 +54,7 @@
|
|
54
54
|
}
|
55
55
|
],
|
56
56
|
"engines": {
|
57
|
-
"node": ">=16.0.0
|
57
|
+
"node": ">=16.0.0",
|
58
58
|
"npm": ">=6.0.0"
|
59
59
|
},
|
60
60
|
"license": "MIT",
|
@@ -64,4 +64,4 @@
|
|
64
64
|
"ts-node": "^10.9.2",
|
65
65
|
"typescript": "^5.8.3"
|
66
66
|
}
|
67
|
-
}
|
67
|
+
}
|