|
@@ -738,7 +738,54 @@ class PageService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async v5RecursiveMigration(grant, rootPath = null) {
|
|
|
|
|
|
|
+ async v5InitialMigration(grant) {
|
|
|
|
|
+ const socket = this.crowicrowi.socketIoService.getAdminSocket();
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this._v5RecursiveMigration(grant);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error('V5 initial miration failed.', err);
|
|
|
|
|
+ socket.emit('v5InitialMirationFailed', { error: err.message });
|
|
|
|
|
+
|
|
|
|
|
+ throw err;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const Page = this.crowi.model('Page');
|
|
|
|
|
+ const indexStatus = await Page.aggregate([{ $indexStats: {} }]);
|
|
|
|
|
+ const pathIndexStatus = indexStatus.filter(status => status.name === 'path_1')?.[0];
|
|
|
|
|
+ const isPathIndexExists = pathIndexStatus != null;
|
|
|
|
|
+ const isUnique = isPathIndexExists && pathIndexStatus.spec?.unique === true;
|
|
|
|
|
+
|
|
|
|
|
+ if (isUnique || !isPathIndexExists) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this._v5NormalizeIndex(isPathIndexExists);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error('V5 index normalization failed.', err);
|
|
|
|
|
+ socket.emit('v5IndexNormalizationFailed', { error: err.message });
|
|
|
|
|
+
|
|
|
|
|
+ throw err;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await this._setIsV5CompatibleTrue();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async _setIsV5CompatibleTrue() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.crowi.configManager.updateConfigsInTheSameNamespace('crowi', {
|
|
|
|
|
+ 'app:isV5Compatible': true,
|
|
|
|
|
+ });
|
|
|
|
|
+ logger.info('Successfully migrated all public pages.');
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.warn('Failed to update app:isV5Compatible to true.');
|
|
|
|
|
+ throw err;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: use websocket to show progress
|
|
|
|
|
+ async _v5RecursiveMigration(grant, rootPath) {
|
|
|
const BATCH_SIZE = 100;
|
|
const BATCH_SIZE = 100;
|
|
|
const PAGES_LIMIT = 1000;
|
|
const PAGES_LIMIT = 1000;
|
|
|
const Page = this.crowi.model('Page');
|
|
const Page = this.crowi.model('Page');
|
|
@@ -767,7 +814,7 @@ class PageService {
|
|
|
baseAggregation = baseAggregation.limit(Math.floor(total * 0.3));
|
|
baseAggregation = baseAggregation.limit(Math.floor(total * 0.3));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const randomPagesStream = await baseAggregation.cursor({ batchSize: BATCH_SIZE }).exec();
|
|
|
|
|
|
|
+ const pagesStream = await baseAggregation.cursor({ batchSize: BATCH_SIZE }).exec();
|
|
|
|
|
|
|
|
// use batch stream
|
|
// use batch stream
|
|
|
const batchStream = createBatchStream(BATCH_SIZE);
|
|
const batchStream = createBatchStream(BATCH_SIZE);
|
|
@@ -800,6 +847,7 @@ class PageService {
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
logger.error('Failed to insert empty pages.', err);
|
|
logger.error('Failed to insert empty pages.', err);
|
|
|
|
|
+ throw err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// find parents again
|
|
// find parents again
|
|
@@ -837,6 +885,7 @@ class PageService {
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
logger.error('Failed to update page.parent.', err);
|
|
logger.error('Failed to update page.parent.', err);
|
|
|
|
|
+ throw err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
callback();
|
|
callback();
|
|
@@ -846,24 +895,41 @@ class PageService {
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- randomPagesStream
|
|
|
|
|
|
|
+ pagesStream
|
|
|
.pipe(batchStream)
|
|
.pipe(batchStream)
|
|
|
.pipe(migratePagesStream);
|
|
.pipe(migratePagesStream);
|
|
|
|
|
|
|
|
await streamToPromise(migratePagesStream);
|
|
await streamToPromise(migratePagesStream);
|
|
|
|
|
+
|
|
|
if (await Page.exists({ grant, parent: null, path: { $ne: '/' } })) {
|
|
if (await Page.exists({ grant, parent: null, path: { $ne: '/' } })) {
|
|
|
return this.v5RecursiveMigration(grant, rootPath);
|
|
return this.v5RecursiveMigration(grant, rootPath);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async _v5NormalizeIndex(isPathIndexExists) {
|
|
|
|
|
+ const collection = mongoose.connection.collection('pages');
|
|
|
|
|
+
|
|
|
|
|
+ if (isPathIndexExists) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // drop pages.path_1 indexes
|
|
|
|
|
+ await collection.dropIndex('path_1');
|
|
|
|
|
+ logger.info('Succeeded to drop unique indexes from pages.path.');
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.warn('Failed to drop unique indexes from pages.path.', err);
|
|
|
|
|
+ throw err;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- await this.crowi.configManager.updateConfigsInTheSameNamespace('crowi', {
|
|
|
|
|
- 'app:isV5Compatible': true,
|
|
|
|
|
- });
|
|
|
|
|
- logger.info('Successfully migrated all public pages.');
|
|
|
|
|
|
|
+ // create indexes without
|
|
|
|
|
+ await collection.createIndex({ path: 1 }, { unique: false });
|
|
|
|
|
+ logger.info('Succeeded to create non-unique indexes on pages.path.');
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
- // just to know
|
|
|
|
|
- logger.error('Failed to update app:isV5Compatible to true.');
|
|
|
|
|
|
|
+ logger.warn('Failed to create non-unique indexes on pages.path.', err);
|
|
|
|
|
+ throw err;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|