如何使用 .aggregate 从多个集合中获取数据,包括 Mongoose + NextJS 的求和值

问题描述 投票:0回答:1

我正在尝试从 2 个模式返回数据。其中一个包含“玩家”的信息,另一个包含每个“玩家”的游戏“分数”信息。

这是我的“玩家”模式:

import { Schema, model, models } from "mongoose";

const PlayerSchema = new Schema({
    creator: {
        type: Schema.Types.ObjectId,
        ref: 'User',
    },

    name: {
        type: String,
        require: [ true, 'Name is required!' ],
    },

    email: {
        type: String,
        unique: [ true, 'Email already exist!' ],
        required: [ true, 'Email is required!' ],
    }
});

const Player = models.Player || model( 'Player', PlayerSchema );

export default Player;

这是我的“分数”模式: (注意这个还包含子文档内容)

import { Schema, model, models } from "mongoose";

const scoresToSave = new Schema({
    player: {
        type: Schema.Types.ObjectId,
        ref: 'Player',
    },
    score: { type: Number }
});

const ScoreSchema = new Schema({
    creator: {
        type: Schema.Types.ObjectId,
        ref: 'User',
    },

    tournament: {
        type: Schema.Types.ObjectId,
        ref: 'Tournament',
    },

    date: {
        type: Date,
        require: [ true, 'Date is required!' ],
    },

    scores: [scoresToSave],
});

const Score = models.Score || model( 'Score', ScoreSchema );

export default Score;

这是保存在集合中的实际文档的示例。

玩家:

{
"_id":{"$oid":"652f3470729e8d8a59b221dd"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"name":"Player 1",
"email":"[email protected]"
}

{
"_id":{"$oid":"652f36c9f6ad40c3aedf0cdf"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"name":"Player 2",
"email":"[email protected]"
}

分数

{
"_id":{"$oid":"65386103b5e6b094e0980f2a"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"tournament":{"$oid":"652f2515729e8d8a59b221b6"},
"date":{"$date":{"$numberLong":"1698192000000"}},
"scores":[
  {
    "player":{"$oid":"652f3470729e8d8a59b221dd"},
    "score":{"$numberInt":"10"},
    "_id":{"$oid":"65386103b5e6b094e0980f2b"}
  },
  {
    "player":{"$oid":"652f36c9f6ad40c3aedf0cdf"},
    "score":{"$numberInt":"20"},
    "_id":{"$oid":"65386103b5e6b094e0980f2c"}
  }
]
}

{
"_id":{"$oid":"6538611bb5e6b094e0980f59"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"tournament":{"$oid":"652f2515729e8d8a59b221b6"},
"date":{"$date":{"$numberLong":"1698192000000"}},
"scores":[
  {
    "player":{"$oid":"652f3470729e8d8a59b221dd"},
    "score":{"$numberInt":"11"},
    "_id":{"$oid":"6538611bb5e6b094e0980f5a"}
  },
  {
    "player":{"$oid":"652f36c9f6ad40c3aedf0cdf"},
    "score":{"$numberInt":"21"},
    "_id":{"$oid":"6538611bb5e6b094e0980f5b"}
  }
]
}

这是我要创建作为响应的数据结构:

[
  {
    _id: new ObjectId("652f3470729e8d8a59b221dd"),
    name: 'Player 1',
    email: '[email protected]',
    totalScore: 21
  },
  {
    _id: new ObjectId("6536f85cb5e6b094e09809f1"),
    name: 'Player 2',
    email: '[email protected]',
    totalScore: 41
  },
]

我尝试获取该数据结构的实际代码是:

import { connectToDB } from "@utils/database";
import Player from "@models/player";

export const GET = async ( req ) => {
    const resources = {
        "_id": "$_id",
        name: { "$first": "$name" },
        email: { "$first": "$email" },
        "totalScore": { "$sum": "scores.scores.score" },
    };

    try {
        await connectToDB();

        const players = await Player.aggregate([
            {
                $group: resources
            }, {
                $lookup: {
                    from: "Scores",
                    localField: "_id",
                    foreignField: "scores.player",
                    as: "score"
                }
            }
        ]);

        console.log('TRY: ', players);

        return new Response(
            JSON.stringify(players),
            {status: 200}
        );
    } catch (error) {
        return new Response(
            "Failed to fetch all players",
            {status: 500}
        );
    }
}

这就是

console.log('TRY: ', players);
返回的内容:

TRY:  [
  {
    _id: new ObjectId("652f3470729e8d8a59b221dd"),
    name: 'Player 1',
    email: '[email protected]',
    totalScore: 0,
    score: []
  },
  {
    _id: new ObjectId("6536f85cb5e6b094e09809f1"),
    name: 'Player 2',
    email: '[email protected]',
    totalScore: 0,
    score: []
  },
]

有人可以尝试帮助我理解我在那里缺少什么吗?

谢谢!

reactjs mongoose aggregate-functions
1个回答
0
投票

我认为在

aggregate
集合上做你的
scores
会更容易。只是因为那是您的
scores.score
字段所在的位置,因此您可以在此处执行
$sum
。可能有更有效的方法,但此查询应该返回您想要的结果:

const players = await Scores.aggregate([
  {
    $unwind: {
      path: "$scores"
    }
  },
  {
    $project: {
      "scores": 1,
      "_id": 0
    }
  },
  {
    $group: {
      _id: "$scores.player",
      count: {
        $sum: "$scores.score"
      }
    }
  },
  {
    $lookup: {
      from: "players",
      localField: "_id",
      foreignField: "_id",
      as: "fromPlayers"
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [
          {
            $arrayElemAt: [
              "$fromPlayers",
              0
            ]
          },
          "$$ROOT"
        ]
      }
    }
  },
  {
    $project: {
      fromPlayers: 0,
      creator: 0
    }
  }
])

请参阅此处了解工作示例。

解释

  1. $unwind
    :展平
    scores
    数组,以便我们为每个分数文档获得一个对象。这显然会重复文档,但更容易按玩家分组。
  2. $project
    :仅输出
    scores
    对象,因为这是唯一感兴趣的对象。
  3. $group
    :按玩家和
    $sum
    他们的分数对每个对象进行分组。
  4. $lookup
    :现在加入基于
    players
    _id
    集合。但是因为
    $lookup
    返回并且数组只是分配给一个名为
    fromPlayers
    的新字段。
  5. $replaceRoot
    :通过将
    fromPlayers
    数组与上一个管道阶段的原始文档合并来创建一个新的根文档。
  6. $project
    :现在从输出中删除
    fromPlayers
    数组和
    creator
    字段。
© www.soinside.com 2019 - 2024. All rights reserved.