const advancedResults = (model, populate) => async (req, res, next) => { if (req.originalUrl.indexOf('admin') === -1) req.query = { ...req.query, $and: [{accessedBy: {$elemMatch: {user: req.user.id}}}], } if (req.query.search === undefined) req.query.search = '' if (req.query.search) req.query = {...req.query, $text: {$search: req.query.search}} const filterAndInGame = ['genre', 'os', 'developer', 'publisher', 'series', 'intel', 'wine', 'controller', 'store', 'soundtrack', 'playStatus', 'rating', 'ratinggte', 'ratinglte', 'steamRating', 'steamRatinggte', 'steamRatinglte'] filterAndInGame.map((filter) => { if (req.query[filter] === undefined) req.query[filter] = '' if (req.query[filter]) { let newFilter if (filter === 'os') { newFilter = req.query[filter].split(',').reduce((prev, curr, index, arr) => { const key = `${[filter]}.${arr[index]}`.toLowerCase() prev.push({[key]: true}) return prev }, []) } else if (filter === 'store' || filter === 'soundtrack' || filter === 'playStatus') { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {[filter]: curr}}}) return prev }, []) } else if (filter === 'rating') { if (Object.hasOwn(req.query[filter], 'gte')) { newFilter = req.query[filter]['gte'].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {rating: {$gte: Number(curr)}}}}) return prev }, []) req.query.rating = `gte${req.query[filter]['gte']}` } else if (Object.hasOwn(req.query[filter], 'lte')) { newFilter = req.query[filter]['lte'].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {rating: {$lte: Number(curr)}}}}) return prev }, []) req.query.rating = `lte${req.query[filter]['lte']}` } else { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {rating: Number(curr)}}}) return prev }, []) } } else if (filter === 'ratinggte') { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {rating: {$gte: Number(curr)}}}}) return prev }, []) req.query.rating = req.query.ratinggte } else if (filter === 'ratinglte') { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({accessedBy: {$elemMatch: {rating: {$lte: Number(curr)}}}}) return prev }, []) req.query.rating = req.query.ratinglte } else if (filter === 'steamRating') { if (Object.hasOwn(req.query[filter], 'gte')) { newFilter = req.query[filter]['gte'].split(',').reduce((prev, curr) => { prev.push({steamRating: {$gte: Number(curr)}}) return prev }, []) req.query.steamRating = `gte${req.query[filter]['gte']}` } else if (Object.hasOwn(req.query[filter], 'lte')) { newFilter = req.query[filter]['lte'].split(',').reduce((prev, curr) => { prev.push({steamRating: {$lte: Number(curr)}}) return prev }, []) req.query.steamRating = `lte${req.query[filter]['lte']}` } else { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({steamRating: Number(curr)}) return prev }, []) } } else if (filter === 'steamRatinggte') { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({steamRating: {$gte: Number(curr)}}) return prev }, []) req.query.steamRating = req.query.steamRatinggte } else if (filter === 'steamRatinglte') { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({steamRating: {$lte: Number(curr)}}) return prev }, []) req.query.steamRating = req.query.steamRatinglte } else { newFilter = req.query[filter].split(',').reduce((prev, curr) => { prev.push({[filter]: curr}) return prev }, []) } newFilter.forEach(element => req.query.$and.push(element)) } }) let query // Copy req.query const reqQuery = {...req.query} // Fields to exclude const removeFields = [ 'select', 'series', 'developer', 'publisher', 'sort', 'limit', 'page', 'search', 'os', 'playStatus', 'store', 'soundtrack', 'genre', 'controller', 'rating', 'ratinggte', 'ratinglte', 'steamRating', 'steamRatinggte', 'steamRatinglte', ] // Loop over removeFields and delete them from reqQuery removeFields.forEach((param) => delete reqQuery[param]) filterAndInGame.forEach((param) => { if (req.query[param] === '') delete reqQuery[param] }) // Create query String let queryStr = JSON.stringify(reqQuery) // Create operators like gt, gte, etc... queryStr = queryStr.replace( /\b(gt|gte|lt|lte|in|all)\b/g, (match) => `$${match}`, ) queryStr = queryStr.replace(/\$\$/g, '$') const total = await model.countDocuments(reqQuery) // Finding resource query = model.find(JSON.parse(queryStr)) // Select Fields if (req.query.select) { const fields = req.query.select.split(',').join(' ') query = query.select(fields) } // Sort if (req.query.sort) { const sortBy = req.query.sort.split(',').join(' ') query = query.sort(sortBy) } else { query = query.sort('series title') } // Pagination let page = parseInt(req.query.page, 10) || 1 const limit = parseInt(req.query.limit, 10) || 10 // This block figures out how many total pages there will be and if // the client is calling a page larger than the total pages let pages if (limit === 1) pages = total else if (total === 0) pages = 1 else if (total % limit === 0) pages = (total - (total % limit)) / limit else pages = (total - (total % limit)) / limit + 1 if (page > pages) page = pages const startIndex = (page - 1) * limit const endIndex = page * limit query = query.skip(startIndex).limit(limit) if (populate) { query = query.populate(populate) } // Executing query const results = await query // Pagination result const pagination = {} if (endIndex < total) { pagination.next = { page: page + 1, } } if (startIndex > 0) { pagination.prev = { page: page - 1, } } res.advancedResults = { success: true, searchParams: req.query.search, count: { gamesPerPage: results.length, totalGames: total, currentPage: page, pages, limit, }, filters: { series: req.query.series, playStatus: req.query.playStatus, genre: req.query.genre, os: req.query.os, store: req.query.store, developer: req.query.developer, controller: req.query.controller, publisher: req.query.publisher, soundtrack: req.query.soundtrack, intel: req.query.intel, wine: req.query.wine, rating: req.query.rating, steamRating: req.query.steamRating, }, pagination, data: results, } next() } export default advancedResults