Skip to content

Commit

Permalink
perf: reduce the number of traversals through posts (#5119)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenjoezhang authored Nov 29, 2022
1 parent ca51e15 commit 47650c3
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 14 deletions.
10 changes: 2 additions & 8 deletions lib/hexo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,9 @@ class Hexo extends EventEmitter {
return db.model('Page').find(query);
});

locals.set('categories', () => {
// Ignore categories with zero posts
return db.model('Category').filter(category => category.length);
});
locals.set('categories', () => db.model('Category'));

locals.set('tags', () => {
// Ignore tags with zero posts
return db.model('Tag').filter(tag => tag.length);
});
locals.set('tags', () => db.model('Tag'));

locals.set('data', () => {
const obj = {};
Expand Down
4 changes: 3 additions & 1 deletion lib/models/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ module.exports = ctx => {
});

Category.virtual('length').get(function() {
return this.posts.length;
const PostCategory = ctx.model('PostCategory');

return PostCategory.find({category_id: this._id}).length;
});

// Check whether a category exists
Expand Down
13 changes: 13 additions & 0 deletions lib/models/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,17 @@ module.exports = ctx => {
return Tag.find({_id: {$in: ids}});
});

Post.method('notPublished', function() {
// The same condition as ctx._bindLocals
return (!ctx.config.future && this.date > Date.now()) || (!ctx._showDrafts() && this.published === false);
});

Post.method('setTags', function(tags) {
if (this.notPublished()) {
// Ignore tags of draft posts
// If the post is unpublished then the tag needs to be removed, thus the function cannot be returned early here
tags = [];
}
tags = removeEmptyTag(tags);

const PostTag = ctx.model('PostTag');
Expand Down Expand Up @@ -119,6 +129,9 @@ module.exports = ctx => {
});

Post.method('setCategories', function(cats) {
if (this.notPublished()) {
cats = [];
}
// Remove empty categories, preserving hierarchies
cats = cats.filter(cat => {
return Array.isArray(cat) || (cat != null && cat !== '');
Expand Down
6 changes: 5 additions & 1 deletion lib/models/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ module.exports = ctx => {
});

Tag.virtual('length').get(function() {
return this.posts.length;
// Note: this.posts.length is also working
// But it's slow because `find` has to iterate over all posts
const PostTag = ctx.model('PostTag');

return PostTag.find({tag_id: this._id}).length;
});

// Check whether a tag exists
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/helper/list_categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function listCategoriesHelper(categories, options) {
query.parent = {$exists: false};
}

return categories.find(query).sort(orderby, order).filter(cat => cat.length);
return categories.find(query).sort(orderby, order);
};

const hierarchicalList = (level, parent) => {
Expand Down
3 changes: 0 additions & 3 deletions lib/plugins/helper/list_tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ function listTagsHelper(tags, options) {
// Sort the tags
tags = tags.sort(orderby, order);

// Ignore tags with zero posts
tags = tags.filter(tag => tag.length);

// Limit the number of tags
if (options.amount) tags = tags.limit(options.amount);

Expand Down
4 changes: 4 additions & 0 deletions test/scripts/models/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ describe('Category', () => {

// draft on
hexo.config.render_drafts = true;

await Promise.all(posts.map(post => post.setCategories(['foo'])));
hexo.locals.invalidate();
cat = Category.findOne({name: 'foo'});
cat.posts.map(mapper).should.eql(posts.map(mapper));
Expand Down Expand Up @@ -227,6 +229,8 @@ describe('Category', () => {

// future off
hexo.config.future = false;

await Promise.all(posts.map(post => post.setCategories(['foo'])));
hexo.locals.invalidate();
cat = Category.findOne({name: 'foo'});
cat.posts.eq(0)._id.should.eql(posts[0]._id);
Expand Down
2 changes: 2 additions & 0 deletions test/scripts/models/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ describe('Tag', () => {

// draft on
hexo.config.render_drafts = true;
await Promise.all(posts.map(post => post.setTags(['foo'])));
tag = Tag.findOne({name: 'foo'});
hexo.locals.invalidate();
tag.posts.map(mapper).should.eql(posts.map(mapper));
Expand Down Expand Up @@ -206,6 +207,7 @@ describe('Tag', () => {

// future off
hexo.config.future = false;
await Promise.all(posts.map(post => post.setTags(['foo'])));
hexo.locals.invalidate();
tag = Tag.findOne({name: 'foo'});
tag.posts.eq(0)._id.should.eql(posts[0]._id);
Expand Down

0 comments on commit 47650c3

Please sign in to comment.