diff --git a/app.js b/app.js index 312454b..c70eb12 100644 --- a/app.js +++ b/app.js @@ -43,6 +43,27 @@ function convertURIregex(input) { return input; } +function getComparisonText(comparison) { + switch (comparison) { + case "<": + return "$lt"; + case "<=": + return "$lte"; + case ">": + return "$gt"; + case ">=": + return "$gte"; + default: + return "$eq"; + } +} + +function getComparisonObject(comparison, value) { + const comparisonString = getComparisonText(comparison); + + return Object.defineProperty({}, comparisonString, {value, writable: true, configurable: true, enumerable: true}); +} + top_pp_list = []; clientdb.connect( function(err, db) { @@ -107,18 +128,90 @@ function makeBoard() { }); app.get('/whitelist', (req, res) => { - var page = parseInt(req.url.split('?page=')[1]); - var query = req.url.split('?query=')[1] - var mapquery; - if (!page) {page = 1;} - if (!query) {mapquery = {}; query = '';} - else { - var regexquery = new RegExp(convertURIregex(query), 'i'); - mapquery = {mapname: regexquery}; + const page = parseInt(req.url.split('?page=')[1]) || 1; + const query = convertURI(req.url.split('?query=')[1] || "").toLowerCase(); + const mapquery = {}; + const sort = {}; + if (query) { + let mapNameQuery = ""; + const comparisonRegex = /[<=>]{1,2}/; + const finalQueries = query.split(/\s+/g); + for (const finalQuery of finalQueries) { + let [key, value] = finalQuery.split(comparisonRegex, 2); + const comparison = (comparisonRegex.exec(finalQuery) ?? ["="])[0]; + switch (key) { + case "cs": + case "ar": + case "od": + case "hp": + case "sr": + case "bpm": + const propertyName = `diffstat.${key}`; + if (mapquery.hasOwnProperty(propertyName)) { + mapquery[propertyName][getComparisonText(comparison)] = parseFloat(value); + } else { + mapquery[propertyName] = getComparisonObject(comparison, parseFloat(value)); + } + break; + case "star": + case "stars": + if (mapquery.hasOwnProperty("diffstat.sr")) { + mapquery["diffstat.sr"][getComparisonText(comparison)] = parseFloat(value); + } else { + mapquery["diffstat.sr"] = getComparisonObject(comparison, parseFloat(value)); + } + break; + case "sort": + const isDescendSort = value.startsWith("-"); + if (isDescendSort) { + value = value.substring(1); + } + switch (value) { + case "beatmapid": + case "mapid": + case "id": + sort.mapid = isDescendSort ? -1 : 1; + break; + case "beatmapname": + case "mapname": + case "name": + sort.mapname = isDescendSort ? -1 : 1; + break; + case "cs": + case "ar": + case "od": + case "hp": + case "bpm": + sort[`diffstat.${value}`] = isDescendSort ? -1 : 1; + break; + case "sr": + case "star": + case "stars": + sort["diffstat.sr"] = isDescendSort ? -1 : 1; + break; + default: + mapNameQuery += finalQuery + " "; + } + break; + default: + mapNameQuery += finalQuery + " "; + } + } + if (mapNameQuery) { + const regexQuery = mapNameQuery.trim().split(/\s+/g).map(v => { + return {mapname: new RegExp(convertURIregex(v), "i")}; + }); + mapquery.$and = regexQuery; + } + } + if (!sort.hasOwnProperty("mapname")) { + sort.mapname = 1; + } + // Allow SR and BPM sort to override beatmap title sort + if (sort.hasOwnProperty("diffstat.sr") || sort.hasOwnProperty("diffstat.bpm")) { + delete sort.mapname; } - var mapsort = { mapname: 1 }; - whitelistdb.find(mapquery, {projection: {_id: 0}}).sort(mapsort).skip((page-1)*30).limit(30).toArray(function(err, resarr) { - //console.log(resarr); + whitelistdb.find(mapquery, {projection: {_id: 0, mapid: 1, mapname: 1, diffstat: 1}}).sort(sort).skip((page-1)*30).limit(30).toArray(function(err, resarr) { var title = 'Map Whitelisting Board' res.render('whitelist', { title: title, diff --git a/views/default.css b/views/default.css index 64ac604..e2a5b60 100644 --- a/views/default.css +++ b/views/default.css @@ -27,3 +27,8 @@ h5 { font: 16px Exo; font-weight: 350; } + +h7 { + font: 14px Exo; + font-weight: 150; +} \ No newline at end of file diff --git a/views/whitelist.pug b/views/whitelist.pug index 02b2d4a..6c2cb78 100644 --- a/views/whitelist.pug +++ b/views/whitelist.pug @@ -23,7 +23,7 @@ h3 div(class="queryall") h5(class="queryshow")= "Current query: " + query form - input(type="text", name="query", id='rcorners', hint="Search") + input(type="text", name="query", value=query, id='rcorners', hint="Search") input(type="submit", id='rcorners2', value="Search") hr @@ -41,13 +41,25 @@ hr table(border="0", width="100%") tbody - th(width="10%")= "Map ID" - th(width="80%")= "Map Name" + th(width="7.5%")= "Map ID" + th(width="67.5%")= "Map Name" + th(width="2.5%")= "CS" + th(width="2.5%")= "AR" + th(width="2.5%")= "OD" + th(width="2.5%")= "HP" + th(width="2.5%")= "SR" + th(width="2.5%")= "BPM" th(width="10%")= "osu! redirect" each val in list tr td= val.mapid td= val.mapname + td= val.diffstat.cs + td= val.diffstat.ar + td= val.diffstat.od + td= val.diffstat.hp + td= val.diffstat.sr + td= val.diffstat.bpm td a(href='https://osu.ppy.sh/b/'+val.mapid)= "Link" @@ -62,4 +74,54 @@ div li(class="nav") a(href='/whitelist?page=' + (page+1) + (query ? '?query='+query : ''), class="pagenav")= "Next Page >>>" +hr + +div + h3 Search Query Guide + h7 + strong Available filter option: + | CS, AR, OD, HP, SR, BPM + br + strong Available sorting option: + | CS, AR, OD, HP, SR, BPM, mapid, mapname (you can put "-" in front to use descend sort instead of ascend) + br + br + strong Equality symbols for filter option: + | + ul + li <= (less than or equal to) + li < (less than) + li = (equal to) + li > (more than) + li >= (more than or equal to) + | All filter and sort options are case insensitive, with each of them separated by space. + br + br + | By default, there is no filter, and the sort option is set to beatmap name. + br + br + | Using SR and/or BPM sort option will override beatmap name sort option. + br + br + | Anything that doesn't fall into any of the filter or sort option will be treated as searching beatmap name. + br + br + strong Examples: + ul + li + strong: i cs>=4.2 cs<=5 sort=cs + | will search for beatmaps with CS between 4.2 (inclusive) and 5 (inclusive) and sort them based on CS ascendingly + li + strong: i od>=5 od<9 ar>7 ar<=9.7 sort=-ar + | will search for beatmaps with AR between 7 (exclusive) and 9.7 (inclusive) and OD between 5 (inclusive) and 9 (exclusive), and then + | sorting the search result by AR descendingly + li + strong: i od>=7 bpm>=180 + | will search for beatmaps with OD above 7 (inclusive) and BPM above 180 (inclusive) and sort them based on BPM ascendingly + li + strong: i cs>=4.2 ar>9.3 od>=8 hp>=5 sort=-sr logic boi is the best + | will search for beatmaps with CS above 4.2 (inclusive), AR above 9.3 (exclusive), OD above 8 (inclusive), + | HP above 5 (inclusive), and matches the keyword "logic boi is the best" (much like osu! search function), + | and then sorting the search result by star rating descendingly +hr \ No newline at end of file