Przeglądaj źródła

Added conditions to Exclude/Include empty pages when finding pages

Taichi Masuyama 4 lat temu
rodzic
commit
75aad134d6

+ 28 - 16
packages/app/src/server/models/obsolete-page.js

@@ -79,8 +79,17 @@ const populateDataToShowRevision = (page, userPublicFields) => {
 
 
 export class PageQueryBuilder {
 export class PageQueryBuilder {
 
 
-  constructor(query) {
+  constructor(query, includeEmpty = false) {
     this.query = query;
     this.query = query;
+    if (!includeEmpty) {
+      this.query = this.query
+        .and({
+          $or: [
+            { isEmpty: false },
+            { isEmpty: null }, // for v4 compatibility
+          ],
+        });
+    }
   }
   }
 
 
   addConditionToExcludeTrashed() {
   addConditionToExcludeTrashed() {
@@ -603,11 +612,14 @@ export const getPageSchema = (crowi) => {
   };
   };
 
 
   // find page by path
   // find page by path
-  pageSchema.statics.findByPath = function(path) {
+  pageSchema.statics.findByPath = function(path, includeEmpty = false) {
     if (path == null) {
     if (path == null) {
       return null;
       return null;
     }
     }
-    return this.findOne({ path });
+
+    const builder = new PageQueryBuilder(this.findOne({ path }), includeEmpty);
+
+    return builder.query.exec();
   };
   };
 
 
   /**
   /**
@@ -615,7 +627,7 @@ export const getPageSchema = (crowi) => {
    * @param {User} user User instance
    * @param {User} user User instance
    * @param {UserGroup[]} userGroups List of UserGroup instances
    * @param {UserGroup[]} userGroups List of UserGroup instances
    */
    */
-  pageSchema.statics.findAncestorByPathAndViewer = async function(path, user, userGroups) {
+  pageSchema.statics.findAncestorByPathAndViewer = async function(path, user, userGroups, includeEmpty = false) {
     if (path == null) {
     if (path == null) {
       throw new Error('path is required.');
       throw new Error('path is required.');
     }
     }
@@ -636,10 +648,10 @@ export const getPageSchema = (crowi) => {
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
     }
 
 
-    const queryBuilder = new PageQueryBuilder(baseQuery);
+    const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups);
 
 
-    return await queryBuilder.query.exec();
+    return queryBuilder.query.exec();
   };
   };
 
 
   pageSchema.statics.findByRedirectTo = function(path) {
   pageSchema.statics.findByRedirectTo = function(path) {
@@ -649,22 +661,22 @@ export const getPageSchema = (crowi) => {
   /**
   /**
    * find pages that is match with `path` and its descendants
    * find pages that is match with `path` and its descendants
    */
    */
-  pageSchema.statics.findListWithDescendants = async function(path, user, option = {}) {
-    const builder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListWithDescendants = async function(path, user, option = {}, includeEmpty = false) {
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListWithDescendants(path, option);
     builder.addConditionToListWithDescendants(path, option);
 
 
-    return await findListFromBuilderAndViewer(builder, user, false, option);
+    return findListFromBuilderAndViewer(builder, user, false, option);
   };
   };
 
 
   /**
   /**
    * find pages that is match with `path` and its descendants whitch user is able to manage
    * find pages that is match with `path` and its descendants whitch user is able to manage
    */
    */
-  pageSchema.statics.findManageableListWithDescendants = async function(page, user, option = {}) {
+  pageSchema.statics.findManageableListWithDescendants = async function(page, user, option = {}, includeEmpty = false) {
     if (user == null) {
     if (user == null) {
       return null;
       return null;
     }
     }
 
 
-    const builder = new PageQueryBuilder(this.find());
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListWithDescendants(page.path, option);
     builder.addConditionToListWithDescendants(page.path, option);
     builder.addConditionToExcludeRedirect();
     builder.addConditionToExcludeRedirect();
 
 
@@ -685,11 +697,11 @@ export const getPageSchema = (crowi) => {
   /**
   /**
    * find pages that start with `path`
    * find pages that start with `path`
    */
    */
-  pageSchema.statics.findListByStartWith = async function(path, user, option) {
-    const builder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListByStartWith = async function(path, user, option, includeEmpty = false) {
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListByStartWith(path, option);
     builder.addConditionToListByStartWith(path, option);
 
 
-    return await findListFromBuilderAndViewer(builder, user, false, option);
+    return findListFromBuilderAndViewer(builder, user, false, option);
   };
   };
 
 
   /**
   /**
@@ -1096,8 +1108,8 @@ export const getPageSchema = (crowi) => {
     await this.removeRedirectOriginPageByPath(redirectPage.path);
     await this.removeRedirectOriginPageByPath(redirectPage.path);
   };
   };
 
 
-  pageSchema.statics.findListByPathsArray = async function(paths) {
-    const queryBuilder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListByPathsArray = async function(paths, includeEmpty = false) {
+    const queryBuilder = new PageQueryBuilder(this.find(), includeEmpty);
     queryBuilder.addConditionToListByPathsArray(paths);
     queryBuilder.addConditionToListByPathsArray(paths);
 
 
     return await queryBuilder.query.exec();
     return await queryBuilder.query.exec();

+ 14 - 12
packages/app/src/server/models/page.ts

@@ -41,7 +41,7 @@ export interface PageModel extends Model<PageDocument> {
   [x: string]: any; // for obsolete methods
   [x: string]: any; // for obsolete methods
   createEmptyPagesByPaths(paths: string[]): Promise<void>
   createEmptyPagesByPaths(paths: string[]): Promise<void>
   getParentIdAndFillAncestors(path: string): Promise<string | null>
   getParentIdAndFillAncestors(path: string): Promise<string | null>
-  findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?: boolean): Promise<PageDocument[]>
+  findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?: boolean, includeEmpty?: boolean): Promise<PageDocument[]>
   findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
   findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
   findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
   findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
   findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>
   findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>
@@ -143,7 +143,7 @@ const generateChildrenRegExp = (path: string): RegExp => {
  */
  */
 schema.statics.createEmptyPagesByPaths = async function(paths: string[]): Promise<void> {
 schema.statics.createEmptyPagesByPaths = async function(paths: string[]): Promise<void> {
   // find existing parents
   // find existing parents
-  const builder = new PageQueryBuilder(this.find({}, { _id: 0, path: 1 }));
+  const builder = new PageQueryBuilder(this.find({}, { _id: 0, path: 1 }), true); // includeEmpty = true
   const existingPages = await builder
   const existingPages = await builder
     .addConditionToListByPathsArray(paths)
     .addConditionToListByPathsArray(paths)
     .query
     .query
@@ -165,7 +165,7 @@ schema.statics.createEmptyPagesByPaths = async function(paths: string[]): Promis
 };
 };
 
 
 /*
 /*
- * Find the pages parent and update if the parent exists.
+ * Find the parent and update if the parent exists.
  * If not,
  * If not,
  *   - first   run createEmptyPagesByPaths with ancestor's paths to ensure all the ancestors exist
  *   - first   run createEmptyPagesByPaths with ancestor's paths to ensure all the ancestors exist
  *   - second  update ancestor pages' parent
  *   - second  update ancestor pages' parent
@@ -175,17 +175,20 @@ schema.statics.getParentIdAndFillAncestors = async function(path: string): Promi
   const parentPath = nodePath.dirname(path);
   const parentPath = nodePath.dirname(path);
 
 
   const parent = await this.findOne({ path: parentPath }); // find the oldest parent which must always be the true parent
   const parent = await this.findOne({ path: parentPath }); // find the oldest parent which must always be the true parent
-  if (parent != null) { // fill parents if parent is null
+  if (parent != null) {
     return parent._id;
     return parent._id;
   }
   }
 
 
+  /*
+   * Fill parents if parent is null
+   */
   const ancestorPaths = collectAncestorPaths(path); // paths of parents need to be created
   const ancestorPaths = collectAncestorPaths(path); // paths of parents need to be created
 
 
   // just create ancestors with empty pages
   // just create ancestors with empty pages
   await this.createEmptyPagesByPaths(ancestorPaths);
   await this.createEmptyPagesByPaths(ancestorPaths);
 
 
   // find ancestors
   // find ancestors
-  const builder = new PageQueryBuilder(this.find({}, { _id: 1, path: 1 }));
+  const builder = new PageQueryBuilder(this.find({}, { _id: 1, path: 1 }), true); // includeEmpty = true
   const ancestors = await builder
   const ancestors = await builder
     .addConditionToListByPathsArray(ancestorPaths)
     .addConditionToListByPathsArray(ancestorPaths)
     .addConditionToSortAncestorPages()
     .addConditionToSortAncestorPages()
@@ -193,7 +196,6 @@ schema.statics.getParentIdAndFillAncestors = async function(path: string): Promi
     .lean()
     .lean()
     .exec();
     .exec();
 
 
-
   const ancestorsMap = new Map(); // Map<path, _id>
   const ancestorsMap = new Map(); // Map<path, _id>
   ancestors.forEach(page => ancestorsMap.set(page.path, page._id));
   ancestors.forEach(page => ancestorsMap.set(page.path, page._id));
 
 
@@ -234,14 +236,14 @@ const addViewerCondition = async(queryBuilder: PageQueryBuilder, user, userGroup
  * Find a page by path and viewer. Pass false to useFindOne to use findOne method.
  * Find a page by path and viewer. Pass false to useFindOne to use findOne method.
  */
  */
 schema.statics.findByPathAndViewer = async function(
 schema.statics.findByPathAndViewer = async function(
-    path: string | null, user, userGroups = null, useFindOne = true,
+    path: string | null, user, userGroups = null, useFindOne = true, includeEmpty = false,
 ): Promise<PageDocument | PageDocument[] | null> {
 ): Promise<PageDocument | PageDocument[] | null> {
   if (path == null) {
   if (path == null) {
     throw new Error('path is required.');
     throw new Error('path is required.');
   }
   }
 
 
   const baseQuery = useFindOne ? this.findOne({ path }) : this.find({ path });
   const baseQuery = useFindOne ? this.findOne({ path }) : this.find({ path });
-  const queryBuilder = new PageQueryBuilder(baseQuery);
+  const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
   return queryBuilder.query.exec();
   return queryBuilder.query.exec();
@@ -269,7 +271,7 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
   ancestorPaths.push(path); // include target
   ancestorPaths.push(path); // include target
 
 
   // Do not populate
   // Do not populate
-  const queryBuilder = new PageQueryBuilder(this.find());
+  const queryBuilder = new PageQueryBuilder(this.find(), true); // includeEmpty = true
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
   const _targetAndAncestors: PageDocument[] = await queryBuilder
   const _targetAndAncestors: PageDocument[] = await queryBuilder
@@ -298,11 +300,11 @@ schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPath
   if (hasSlash(parentPathOrId)) {
   if (hasSlash(parentPathOrId)) {
     const path = parentPathOrId;
     const path = parentPathOrId;
     const regexp = generateChildrenRE2(path);
     const regexp = generateChildrenRE2(path);
-    queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }));
+    queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }), true); // includeEmpty = true
   }
   }
   else {
   else {
     const parentId = parentPathOrId;
     const parentId = parentPathOrId;
-    queryBuilder = new PageQueryBuilder(this.find({ parent: parentId }));
+    queryBuilder = new PageQueryBuilder(this.find({ parent: parentId }), true); // includeEmpty = true
   }
   }
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
@@ -314,7 +316,7 @@ schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: strin
   const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
   const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
 
 
   // get pages at once
   // get pages at once
-  const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }));
+  const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }), true); // includeEmpty = true
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
   const _pages = await queryBuilder
   const _pages = await queryBuilder
     .addConditionAsMigrated()
     .addConditionAsMigrated()

+ 1 - 1
packages/app/src/server/service/page.js

@@ -999,7 +999,7 @@ class PageService {
         await Page.createEmptyPagesByPaths(parentPaths);
         await Page.createEmptyPagesByPaths(parentPaths);
 
 
         // find parents again
         // find parents again
-        const builder = new PageQueryBuilder(Page.find({}, { _id: 1, path: 1 }));
+        const builder = new PageQueryBuilder(Page.find({}, { _id: 1, path: 1 }), true); // includeEmpty = true
         const parents = await builder
         const parents = await builder
           .addConditionToListByPathsArray(parentPaths)
           .addConditionToListByPathsArray(parentPaths)
           .query
           .query