Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: reduce the number of traversals through posts #5119

Merged
merged 1 commit into from Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 2 additions & 8 deletions lib/hexo/index.js
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
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
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
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
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
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
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
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