Node.js环境下如何按分数变化幅度对MongoDB数据排序?
问题:如何按分数变化幅度对MongoDB中的数据排序?
我编写了一段每日24小时从第三方API备份、删除并插入数据的代码,用来同步第三方API的每日变更。现在需要实现从MongoDB获取信息时,按分数变化幅度最大来排序。
API数据结构示例
{ "_id": "6365e1dbde0dd3639536f4b7", "position": 1, "id": "105162", "score": 2243536903, "__v": 0 },
当前查询代码
app.get('/api/TopFlops', async (req, res) => { const topflops = await TopFlops.find({}).sort({_id: +1}).limit(5) res.json(topflops); })
第三方API数据插入数据库代码
cron.schedule('59 23 * * *', async () => { const postSchema = new mongoose.Schema({ id: { type: Number, required: true }, name: { type: String, required: true }, status: { type: String, required: false }, }); const Post = mongoose.model('players', postSchema); async function getPosts() { const getPlayers = await fetch("http://localhost:3008/api/players"); const response = await getPlayers.json(); for( let i = 0;i < response.players.length; i++){ const post = new Post({ id: response.players[i]['id'], name: response.players[i]['name'], status: response.players[i]['status'], }); post.save(); } } console.log("Table submitted successfully") await getPosts(); });
前端API获取代码
const [playerName, setPlayerName] = useState([]); const [playerRank, setPlayerRank] = useState([]); const [player, setPlayer] = useState([]); const [perPage, setPerPage] = useState(10); const [size, setSize] = useState(perPage); const [current, setCurrent] = useState(1); const [players, setPlayers] = useState(); const fetchData = () => { const playerAPI = 'http://localhost:3001/api/topflops'; const playerRank = 'http://localhost:3001/api/topflops'; const getINFOPlayer = axios.get(playerAPI) const getPlayerRank = axios.get(playerRank) axios.all([getINFOPlayer, getPlayerRank]).then( axios.spread((...allData) => { const allDataPlayer = allData[0].data const getINFOPlayerRank = allData[1].data const newPlayer = allDataPlayer.map(name => { const pr = getINFOPlayerRank.find(rank => name.id === rank.id) return { id: name.id, name: name.name, alliance: name.alliance, position: pr?.position, score: pr?.score } }) setPlayerName(allDataPlayer) setPlayerRank(getINFOPlayerRank) console.log(getINFOPlayerRank) console.log(newPlayer) setPlayer(newPlayer) }) ) } useEffect(() => { fetchData() }, []) const getData = (current, pageSize) => { // Normally you should get the data from the server return player?.slice((current - 1) * pageSize, current * pageSize); };
解决方案
要实现按分数变化幅度排序,首先需要保留历史分数数据——当前的逻辑是直接删除旧数据插入新数据,丢失了计算变化所需的对比依据。以下是具体实现步骤:
1. 修改数据存储逻辑,保留历史分数
方案A:新增previousScore字段(仅对比前一天数据)
修改TopFlops的Schema,增加存储前一日分数的字段:
const topFlopsSchema = new mongoose.Schema({ id: { type: String, required: true }, position: Number, score: Number, previousScore: Number, // 存储前一天的分数 updatedAt: { type: Date, default: Date.now } }); const TopFlops = mongoose.model('TopFlops', topFlopsSchema);
每日同步时,先查询玩家现有记录,将当前分数赋值给previousScore,再更新新分数:
cron.schedule('59 23 * * *', async () => { const response = await fetch("http://localhost:3008/api/players"); const players = await response.json(); for (const player of players.players) { const existingPlayer = await TopFlops.findOne({ id: player.id }); if (existingPlayer) { await TopFlops.updateOne( { id: player.id }, { $set: { previousScore: existingPlayer.score, score: player.score, position: player.position, updatedAt: new Date() } } ); } else { await TopFlops.create({ id: player.id, name: player.name, position: player.position, score: player.score, previousScore: player.score // 首次插入无变化,默认等于当前分数 }); } } console.log("数据同步完成"); });
方案B:存储每日快照(支持查看所有历史变化)
创建单独的分数历史集合,每次同步插入当日快照:
const playerScoreHistorySchema = new mongoose.Schema({ playerId: { type: String, required: true }, score: Number, position: Number, date: { type: Date, default: Date.now } }); const PlayerScoreHistory = mongoose.model('PlayerScoreHistory', playerScoreHistorySchema); // 每日同步插入快照 cron.schedule('59 23 * * *', async () => { const response = await fetch("http://localhost:3008/api/players"); const players = await response.json(); const historyRecords = players.players.map(player => ({ playerId: player.id, score: player.score, position: player.position, date: new Date() })); await PlayerScoreHistory.insertMany(historyRecords); console.log("快照已保存"); });
2. 按分数变化幅度排序查询
基于方案A的查询逻辑
使用MongoDB聚合计算分数变化绝对值,再排序:
app.get('/api/TopFlops', async (req, res) => { const topflops = await TopFlops.aggregate([ // 计算分数变化幅度(绝对值) { $addFields: { scoreChange: { $abs: { $subtract: ["$score", "$previousScore"] } } } }, // 按变化幅度降序排序 { $sort: { scoreChange: -1 } }, // 取前5条数据 { $limit: 5 } ]); res.json(topflops); });
基于方案B的查询逻辑
先聚合获取玩家最近两天的分数,计算变化后排序:
app.get('/api/TopFlops', async (req, res) => { const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); const playerScores = await PlayerScoreHistory.aggregate([ // 筛选最近两天的记录 { $match: { date: { $gte: yesterday, $lte: today } } }, // 按玩家ID和日期倒序排列 { $sort: { playerId: 1, date: -1 } }, // 分组获取最新和前一天的分数 { $group: { _id: "$playerId", latestScore: { $first: "$score" }, previousScore: { $last: "$score" }, latestPosition: { $first: "$position" } } }, // 计算变化幅度 { $addFields: { scoreChange: { $abs: { $subtract: ["$latestScore", "$previousScore"] } } } }, // 按变化幅度降序排序 { $sort: { scoreChange: -1 } }, { $limit: 5 } ]); res.json(playerScores); });
3. 前端适配(可选)
如果后端已返回排序后的数据,前端直接使用即可。若需前端临时排序(不推荐,建议后端处理),可在数据处理后添加排序逻辑:
// 在setPlayer前添加排序 const sortedPlayers = [...newPlayer].sort((a, b) => { const changeA = Math.abs(a.score - a.previousScore); const changeB = Math.abs(b.score - b.previousScore); return changeB - changeA; // 降序排列 }); setPlayer(sortedPlayers);
内容的提问来源于stack exchange,提问作者yandry santana
相关产品推荐
相关产品推荐

