mongodb 按多个匹配字段之一分组

aij0ehis  于 2022-11-22  发布在  Go
关注(0)|答案(1)|浏览(114)

我有一个文档集合(grades),其中缺少一些键,如下所示:
| 名称名称名称|识别码|吉苏布|点数|
| - -|- -|- -|- -|
| 彼得|一个||一百二十三|
| | 一个|聚丙烯|四百五十六|
| 爱丽斯||爱丽丝1|二百三十四|
| | 2个|爱丽丝1|五六七|
我想通过匹配Name、ID或Github中的任何一个来对这些数据进行分组,并收集点数。结果应该如下所示:
| 识别码(_I)|点数|
| - -|- -|
| [彼得,1,PPane]|[一二三,四五六]|
| [爱丽丝,2,爱丽丝1]|【二三四,五六七】|
现在我在后端这样做:

const students = new Map<string, CourseStudent>();
    const keys = ['Name', 'ID', 'Github'];
    for (const grade of grades) {
      let student: CourseStudent | undefined = undefined;
      for (const key of keys) {
        const value = grade[key];
        if (value && (student = students.get(value))) {
          break;
        }
      }
      if (!student) {
        const {Name, ID, Github} = grade;
        student = {_id: {Name, ID, Github}, points: []};
      }
      for (const key of keys) {
        const value = grade[key];
        if (value) {
          students.set(value, student);
        }
      }

      student.points.push(grade.points);
    }
    return Array.from(students.values());

我的用例中的数据大小是1000-10000个年级(100-1000个学生x 10个作业)。实际的“年级”数据包含更多的字段,其中大多数字段不用于最终结果,但将所有这些字段保存在内存中可能会花费很大的成本。
是否有办法在数据库中通过聚合管道(例如使用$group)实现这一点?
首先,这里是一个非工作聚合,因为它要求所有字段匹配,而不是只有一个:

{$group: {_id: ['$Name', '$ID', '$Github'], points: {$push: '$Points'}}},
oalqel3c

oalqel3c1#

由于您只有3个密钥可供使用,因此按一个密钥分组并收集另外两个密钥将导致仅缺少一个密钥:

  1. $group乘以ID并收集其他密钥。假设每个用户至少有一个文档包含该用户的key(如您示例中所示),此步骤的结果是文档数等于用户数+ 1。每个文档包含用户IDNameGithub。一份文件指所有文件,不含任何ID
  2. $match仅保留用户文档
    1.对于每个用户文档,使用$lookup获取所有匹配的原始文档,现在我们有了获取它们所需的数据。
    1.对结果进行分组和格式化。
db.collection.aggregate([
  {$group: {_id: "$ID", Name: {$first: "$Name"}, Github: {$first: "$Github"}}},
  {$match: {_id: {$ne: null}}},
  {$lookup: {
      from: "collection",
      let: {github: "$Github", iD: "$_id"},
      pipeline: [
        {$match: {
            $expr: {$or: [
                {$eq: ["$$github", "$Github"]},
                {$eq: ["$$iD", "$ID"]}
            ]}
        }},
        {$group: {
            _id: 0,
            Name: {$addToSet: "$Name"},
            Github: {$addToSet: "$Github"},
            ID: {$addToSet: "$ID"},
            Points: {$push: "$Points"}
        }}
      ],
      as: "docs"
    }
  },
  {$replaceRoot: {newRoot: {$first: "$docs"}}},
  {$project: {
      _id: [{$first: "$Name"}, {$first: "$ID"}, {$first: "$Github"}],
      Points: 1
  }}
])

了解它在playground example上的工作原理

相关问题