Yuki Takei 6 лет назад
Родитель
Сommit
f77dba2d1a
3 измененных файлов с 68 добавлено и 10 удалено
  1. 36 0
      src/server/models/page-tag-relation.js
  2. 11 0
      src/server/models/tag.js
  3. 21 10
      src/server/util/search.js

+ 36 - 0
src/server/models/page-tag-relation.js

@@ -60,6 +60,42 @@ class PageTagRelation {
     return relations.map((relation) => { return relation.relatedTag.name });
   }
 
+  /**
+   * @return {object} key: Page._id, value: array of tag names
+   */
+  static async getIdToTagNamesMap(pageIds) {
+    // see https://docs.mongodb.com/manual/reference/operator/aggregation/group/#pivot-data
+    const results = await this.aggregate()
+      .match({ relatedPage: { $in: pageIds } })
+      .group({ _id: '$relatedPage', tagIds: { $push: '$relatedTag' } });
+
+    if (results.length === 0) {
+      return {};
+    }
+
+    console.log(results);
+    console.log(results.map(result => result.tagIds));
+
+    // extract distinct tag ids
+    const allTagIds = await results
+      .map(result => result.tagIds) //  [[tagId1, tagId2, ...], [tagId1, tagId3, ...] ... ]
+      .flat(); //                       [tagId1, tagId2, tagId1, tagId3, ...]
+    const distinctTagIds = Array.from(new Set(allTagIds));
+
+    // retrieve tag documents
+    const Tag = mongoose.model('Tag');
+    const tagIdToNameMap = Tag.getIdToNameMap(distinctTagIds);
+
+    // convert to map
+    const idToTagNamesMap = {};
+    results.forEach((result) => {
+      const tagNames = result.tagsIds.map(tagId => tagIdToNameMap[tagId]);
+      idToTagNamesMap[result._id] = tagNames;
+    });
+
+    return idToTagNamesMap;
+  }
+
   static async updatePageTags(pageId, tags) {
     if (pageId == null || tags == null) {
       throw new Error('args \'pageId\' and \'tags\' are required.');

+ 11 - 0
src/server/models/tag.js

@@ -23,6 +23,17 @@ schema.plugin(mongoosePaginate);
  */
 class Tag {
 
+  static async getIdToNameMap(tagIds) {
+    const tags = this.find({ _id: { $in: tagIds } });
+
+    const idToNameMap = {};
+    tags.forEach((tag) => {
+      idToNameMap[tag._id.toString()] = tag.name;
+    });
+
+    return idToNameMap;
+  }
+
   static async findOrCreate(tagName) {
     const tag = await this.findOne({ name: tagName });
     if (!tag) {

+ 21 - 10
src/server/util/search.js

@@ -446,15 +446,26 @@ SearchClient.prototype.addAllPages = async function() {
     },
   });
 
-  // const appendTagNamesStream = new Transform({
-  //   objectMode: true,
-  //   async transform(chunk, encoding, callback) {
-  //     const tagRelations = await PageTagRelation.find({ relatedPage: chunk._id }).populate('relatedTag');
-  //     const tagNames = tagRelations.map((relation) => { return relation.relatedTag.name });
-  //     this.push({ ...chunk, tagNames });
-  //     callback();
-  //   },
-  // });
+  const appendTagNamesStream = new Transform({
+    objectMode: true,
+    async transform(chunk, encoding, callback) {
+      const pageIds = chunk.map(doc => doc._id);
+
+      const idToTagNamesMap = await PageTagRelation.getIdToTagNamesMap(pageIds);
+      const idsHavingTagNames = Object.keys(idToTagNamesMap);
+
+      // append count
+      chunk
+        .filter(doc => idsHavingTagNames.includes(doc._id.toString()))
+        .forEach((doc) => {
+          // append tagName from idToTagNamesMap
+          doc.bookmarkCount = idToTagNamesMap[doc._id.toString()];
+        });
+
+      this.push(chunk);
+      callback();
+    },
+  });
 
   let count = 0;
   const writeStream = new Writable({
@@ -491,7 +502,7 @@ SearchClient.prototype.addAllPages = async function() {
     .pipe(thinOutStream)
     .pipe(batchingStream)
     .pipe(appendBookmarkCountStream)
-    // .pipe(appendTagNamesStream)
+    .pipe(appendTagNamesStream)
     .pipe(writeStream);
 
   return streamToPromise(readStream);