search.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. let express = require('express')
  2. let router = express.Router()
  3. let { Post, Thread, User, Category, Sequelize } = require('../models')
  4. const Errors = require('../lib/errors')
  5. router.get('/thread', async (req, res, next) => {
  6. try {
  7. let searchString = req.query.q
  8. //Offset is really the previously lowest id
  9. //(as a proxy for oldest thread of the previous search)
  10. //if there is no offset, just use a clause that will always be true
  11. //i.e. greater than 0 id
  12. let offset = +req.query.offset ? { $lt: +req.query.offset } : { $gt: 0 }
  13. let limit = 10
  14. /*
  15. Task is to find threads that either have the
  16. string in the title or in the content of the first post
  17. Method
  18. 1) Select first n items from each group (posts and threads), where n is the LIMIT,
  19. greater than id x, where x is previous OFFSET
  20. 2) Merge results from both, remove duplicates and sort
  21. 3) Select first n items from merged group
  22. 4) Set x as the last item from merged group
  23. */
  24. let threadTitles = await Thread.findAll({
  25. where: {
  26. name: { $like: '%' + searchString + '%' },
  27. id: offset
  28. },
  29. order: [ ['id', 'DESC'] ],
  30. include: [
  31. {
  32. model: Post,
  33. include: [{ model: User, attributes: { exclude: ['hash'] } }],
  34. where: {
  35. postNumber: 0
  36. }
  37. },
  38. { model: Category },
  39. { model: User, attributes: { exclude: ['hash'] } }
  40. ],
  41. limit
  42. })
  43. let threadPosts = await Thread.findAll({
  44. where: {
  45. id: offset
  46. },
  47. order: [ ['id', 'DESC'] ],
  48. include: [
  49. {
  50. model: Post,
  51. include: [{ model: User, attributes: { exclude: ['hash'] } }],
  52. where: {
  53. postNumber: 0,
  54. content: { $like: '%' + searchString + '%' }
  55. }
  56. },
  57. { model: Category },
  58. { model: User, attributes: { exclude: ['hash'] } }
  59. ],
  60. limit
  61. })
  62. let merged = [...threadTitles, ...threadPosts];
  63. let unique = [];
  64. merged.forEach(thread => {
  65. let includes = unique.filter(u => thread.id === u.id);
  66. if(!includes.length) unique.push(thread);
  67. });
  68. let sorted = unique
  69. .sort((a, b) => {
  70. return b.id - a.id;
  71. })
  72. .slice(0, limit);
  73. //To get latest post, find threads where
  74. //the post number is equal to the overal posts count
  75. //and the post number > 0 (i.e. there are replies)
  76. let whereClause = sorted.reduce((arr, thread) => {
  77. console.log(thread.postsCount)
  78. if(thread.postsCount > 1) {
  79. let clause = {
  80. $and: {
  81. ThreadId: thread.id,
  82. postNumber: thread.postsCount-1
  83. }
  84. }
  85. return [...arr, clause];
  86. } else {
  87. return arr;
  88. }
  89. }, []);
  90. let latestPosts = await Post.findAll({
  91. where: {
  92. $or: whereClause
  93. },
  94. order: [ ['ThreadId', 'DESC'] ],
  95. include: [{ model: User, attributes: { exclude: ['hash'] } }]
  96. })
  97. //Merge latest posts with threads array
  98. let ret = sorted.map(thread => {
  99. if(thread.postsCount > 1) {
  100. let post = latestPosts.filter(p => p.ThreadId === thread.id)[0];
  101. thread.Posts.push(post);
  102. }
  103. return thread;
  104. })
  105. res.json({
  106. threads: ret,
  107. offset: ret.length ? ret.slice(-1)[0].id : null,
  108. next: ret.length < limit ? null : limit
  109. })
  110. } catch (e) { next(e) }
  111. })
  112. router.get('/user', async (req, res, next) => {
  113. try {
  114. let searchString = req.query.q
  115. let offset = +req.query.offset || 0
  116. let limit = 10
  117. let users = await User.findAll({
  118. where: {
  119. username: { $like: '%' + searchString + '%' }
  120. },
  121. order: [ ['username', 'DESC'] ],
  122. attributes: { exclude: ['hash'] },
  123. limit,
  124. offset
  125. })
  126. res.json({
  127. users,
  128. offset: users.length < limit ? null : offset + limit,
  129. next: users.length < limit ? null : limit
  130. })
  131. } catch (e) { next(e) }
  132. })
  133. module.exports = router