games-express-api/middleware/advancedResults.js

241 lines
8.4 KiB
JavaScript

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