|
@@ -4,25 +4,37 @@ import {
|
|
|
Schema, Model, Document,
|
|
Schema, Model, Document,
|
|
|
} from 'mongoose';
|
|
} from 'mongoose';
|
|
|
|
|
|
|
|
|
|
+import loggerFactory from '~/utils/logger';
|
|
|
|
|
+
|
|
|
import { getOrCreateModel } from '../util/mongoose-utils';
|
|
import { getOrCreateModel } from '../util/mongoose-utils';
|
|
|
|
|
|
|
|
-export interface IPageRedirectChains {
|
|
|
|
|
- start: IPageRedirect,
|
|
|
|
|
- end: IPageRedirect,
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-export interface IPageRedirect {
|
|
|
|
|
|
|
+const logger = loggerFactory('growi:models:page-redirects');
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+export type IPageRedirect = {
|
|
|
fromPath: string,
|
|
fromPath: string,
|
|
|
toPath: string,
|
|
toPath: string,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export type IPageRedirectEndpoints = {
|
|
|
|
|
+ start: IPageRedirect,
|
|
|
|
|
+ end: IPageRedirect,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export interface PageRedirectDocument extends IPageRedirect, Document {}
|
|
export interface PageRedirectDocument extends IPageRedirect, Document {}
|
|
|
|
|
|
|
|
export interface PageRedirectModel extends Model<PageRedirectDocument> {
|
|
export interface PageRedirectModel extends Model<PageRedirectDocument> {
|
|
|
- retrievePageRedirectChains(fromPath: string, storedChains?: IPageRedirectChains): Promise<IPageRedirectChains>
|
|
|
|
|
- removePageRedirectByToPath(toPath: string): Promise<void>
|
|
|
|
|
|
|
+ retrievePageRedirectEndpoints(fromPath: string): Promise<IPageRedirectEndpoints>
|
|
|
|
|
+ removePageRedirectsByToPath(toPath: string): Promise<void>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const CHAINS_FIELD_NAME = 'chains';
|
|
|
|
|
+const DEPTH_FIELD_NAME = 'depth';
|
|
|
|
|
+type IPageRedirectWithChains = PageRedirectDocument & {
|
|
|
|
|
+ [CHAINS_FIELD_NAME]: (PageRedirectDocument & { [DEPTH_FIELD_NAME]: number })[]
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const schema = new Schema<PageRedirectDocument, PageRedirectModel>({
|
|
const schema = new Schema<PageRedirectDocument, PageRedirectModel>({
|
|
|
fromPath: {
|
|
fromPath: {
|
|
|
type: String, required: true, unique: true, index: true,
|
|
type: String, required: true, unique: true, index: true,
|
|
@@ -30,18 +42,40 @@ const schema = new Schema<PageRedirectDocument, PageRedirectModel>({
|
|
|
toPath: { type: String, required: true },
|
|
toPath: { type: String, required: true },
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-schema.statics.retrievePageRedirectChains = async function(fromPath: string, storedChains?: IPageRedirectChains): Promise<IPageRedirectChains|null> {
|
|
|
|
|
- const chainedRedirect = await this.findOne({ fromPath });
|
|
|
|
|
|
|
+schema.statics.retrievePageRedirectEndpoints = async function(fromPath: string): Promise<IPageRedirectEndpoints|null> {
|
|
|
|
|
+ const aggResult: IPageRedirectWithChains[] = await this.aggregate([
|
|
|
|
|
+ { $match: { fromPath } },
|
|
|
|
|
+ {
|
|
|
|
|
+ $graphLookup: {
|
|
|
|
|
+ from: 'pageredirects',
|
|
|
|
|
+ startWith: '$toPath',
|
|
|
|
|
+ connectFromField: 'toPath',
|
|
|
|
|
+ connectToField: 'fromPath',
|
|
|
|
|
+ as: CHAINS_FIELD_NAME,
|
|
|
|
|
+ depthField: DEPTH_FIELD_NAME,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
- if (chainedRedirect == null) {
|
|
|
|
|
- return storedChains ?? null;
|
|
|
|
|
|
|
+ if (aggResult.length === 0) {
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const chains = storedChains ?? { start: chainedRedirect, end: chainedRedirect };
|
|
|
|
|
- chains.end = chainedRedirect;
|
|
|
|
|
|
|
+ if (aggResult.length > 1) {
|
|
|
|
|
+ logger.warn(`Although two or more PageRedirect documents starts from '${fromPath}' exists, The first one is used.`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const redirectWithChains = aggResult[0];
|
|
|
|
|
+
|
|
|
|
|
+ // sort chains in desc
|
|
|
|
|
+ const sortedChains = redirectWithChains[CHAINS_FIELD_NAME].sort((a, b) => b[DEPTH_FIELD_NAME] - a[DEPTH_FIELD_NAME]);
|
|
|
|
|
+
|
|
|
|
|
+ const start = { fromPath: redirectWithChains.fromPath, toPath: redirectWithChains.toPath };
|
|
|
|
|
+ const end = sortedChains.length === 0
|
|
|
|
|
+ ? start
|
|
|
|
|
+ : sortedChains[0];
|
|
|
|
|
|
|
|
- // find the end recursively
|
|
|
|
|
- return this.retrievePageRedirectChains(chainedRedirect.toPath, chains);
|
|
|
|
|
|
|
+ return { start, end };
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
schema.statics.removePageRedirectByToPath = async function(toPath: string): Promise<void> {
|
|
schema.statics.removePageRedirectByToPath = async function(toPath: string): Promise<void> {
|