瀏覽代碼

update descendantCount

yohei0125 4 年之前
父節點
當前提交
8926ecb7ba
共有 1 個文件被更改,包括 99 次插入50 次删除
  1. 99 50
      packages/app/src/server/service/page.js

+ 99 - 50
packages/app/src/server/service/page.js

@@ -31,7 +31,8 @@ class PageService {
     // init
     // init
     this.initPageEvent();
     this.initPageEvent();
     // this code is written to check if method works. will delete in the end.
     // this code is written to check if method works. will delete in the end.
-    this.updateAllDescendantCount().then(res => logger.info(res)).catch(err => logger.warn(err));
+    this.updateDescendantCount('/', 1).then(res => logger.info(res)).catch(err => logger.warn(err));
+    // this.recountPage().then(res => logger.info(res)).catch(err => logger.warn(err));
   }
   }
 
 
   initPageEvent() {
   initPageEvent() {
@@ -1247,60 +1248,108 @@ class PageService {
     return Page.count({ parent: null, creator: user, grant: { $ne: Page.GRANT_PUBLIC } });
     return Page.count({ parent: null, creator: user, grant: { $ne: Page.GRANT_PUBLIC } });
   }
   }
 
 
+  async findSelfAndDescendant(path, grant = null) {
+    const Page = this.crowi.model('Page');
+    return Page.aggregate(
+      [
+        { $match: { path: { $regex: `^${path}.*` }, grant } },
+        {
+          $project: {
+            path: 1,
+            parent: 1,
+            field_length: { $strLenCP: '$path' },
+          },
+        },
+        { $sort: { field_length: -1 } },
+        { $project: { field_length: 0 } },
+      ],
+    );
+  }
 
 
-  async updateAllDescendantCount(path = '/', grant = 1) {
-    const Page = mongoose.model('Page');
+  async recountPage(targetIds) {
+    const Page = this.crowi.model('Page');
+    const res = await Page.aggregate(
+      [
+        {
+          $match: {
+            parent: { $in: targetIds },
+          },
+
+        },
+        {
+          $project: {
+            path: 1,
+            parent: 1,
+            descendantCount: 1,
+          },
+        },
+        {
+          $group: {
+            _id: '$parent',
+            sumOfDescendantCount: {
+              $sum: '$descendantCount',
+            },
+            sumOfDocsCount: {
+              $sum: 1,
+            },
+          },
+        },
+        {
+          $set: {
+            descendantCount: {
+              $sum: ['$sumOfDescendantCount', '$sumOfDocsCount'],
+            },
+          },
+        },
+      ],
+    );
+    const idWithChildren = res.map(data => data._id.toString());
+    const pageIdsWithNoChildren = targetIds.filter((targetId) => {
+      return !idWithChildren.includes(targetId.toString());
+    });
+
+    const operationForPageWithChildren = res.map((data) => {
+      return {
+        updateOne: {
+          filter: { _id: data._id },
+          update: { $set: { descendantCount: data.descendantCount } },
+        },
+      };
+
+    });
+    const operationsForPageWithoutChildren = pageIdsWithNoChildren.map((data) => {
+      return {
+        updateOne: {
+          filter: { _id: data._id },
+          update: { $set: { descendantCount: 0 } },
+        },
+      };
+    });
+
+    const operations = operationForPageWithChildren.concat(operationsForPageWithoutChildren);
+    await Page.bulkWrite(operations);
+  }
 
 
-    /**
-     * retrieve all public descendants pages starting from path arg sorted descending order
-     * /A/B/C
-     * /A/B
-     * /A
-     */
-    const publicPages = await Page.findAllDescendantsByPath(path, grant);
-    for (const parentPage of publicPages) {
-
-      // find pages with parentPage._id set in the parent field
-      const childrenPages = publicPages.filter((childPage) => {
-        if (childPage.parent == null) return;
-        return childPage.parent.toString() === parentPage._id.toString();
+  async updateDescendantCount(path = '/', grant = 1) {
+    const findSelfAndDescendant = await this.findSelfAndDescendant(path, grant);
+    const updatedPageIds = []; // 既に更新されたページ
+    for (const page of findSelfAndDescendant) {
+
+      // find pages that have the same parent
+      const pagesWithSameParentIds = findSelfAndDescendant.filter((p) => {
+        return page.parent?.toString() === p.parent?.toString();
+      }).map(p => p._id);
+
+      // filter out pages that are already updated
+      const recountTargetIds = pagesWithSameParentIds.filter((id) => {
+        return !updatedPageIds.map(_ => _.toString()).includes(id.toString());
       });
       });
 
 
-      // if children not exist, set descendantCount to 0
-      if (childrenPages.length === 0) {
-        try {
-          // eslint-disable-next-line no-await-in-loop
-          await Page.findOneAndUpdate({ _id: parentPage._id }, { $set: { descendantCount: 0 } });
-        }
-        catch (err) {
-          logger.warn(err);
-        }
-      }
-      else {
-        // if children exist, set descendantCount of parent page by following fomula.
-        // sum of children + sum of descendantCount that each children have in descendantCount field
-        try {
-          // aggregate the sum of descendantCount of parent page
-          // eslint-disable-next-line no-await-in-loop
-          const res = await Page.aggregate([
-            { $match: { parent: parentPage._id } },
-            {
-              $group: {
-                _id: null,
-                sumOfDescendantCount: { $sum: '$descendantCount' },
-              },
-            },
-          ]);
-          const sumOfDescendantCountArray = res.map(res => res.sumOfDescendantCount);
-          const totalDescendantCount = sumOfDescendantCountArray.reduce((prev, current) => prev + current);
+      // eslint-disable-next-line no-await-in-loop
+      await this.recountPage(pagesWithSameParentIds);
+
+      updatedPageIds.push(...recountTargetIds);
 
 
-          // eslint-disable-next-line no-await-in-loop
-          await Page.findOneAndUpdate({ _id: parentPage._id }, { $set: { descendantCount: childrenPages.length + totalDescendantCount } });
-        }
-        catch (err) {
-          logger.warn(err);
-        }
-      }
     }
     }
   }
   }