Skip to content

Commit

Permalink
feat: ability to search categories, #8813
Browse files Browse the repository at this point in the history
  • Loading branch information
barisusakli committed Jan 28, 2021
1 parent faeb637 commit 34c42c6
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 4 deletions.
9 changes: 7 additions & 2 deletions src/categories/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = function (Categories) {
const parentCid = data.parentCid ? data.parentCid : 0;
const cid = await db.incrObjectField('global', 'nextCid');

data.name = data.name || 'Category ' + cid;
data.name = String(data.name || 'Category ' + cid);
const slug = cid + '/' + slugify(data.name);
const order = data.order || cid; // If no order provided, place it at the end
const colours = Categories.assignColours();
Expand Down Expand Up @@ -53,7 +53,12 @@ module.exports = function (Categories) {
if (!category.descriptionParsed) {
await Categories.parseDescription(category.cid, category.description);
}
await db.sortedSetsAdd(['categories:cid', 'cid:' + parentCid + ':children'], category.order, category.cid);

await db.sortedSetAddBulk([
['categories:cid', category.order, category.cid],
['cid:' + parentCid + ':children', category.order, category.cid],
['categories:name', 0, data.name.substr(0, 200).toLowerCase() + ':' + category.cid],
]);

const defaultPrivileges = [
'groups:find',
Expand Down
1 change: 1 addition & 0 deletions src/categories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require('./activeusers')(Categories);
require('./recentreplies')(Categories);
require('./update')(Categories);
require('./watch')(Categories);
require('./search')(Categories);

Categories.exists = async function (cid) {
if (Array.isArray(cid)) {
Expand Down
71 changes: 71 additions & 0 deletions src/categories/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

const _ = require('lodash');

const privileges = require('../privileges');
const plugins = require('../plugins');
const db = require('../database');

module.exports = function (Categories) {
Categories.search = async function (data) {
const query = data.query || '';
const page = data.page || 1;
const uid = data.uid || 0;
const paginate = data.hasOwnProperty('paginate') ? data.paginate : true;

const startTime = process.hrtime();

let cids = await findCids(query, data.hardCap);

const result = await plugins.hooks.fire('filter:categories.search', {
cids: cids,
uid: uid,
});
cids = await privileges.categories.filterCids('find', result.cids, uid);

const searchResult = {
matchCount: cids.length,
};

if (paginate) {
const resultsPerPage = data.resultsPerPage || 50;
const start = Math.max(0, page - 1) * resultsPerPage;
const stop = start + resultsPerPage;
searchResult.pageCount = Math.ceil(cids.length / resultsPerPage);
cids = cids.slice(start, stop);
}

const childrenCids = await getChildrenCids(cids, uid);
const uniqCids = _.uniq(cids.concat(childrenCids));
const categoryData = await Categories.getCategories(uniqCids, uid);

Categories.getTree(categoryData, 0);
await Categories.getRecentTopicReplies(categoryData, uid, data.qs);
categoryData.sort(function (c1, c2) {
if (c1.parentCid !== c2.parentCid) {
return c1.parentCid - c2.parentCid;
}
return c1.order - c2.order;
});
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
searchResult.categories = categoryData.filter(c => cids.includes(c.cid));
return searchResult;
};

async function findCids(query, hardCap) {
if (!query || String(query).length < 2) {
return [];
}
const data = await db.getSortedSetScan({
key: 'categories:name',
match: '*' + String(query).toLowerCase() + '*',
limit: hardCap || 500,
});
return data.map(data => parseInt(data.split(':').pop(), 10));
}

async function getChildrenCids(cids, uid) {
const childrenCids = await Promise.all(cids.map(cid => Categories.getChildrenCids(cid)));
return await privileges.categories.filterCids('find', childrenCids.flat(), uid);
}
};
8 changes: 6 additions & 2 deletions src/controllers/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ searchController.search = async function (req, res, next) {
req.query.in = req.query.in || 'posts';
const allowed = (req.query.in === 'users' && userPrivileges['search:users']) ||
(req.query.in === 'tags' && userPrivileges['search:tags']) ||
(req.query.in === 'categories') ||
(['titles', 'titlesposts', 'posts'].includes(req.query.in) && userPrivileges['search:content']);

if (!allowed) {
Expand Down Expand Up @@ -77,8 +78,11 @@ searchController.search = async function (req, res, next) {
return res.json(searchData);
}

searchData.categories = categoriesData;
searchData.categoriesCount = Math.max(10, Math.min(20, categoriesData.length));
if (['titles', 'titlesposts', 'posts'].includes(req.query.in)) {
searchData.categories = categoriesData;
searchData.categoriesCount = Math.max(10, Math.min(20, categoriesData.length));
}

searchData.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[global:search]]' }]);
searchData.expandSearch = !req.query.term;

Expand Down
2 changes: 2 additions & 0 deletions src/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ search.search = async function (data) {
result = await searchInContent(data);
} else if (data.searchIn === 'users') {
result = await user.search(data);
} else if (data.searchIn === 'categories') {
result = await categories.search(data);
} else if (data.searchIn === 'tags') {
result = await topics.searchAndLoadTags(data);
} else {
Expand Down
30 changes: 30 additions & 0 deletions src/upgrades/1.17.0/category_name_zset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

const db = require('../../database');
const batch = require('../../batch');

module.exports = {
name: 'Create category name sorted set',
timestamp: Date.UTC(2021, 0, 27),
method: async function () {
const progress = this.progress;

await batch.processSortedSet('categories:cid', async function (cids) {
const keys = cids.map(cid => 'category:' + cid);
let categoryData = await db.getObjectsFields(keys, ['cid', 'name']);
categoryData = categoryData.filter(c => c.cid && c.name);
const bulkAdd = categoryData.map(function (cat) {
return [
'categories:name',
0,
String(cat.name).substr(0, 200).toLowerCase() + ':' + cat.cid,
];
});
await db.sortedSetAddBulk(bulkAdd);
progress.incr(cids.length);
}, {
batch: 500,
progress: progress,
});
},
};

0 comments on commit 34c42c6

Please sign in to comment.