Files
NodeBB/src/user/search.js

172 lines
4.6 KiB
JavaScript
Raw Normal View History

2014-03-12 22:11:48 -04:00
'use strict';
const _ = require('lodash');
2019-07-16 20:44:00 -04:00
const meta = require('../meta');
const plugins = require('../plugins');
const db = require('../database');
const groups = require('../groups');
const utils = require('../utils');
2014-03-12 22:11:48 -04:00
module.exports = function (User) {
const filterFnMap = {
online: user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000),
flagged: user => parseInt(user.flags, 10) > 0,
verified: user => !!user['email:confirmed'],
unverified: user => !user['email:confirmed'],
};
const filterFieldMap = {
online: ['status', 'lastonline'],
flagged: ['flags'],
verified: ['email:confirmed'],
unverified: ['email:confirmed'],
};
2019-07-16 20:44:00 -04:00
User.search = async function (data) {
const query = data.query || '';
const searchBy = data.searchBy || 'username';
const page = data.page || 1;
const uid = data.uid || 0;
const paginate = data.hasOwnProperty('paginate') ? data.paginate : true;
const startTime = process.hrtime();
let uids = [];
if (searchBy === 'ip') {
uids = await searchByIP(query);
} else if (searchBy === 'uid') {
uids = [query];
} else {
const searchMethod = data.findUids || findUids;
uids = await searchMethod(query, searchBy, data.hardCap);
}
uids = await filterAndSortUids(uids, data);
const result = await plugins.hooks.fire('filter:users.search', { uids: uids, uid: uid });
2019-07-16 20:44:00 -04:00
uids = result.uids;
const searchResult = {
matchCount: uids.length,
};
2015-01-12 15:57:45 -05:00
2019-07-16 20:44:00 -04:00
if (paginate) {
const resultsPerPage = data.resultsPerPage || meta.config.userSearchResultsPerPage;
const start = Math.max(0, page - 1) * resultsPerPage;
const stop = start + resultsPerPage;
2019-07-16 20:44:00 -04:00
searchResult.pageCount = Math.ceil(uids.length / resultsPerPage);
uids = uids.slice(start, stop);
}
const [userData, blocks] = await Promise.all([
User.getUsers(uids, uid),
User.blocks.list(uid),
]);
if (blocks.length) {
userData.forEach((user) => {
if (user) {
user.isBlocked = blocks.includes(user.uid);
}
});
}
2019-07-16 20:44:00 -04:00
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
searchResult.users = userData.filter(user => user && user.uid > 0);
2019-07-16 20:44:00 -04:00
return searchResult;
2015-01-12 15:57:45 -05:00
};
2015-03-10 14:09:24 -04:00
2019-07-16 20:44:00 -04:00
async function findUids(query, searchBy, hardCap) {
if (!query) {
2019-07-16 20:44:00 -04:00
return [];
}
2019-07-16 20:44:00 -04:00
query = String(query).toLowerCase();
const min = query;
const max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1);
2019-07-16 20:44:00 -04:00
const resultsPerPage = meta.config.userSearchResultsPerPage;
2017-07-28 00:55:02 -04:00
hardCap = hardCap || resultsPerPage * 10;
2021-02-03 23:59:08 -07:00
const data = await db.getSortedSetRangeByLex(`${searchBy}:sorted`, min, max, 0, hardCap);
const uids = data.map(data => data.split(':').pop());
2019-07-16 20:44:00 -04:00
return uids;
2015-01-12 15:57:45 -05:00
}
2019-07-16 20:44:00 -04:00
async function filterAndSortUids(uids, data) {
2018-10-25 17:02:59 -04:00
uids = uids.filter(uid => parseInt(uid, 10));
let filters = data.filters || [];
filters = Array.isArray(filters) ? filters : [data.filters];
2019-07-16 20:44:00 -04:00
const fields = [];
2014-03-12 22:11:48 -04:00
2017-07-28 00:55:02 -04:00
if (data.sortBy) {
fields.push(data.sortBy);
}
2021-02-04 00:01:39 -07:00
filters.forEach((filter) => {
if (filterFieldMap[filter]) {
fields.push(...filterFieldMap[filter]);
}
});
2015-01-12 15:57:45 -05:00
if (data.groupName) {
const isMembers = await groups.isMembers(uids, data.groupName);
uids = uids.filter((uid, index) => isMembers[index]);
}
2017-07-28 00:55:02 -04:00
if (!fields.length) {
2019-07-16 20:44:00 -04:00
return uids;
2017-07-28 00:55:02 -04:00
}
2020-12-14 09:20:41 +03:00
if (filters.includes('banned') || filters.includes('notbanned')) {
const isMembersOfBanned = await groups.isMembers(uids, groups.BANNED_USERS);
const checkBanned = filters.includes('banned');
uids = uids.filter((uid, index) => (checkBanned ? isMembersOfBanned[index] : !isMembersOfBanned[index]));
}
2019-07-16 20:44:00 -04:00
fields.push('uid');
let userData = await User.getUsersFields(uids, fields);
2016-07-04 17:49:02 +03:00
2021-02-04 00:01:39 -07:00
filters.forEach((filter) => {
if (filterFnMap[filter]) {
userData = userData.filter(filterFnMap[filter]);
}
});
2015-01-12 15:57:45 -05:00
2019-07-16 20:44:00 -04:00
if (data.sortBy) {
sortUsers(userData, data.sortBy, data.sortDirection);
2019-07-16 20:44:00 -04:00
}
2015-01-12 15:57:45 -05:00
2019-07-16 20:44:00 -04:00
return userData.map(user => user.uid);
2015-01-12 15:57:45 -05:00
}
function sortUsers(userData, sortBy, sortDirection) {
if (!userData || !userData.length) {
return;
}
sortDirection = sortDirection || 'desc';
const direction = sortDirection === 'desc' ? 1 : -1;
const isNumeric = utils.isNumber(userData[0][sortBy]);
if (isNumeric) {
userData.sort((u1, u2) => direction * (u2[sortBy] - u1[sortBy]));
2015-01-28 19:19:55 -05:00
} else {
2021-02-04 00:01:39 -07:00
userData.sort((u1, u2) => {
if (u1[sortBy] < u2[sortBy]) {
return direction * -1;
} else if (u1[sortBy] > u2[sortBy]) {
return direction * 1;
2015-01-12 15:57:45 -05:00
}
return 0;
2015-01-28 19:19:55 -05:00
});
}
2015-01-12 15:57:45 -05:00
}
2014-12-02 12:38:53 -05:00
2019-07-16 20:44:00 -04:00
async function searchByIP(ip) {
2021-02-03 23:59:08 -07:00
const ipKeys = await db.scan({ match: `ip:${ip}*` });
const uids = await db.getSortedSetRevRange(ipKeys, 0, -1);
return _.uniq(uids);
2014-12-02 12:38:53 -05:00
}
2014-03-12 22:11:48 -04:00
};