From 12f3fa14a4c0addd952b802e924c1d56dc9d56fa Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 12 Jan 2023 14:52:27 +0800 Subject: [PATCH] perf(post): cache tags getter --- lib/models/post.js | 69 ++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/lib/models/post.js b/lib/models/post.js index 4cb28421e3..235ce6a878 100644 --- a/lib/models/post.js +++ b/lib/models/post.js @@ -5,7 +5,7 @@ const moment = require('moment'); const { extname, join, sep } = require('path'); const Promise = require('bluebird'); const Moment = require('./types/moment'); -const { full_url_for } = require('hexo-util'); +const { full_url_for, Cache } = require('hexo-util'); function pickID(data) { return data._id; @@ -15,10 +15,12 @@ function removeEmptyTag(tags) { return tags.filter(tag => tag != null && tag !== '').map(tag => `${tag}`); } +const tagsGetterCache = new Cache(); + module.exports = ctx => { const Post = new Schema({ id: String, - title: {type: String, default: ''}, + title: { type: String, default: '' }, date: { type: Moment, default: moment, @@ -30,22 +32,22 @@ module.exports = ctx => { language: ctx.config.languages, timezone: ctx.config.timezone }, - comments: {type: Boolean, default: true}, - layout: {type: String, default: 'post'}, - _content: {type: String, default: ''}, - source: {type: String, required: true}, - slug: {type: String, required: true}, + comments: { type: Boolean, default: true }, + layout: { type: String, default: 'post' }, + _content: { type: String, default: '' }, + source: { type: String, required: true }, + slug: { type: String, required: true }, photos: [String], - link: {type: String, default: ''}, - raw: {type: String, default: ''}, - published: {type: Boolean, default: true}, - content: {type: String}, - excerpt: {type: String}, - more: {type: String} + link: { type: String, default: '' }, + raw: { type: String, default: '' }, + published: { type: Boolean, default: true }, + content: { type: String }, + excerpt: { type: String }, + more: { type: String } }); Post.virtual('path').get(function() { - const path = ctx.execFilterSync('post_permalink', this, {context: ctx}); + const path = ctx.execFilterSync('post_permalink', this, { context: ctx }); return typeof path === 'string' ? path : ''; }); @@ -66,9 +68,9 @@ module.exports = ctx => { const PostTag = ctx.model('PostTag'); const Tag = ctx.model('Tag'); - const ids = PostTag.find({post_id: this._id}, {lean: true}).map(item => item.tag_id); + const ids = PostTag.find({ post_id: this._id }, { lean: true }).map(item => item.tag_id); - return Tag.find({_id: {$in: ids}}); + return Tag.find({ _id: { $in: ids } }); }); Post.method('notPublished', function() { @@ -82,29 +84,30 @@ module.exports = ctx => { // If the post is unpublished then the tag needs to be removed, thus the function cannot be returned early here tags = []; } + tagsGetterCache.flush(); tags = removeEmptyTag(tags); const PostTag = ctx.model('PostTag'); const Tag = ctx.model('Tag'); const id = this._id; - const existed = PostTag.find({post_id: id}, {lean: true}).map(pickID); + const existed = PostTag.find({ post_id: id }, { lean: true }).map(pickID); return Promise.map(tags, tag => { // Find the tag by name - const data = Tag.findOne({name: tag}, {lean: true}); + const data = Tag.findOne({ name: tag }, { lean: true }); if (data) return data; // Insert the tag if not exist - return Tag.insert({name: tag}).catch(err => { + return Tag.insert({ name: tag }).catch(err => { // Try to find the tag again. Throw the error if not found - const data = Tag.findOne({name: tag}, {lean: true}); + const data = Tag.findOne({ name: tag }, { lean: true }); if (data) return data; throw err; }); }).map(tag => { // Find the reference - const ref = PostTag.findOne({post_id: id, tag_id: tag._id}, {lean: true}); + const ref = PostTag.findOne({ post_id: id, tag_id: tag._id }, { lean: true }); if (ref) return ref; // Insert the reference if not exist @@ -123,9 +126,9 @@ module.exports = ctx => { const PostCategory = ctx.model('PostCategory'); const Category = ctx.model('Category'); - const ids = PostCategory.find({post_id: this._id}, {lean: true}).map(item => item.category_id); + const ids = PostCategory.find({ post_id: this._id }, { lean: true }).map(item => item.category_id); - return Category.find({_id: {$in: ids}}); + return Category.find({ _id: { $in: ids } }); }); Post.method('setCategories', function(cats) { @@ -143,7 +146,7 @@ module.exports = ctx => { const Category = ctx.model('Category'); const id = this._id; const allIds = []; - const existed = PostCategory.find({post_id: id}, {lean: true}).map(pickID); + const existed = PostCategory.find({ post_id: id }, { lean: true }).map(pickID); const hasHierarchy = cats.filter(Array.isArray).length > 0; // Add a hierarchy of categories @@ -156,8 +159,8 @@ module.exports = ctx => { // Find the category by name const data = Category.findOne({ name: cat, - parent: i ? parentIds[i - 1] : {$exists: false} - }, {lean: true}); + parent: i ? parentIds[i - 1] : { $exists: false } + }, { lean: true }); if (data) { allIds.push(data._id); @@ -166,15 +169,15 @@ module.exports = ctx => { } // Insert the category if not exist - const obj = {name: cat}; + const obj = { name: cat }; if (i) obj.parent = parentIds[i - 1]; return Category.insert(obj).catch(err => { // Try to find the category again. Throw the error if not found const data = Category.findOne({ name: cat, - parent: i ? parentIds[i - 1] : {$exists: false} - }, {lean: true}); + parent: i ? parentIds[i - 1] : { $exists: false } + }, { lean: true }); if (data) return data; throw err; @@ -189,7 +192,7 @@ module.exports = ctx => { return (hasHierarchy ? Promise.each(cats, addHierarchy) : Promise.resolve(addHierarchy(cats)) ).then(() => allIds).map(catId => { // Find the reference - const ref = PostCategory.findOne({post_id: id, category_id: catId}, {lean: true}); + const ref = PostCategory.findOne({ post_id: id, category_id: catId }, { lean: true }); if (ref) return ref; // Insert the reference if not exist @@ -204,19 +207,19 @@ module.exports = ctx => { // Remove PostTag references Post.pre('remove', data => { const PostTag = ctx.model('PostTag'); - return PostTag.remove({post_id: data._id}); + return PostTag.remove({ post_id: data._id }); }); // Remove PostCategory references Post.pre('remove', data => { const PostCategory = ctx.model('PostCategory'); - return PostCategory.remove({post_id: data._id}); + return PostCategory.remove({ post_id: data._id }); }); // Remove assets Post.pre('remove', data => { const PostAsset = ctx.model('PostAsset'); - return PostAsset.remove({post: data._id}); + return PostAsset.remove({ post: data._id }); }); return Post;