diff --git a/public/language/en-GB/admin/dashboard.json b/public/language/en-GB/admin/dashboard.json index 4d39626882..ba945e281a 100644 --- a/public/language/en-GB/admin/dashboard.json +++ b/public/language/en-GB/admin/dashboard.json @@ -83,8 +83,11 @@ "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", - "details.no-searches": "No searches have been made yet", + "details.no-searches": "No searches have been made within the selected timeframe", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", - "details.logins-login-time": "Login Time" + "details.logins-login-time": "Login Time", + "start": "Start", + "end": "End", + "filter": "Filter" } diff --git a/src/controllers/admin/dashboard.js b/src/controllers/admin/dashboard.js index ac9f4fc130..09be9fb0ad 100644 --- a/src/controllers/admin/dashboard.js +++ b/src/controllers/admin/dashboard.js @@ -337,8 +337,54 @@ dashboardController.getTopics = async (req, res) => { }; dashboardController.getSearches = async (req, res) => { - const searches = await db.getSortedSetRevRangeWithScores('searches:all', 0, 99); + let start = 0; + let end = 0; + if (req.query.start) { + start = new Date(req.query.start); + start.setHours(24, 0, 0, 0); + end = new Date(); + end.setHours(24, 0, 0, 0); + } + if (req.query.end) { + end = new Date(req.query.end); + end.setHours(24, 0, 0, 0); + } + + let searches; + if (start && end && start <= end) { + const daysArr = [start]; + const nextDay = new Date(start.getTime()); + while (nextDay < end) { + nextDay.setDate(nextDay.getDate() + 1); + nextDay.setHours(0, 0, 0, 0); + daysArr.push(new Date(nextDay.getTime())); + } + + const daysData = await Promise.all( + daysArr.map(async d => db.getSortedSetRevRangeWithScores(`searches:${d.getTime()}`, 0, -1)) + ); + + const map = {}; + daysData.forEach((d) => { + d.forEach((search) => { + if (!map[search.value]) { + map[search.value] = search.score; + } else { + map[search.value] += search.score; + } + }); + }); + + searches = Object.keys(map) + .map(key => ({ value: key, score: map[key] })) + .sort((a, b) => b.score - a.score); + } else { + searches = await db.getSortedSetRevRangeWithScores('searches:all', 0, 99); + } + res.render('admin/dashboard/searches', { searches: searches.map(s => ({ value: validator.escape(String(s.value)), score: s.score })), + startDate: validator.escape(String(req.query.start)), + endDate: validator.escape(String(req.query.end)), }); }; diff --git a/src/controllers/search.js b/src/controllers/search.js index 238c167709..013db8457f 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -2,6 +2,7 @@ 'use strict'; const validator = require('validator'); +const _ = require('lodash'); const db = require('../database'); const meta = require('../meta'); @@ -120,7 +121,12 @@ async function recordSearch(data) { q => !copy.find(query => query.startsWith(q) && query.length > q.length) ); delete searches[data.uid]; - await Promise.all(filtered.map(query => db.sortedSetIncrBy('searches:all', 1, query))); + const dayTimestamp = (new Date()); + dayTimestamp.setHours(0, 0, 0, 0); + await Promise.all(_.uniq(filtered).map(async (query) => { + await db.sortedSetIncrBy('searches:all', 1, query); + await db.sortedSetIncrBy(`searches:${dayTimestamp.getTime()}`, 1, query); + })); } }, 5000); } diff --git a/src/views/admin/dashboard/searches.tpl b/src/views/admin/dashboard/searches.tpl index baefcea306..2737f0126d 100644 --- a/src/views/admin/dashboard/searches.tpl +++ b/src/views/admin/dashboard/searches.tpl @@ -1,10 +1,23 @@
- - - [[admin/dashboard:back-to-dashboard]] - - +
+ + + [[admin/dashboard:back-to-dashboard]] + +
+
+ + +
+
+ + +
+ +
+
+