我正在try 返回来自2个模式的数据.其中一个包含"玩家"的信息,另一个包含每个"玩家"按游戏的"得分"信息.

This is my 'Player' schema:

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;

This is my 'Score' schema:. (note this one also contain a subdocuments content).

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;

This is an example of the actual documents saved in the Collections.

Players:

{
"_id":{"$oid":"652f3470729e8d8a59b221dd"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"name":"Player 1",
"email":"player1@email.com"
}

{
"_id":{"$oid":"652f36c9f6ad40c3aedf0cdf"},
"creator":{"$oid":"652af415729e8d8a59b2202f"},
"name":"Player 2",
"email":"player2@email.com"
}

Scores

{
"_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"}
  }
]
}

This is the data structure that I want to create as response:

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

My actual code to try to get that data structure is:

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}
        );
    }
}

This is what 100 returns:

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

有人能帮我弄明白我错过了什么吗?

谢谢!

推荐答案

我认为在scores系列上做你的aggregate会更容易.很简单,因为这是您的scores.score个字段所在的位置,所以您可以在那里执行$sum.可能有更有效的方法,但此查询应该返回您想要的结果:

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

有关工作示例,请参见HERE.

Explanation:

  1. $unwind:展平scores数组,以便每个分数文档都有一个对象.这显然复制了文档,但更容易按玩家分组.
  2. $group:根据玩家对每个对象进行分组,并对其分数进行$sum.
  3. $lookup:现在加入基于_idplayers集合.但是因为$lookup返回一个数组,所以只需将其赋给一个名为fromPlayers的新字段.
  4. $replaceRoot:通过将fromPlayers数组与前一个管道阶段的原始文档合并来创建新的根文档.
  5. $project:现在从输出中删除fromPlayers数组和creator字段.

Reactjs相关问答推荐

Npm运行构建`在Next.js 14中不起作用

cypress 不能点击SPAN

如何通过回调函数获取多个值?

无法将react 路由状态从路由传递给子组件

在Map()完成React.js中的多个垃圾API请求后执行函数

子路径上未定义useMatches句柄

当项目开始时,它不会从三元运算符(REACT)加载一些tailwind 类名称

有没有可能在next.js和ssr中使用tsps?

无法在REACTION TANSTACK查询中传递参数

为什么在页面重新加载时Location.State变得未定义?

如何将 MUI X 数据网格单元格方向更改为行级而不是列级?

如何在我的自定义主题中样式化MUI TreeItem组件

如何读取 null 的属性?

Cypress 组件测试不渲染react 组件(但它的内容)

在 React 的父级中多次调用子级时从子级获取数据到父级

用 jest 测试包裹在 redux 和 router 中的组件

如何用实际的br标签替换axios响应中的br标签?

Select MenuItem 时 Material-ui 不更改样式

a.active 类在导入 x.scss 文件时有效,但在使用 x.module.scss 时无效

用户输入值时更改值