Просмотр исходного кода

Merge pull request #5086 from weseek/update-descendant-count

add method to update descendant count
Yohei Shiina 4 лет назад
Родитель
Сommit
0aa7697f84
2 измененных файлов с 97 добавлено и 0 удалено
  1. 71 0
      packages/app/src/server/models/page.ts
  2. 26 0
      packages/app/src/server/service/page.js

+ 71 - 0
packages/app/src/server/models/page.ts

@@ -341,6 +341,77 @@ schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: strin
   return pathToChildren;
   return pathToChildren;
 };
 };
 
 
+/**
+ * return aggregate condition to get following pages
+ * - page that has the same path as the provided path
+ * - pages that are descendants of the above page
+ */
+schema.statics.getAggrConditionForPageWithProvidedPathAndDescendants = function(path:string) {
+  const match = {
+    $match: {
+      $or: [
+        {
+          // https://regex101.com/r/ncnxaR/1
+          path: { $regex: `^${path}(/.*|$)` },
+          parent: { $ne: null },
+        },
+        { path: '/' },
+      ],
+    },
+  };
+  return [
+    match,
+    {
+      $project: {
+        path: 1,
+        parent: 1,
+        field_length: { $strLenCP: '$path' },
+      },
+    },
+    { $sort: { field_length: -1 } },
+    { $project: { field_length: 0 } },
+  ];
+};
+
+schema.statics.recountPage = async function(id:mongoose.Types.ObjectId):Promise<void> {
+  const res = await this.aggregate(
+    [
+      {
+        $match: {
+          parent: id,
+        },
+      },
+      {
+        $project: {
+          path: 1,
+          parent: 1,
+          descendantCount: 1,
+        },
+      },
+      {
+        $group: {
+          _id: '$parent',
+          sumOfDescendantCount: {
+            $sum: '$descendantCount',
+          },
+          sumOfDocsCount: {
+            $sum: 1,
+          },
+        },
+      },
+      {
+        $set: {
+          descendantCount: {
+            $sum: ['$sumOfDescendantCount', '$sumOfDocsCount'],
+          },
+        },
+      },
+    ],
+  );
+
+  const query = { descendantCount: res.length === 0 ? 0 : res[0].descendantCount };
+  await this.findByIdAndUpdate(id, query);
+};
 
 
 /*
 /*
  * Merge obsolete page model methods and define new methods which depend on crowi instance
  * Merge obsolete page model methods and define new methods which depend on crowi instance

+ 26 - 0
packages/app/src/server/service/page.js

@@ -1248,6 +1248,32 @@ 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 } });
   }
   }
 
 
+  // update descendantCount of all pages with path starting with provided string
+  async updateSelfAndDescendantCount(path = '/') {
+    const BATCH_SIZE = 200;
+    const Page = this.crowi.model('Page');
+
+    const aggregateCondition = Page.getAggrConditionForPageWithProvidedPathAndDescendants(path);
+    const aggregatedPages = await Page.aggregate(aggregateCondition).cursor({ batchSize: BATCH_SIZE });
+
+    const recountWriteStream = new Writable({
+      objectMode: true,
+      async write(pageDocuments, encoding, callback) {
+        for (const document of pageDocuments) {
+          // eslint-disable-next-line no-await-in-loop
+          await Page.recountPage(document._id);
+        }
+        callback();
+      },
+      final(callback) {
+        callback();
+      },
+    });
+    aggregatedPages
+      .pipe(createBatchStream(BATCH_SIZE))
+      .pipe(recountWriteStream);
+  }
+
 }
 }
 
 
 module.exports = PageService;
 module.exports = PageService;