cubash-archive/routes/search.js

189 lines
5.5 KiB
JavaScript

const { Op } = require("sequelize");
let express = require('express')
let router = express.Router()
const auth = require('../lib/auth')
let { Post, Ban, Team, Thread, User, Category, Sequelize } = require('../models')
const Errors = require('../lib/errors')
const { setRandomFallback } = require('bcryptjs')
router.get('/thread', async(req, res, next) => {
try {
let searchString = req.query.q.trim()
if(searchString.length < 2) {
throw Errors.sequelizeValidation(Sequelize, {
error: 'search string must be at least 2 characters',
value: searchString
})
}
//Offset is really the previously lowest id
//(as a proxy for oldest thread of the previous search)
//if there is no offset, just use a clause that will always be true
//i.e. greater than 0 id
let offset = +req.query.offset ? { $lt: +req.query.offset } : { $gt: 0 }
let limit = 10
/*
Task is to find threads that either have the
string in the title or in the content of the first post
Method
1) Select first n items from each group (posts and threads), where n is the LIMIT,
greater than id x, where x is previous OFFSET
2) Merge results from both, remove duplicates and sort
3) Select first n items from merged group
4) Set x as the last item from merged group
*/
let threadTitles = await Thread.findAll({
where: {
name: { [Op.like]: '%' + searchString + '%' },
id: offset
},
order: [ ['id', 'DESC'] ],
include: [
{
model: Post,
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']} }],
where: {
postNumber: 0
}
},
{ model: Category },
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }
],
limit
})
let threadPosts = await Thread.findAll({
where: {
id: offset
},
order: [ ['id', 'DESC'] ],
include: [
{
model: Post,
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }],
where: {
postNumber: 0,
plainText: { [Op.like]: '%' + searchString + '%' }
}
},
{ model: Category },
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }
],
limit
})
let merged = [...threadTitles, ...threadPosts];
let unique = [];
merged.forEach(thread => {
let includes = unique.filter(u => thread.id === u.id);
if(!includes.length) unique.push(thread);
});
let sorted = unique
.sort((a, b) => {
return b.id - a.id;
})
.slice(0, limit);
//To get latest post, find threads where
//the post number is equal to the overal posts count
//and the post number > 0 (i.e. there are replies)
let whereClause = sorted.reduce((arr, thread) => {
if(thread.postsCount > 1) {
let clause = {
$and: {
ThreadId: thread.id,
postNumber: thread.postsCount-1
}
}
return [...arr, clause];
} else {
return arr;
}
}, []);
let latestPosts = await Post.findAll({
where: {
[Op.or]: whereClause
},
order: [ ['ThreadId', 'DESC'] ],
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }]
})
//Merge latest posts with threads array
let ret = sorted.map(thread => {
if(thread.postsCount > 1) {
let post = latestPosts.filter(p => p.ThreadId === thread.id)[0];
thread.Posts.push(post);
}
return thread;
})
res.json({
threads: ret,
offset: ret.length ? ret.slice(-1)[0].id : null,
next: ret.length < limit ? null : limit
})
} catch (e) { next(e) }
})
router.get('/user', async(req, res, next) => {
try {
let searchString = req.query.q
let offset = +req.query.offset || 0
let limit = 10
let users = await User.findAll({
where: {
username: { [Op.like]: '%' + searchString + '%' }
},
order: [ ['username', 'DESC'] ],
attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']},
limit,
offset
})
res.json({
users,
offset: users.length < limit ? null : offset + limit,
next: users.length < limit ? null : limit
})
} catch (e) { next(e) }
})
router.get('/team', async(req, res, next) => {
try {
let searchString = req.query.q
let offset = +req.query.offset || 0
let limit = 10
let teams = await Team.findAll({
where: {
username: { [Op.like]: '%' + searchString + '%' }
},
order: [ ['username', 'DESC'] ],
attributes: {exclude: ['banReason']},
limit,
offset
})
res.json({
teams,
offset: teams.length < limit ? null : offset + limit,
next: teams.length < limit ? null : limit
})
} catch (e) { next(e) }
})
module.exports = router