You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:10:27