Skip to content

Commit 47650c3

Browse files
perf: reduce the number of traversals through posts (#5119)
1 parent ca51e15 commit 47650c3

File tree

8 files changed

+30
-14
lines changed

8 files changed

+30
-14
lines changed

lib/hexo/index.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,9 @@ class Hexo extends EventEmitter {
200200
return db.model('Page').find(query);
201201
});
202202

203-
locals.set('categories', () => {
204-
// Ignore categories with zero posts
205-
return db.model('Category').filter(category => category.length);
206-
});
203+
locals.set('categories', () => db.model('Category'));
207204

208-
locals.set('tags', () => {
209-
// Ignore tags with zero posts
210-
return db.model('Tag').filter(tag => tag.length);
211-
});
205+
locals.set('tags', () => db.model('Tag'));
212206

213207
locals.set('data', () => {
214208
const obj = {};

lib/models/category.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ module.exports = ctx => {
5252
});
5353

5454
Category.virtual('length').get(function() {
55-
return this.posts.length;
55+
const PostCategory = ctx.model('PostCategory');
56+
57+
return PostCategory.find({category_id: this._id}).length;
5658
});
5759

5860
// Check whether a category exists

lib/models/post.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,17 @@ module.exports = ctx => {
7171
return Tag.find({_id: {$in: ids}});
7272
});
7373

74+
Post.method('notPublished', function() {
75+
// The same condition as ctx._bindLocals
76+
return (!ctx.config.future && this.date > Date.now()) || (!ctx._showDrafts() && this.published === false);
77+
});
78+
7479
Post.method('setTags', function(tags) {
80+
if (this.notPublished()) {
81+
// Ignore tags of draft posts
82+
// If the post is unpublished then the tag needs to be removed, thus the function cannot be returned early here
83+
tags = [];
84+
}
7585
tags = removeEmptyTag(tags);
7686

7787
const PostTag = ctx.model('PostTag');
@@ -119,6 +129,9 @@ module.exports = ctx => {
119129
});
120130

121131
Post.method('setCategories', function(cats) {
132+
if (this.notPublished()) {
133+
cats = [];
134+
}
122135
// Remove empty categories, preserving hierarchies
123136
cats = cats.filter(cat => {
124137
return Array.isArray(cat) || (cat != null && cat !== '');

lib/models/tag.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ module.exports = ctx => {
4343
});
4444

4545
Tag.virtual('length').get(function() {
46-
return this.posts.length;
46+
// Note: this.posts.length is also working
47+
// But it's slow because `find` has to iterate over all posts
48+
const PostTag = ctx.model('PostTag');
49+
50+
return PostTag.find({tag_id: this._id}).length;
4751
});
4852

4953
// Check whether a tag exists

lib/plugins/helper/list_categories.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function listCategoriesHelper(categories, options) {
2929
query.parent = {$exists: false};
3030
}
3131

32-
return categories.find(query).sort(orderby, order).filter(cat => cat.length);
32+
return categories.find(query).sort(orderby, order);
3333
};
3434

3535
const hierarchicalList = (level, parent) => {

lib/plugins/helper/list_tags.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ function listTagsHelper(tags, options) {
4747
// Sort the tags
4848
tags = tags.sort(orderby, order);
4949

50-
// Ignore tags with zero posts
51-
tags = tags.filter(tag => tag.length);
52-
5350
// Limit the number of tags
5451
if (options.amount) tags = tags.limit(options.amount);
5552

test/scripts/models/category.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ describe('Category', () => {
191191

192192
// draft on
193193
hexo.config.render_drafts = true;
194+
195+
await Promise.all(posts.map(post => post.setCategories(['foo'])));
194196
hexo.locals.invalidate();
195197
cat = Category.findOne({name: 'foo'});
196198
cat.posts.map(mapper).should.eql(posts.map(mapper));
@@ -227,6 +229,8 @@ describe('Category', () => {
227229

228230
// future off
229231
hexo.config.future = false;
232+
233+
await Promise.all(posts.map(post => post.setCategories(['foo'])));
230234
hexo.locals.invalidate();
231235
cat = Category.findOne({name: 'foo'});
232236
cat.posts.eq(0)._id.should.eql(posts[0]._id);

test/scripts/models/tag.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ describe('Tag', () => {
172172

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

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

0 commit comments

Comments
 (0)