【问题标题】:MongoDB How do I reference an array?MongoDB 如何引用数组?
【发布时间】:2021-11-26 20:02:15
【问题描述】:

我正在尝试创建一个包含两个集合的 MongoDB 数据库:学生和课程。

第一个集合“学生”包含:

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.Database

student = [{"_id":"0",
          "firstname":"Bert",
           "lastname":"Holden"},
           {"_id":"1",
            "firstname":"Sam",
            "lastname":"Olsen"},
           {"_id":"2",
            "firstname":"James",
            "lastname":"Swan"}]


students = db.students
students.insert_many(student)
pprint.pprint(students.find_one())

第二个集合“课程”包含:

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.Database

course = [{"_id":"10",
           "coursename":"Databases",
           "grades":"[{student_id:0, grade:83.442}, {student_id:1, grade:45.323}, {student_id:2, grade:87.435}]"}]




courses = db.courses
courses.insert_many(course)
pprint.pprint(courses.find_one())

然后我想使用聚合来查找学生和相应的课程和成绩。

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client["Database"]

pipeline = [
    {
        "$lookup": {
            "from": "courses",
            "localField": "_id",
            "foreignField": "student_id",
            "as": "student_course"
            }
    },
    {
        "$match": {
            "_id": "0"
        }
    }
]

pprint.pprint(list(db.students.aggregate(pipeline)))

我不确定 student_id/grade 是否在“课程”集合中正确实施,所以这可能是我的arregation 返回 [] 的原因之一。

如果我为每个学生创建单独的课程,聚合就可以工作,但这似乎很浪费内存,所以我希望有一门课程包含一个数组中的所有 student_ids 和成绩。

预期输出:

 [{'_id': '0',
  'firstname': 'Bert',
  'lastname': 'Holden',
  'student_course': [{'_id': '10',
                      'coursename': 'Databases',
                      'grade': '83.442',
                      'student_id': '0'}]}]

【问题讨论】:

  • 为什么将成绩保存为字符串而不是对象?
  • 我不确定。你愿意解释一下吗? :)
  • 抱歉,直到现在我无法深入了解。我已经发布了一个答案(即使您已经找到了一个有效的答案)。

标签: python database mongodb reference


【解决方案1】:

有几点值得一提..

  1. 文件“courses.py”中的示例代码将成绩插入为 表示数组的字符串,而不是实际的数组。这是 由马特在 cmets 中指出,您要求 解释。这是我试图解释的 - 如果你插入一个字符串 看起来像一个数组,您无法对其执行 $unwind 或 $lookup 子元素,因为它们不是子元素,它们是 字符串。
  2. 您在包含学生成绩的课程中有数组数据,这些数据是 所需的数据点,但您开始聚合 学生收藏。相反,也许改变你的观点 一点点从课程集合中获取,而不是从 学生视角。如果你这样做,你可能会重新获得资格 要求为-“向我显示所有课程和学生成绩 学生 ID 为 0"。
  3. 您的数组数据似乎存在数据类型不匹配。学生证 是字符串变量“数组”中的整数,但学生 集合具有学生 ID 作为字符串。需要保持一致 为 $lookup 正常工作(如果不想执行一堆 铸造)。

但是,尽管如此,这里有一个可能的解决方案来解决您的问题。我已经修改了python代码,包括重新定义聚合...

我的测试数据库的名称是pythontest,如本代码示例所示。 此数据库必须在运行代码之前存在,否则会出错。

归档students.py

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.pythontest

student = [{"_id":"0",
          "firstname":"Bert",
           "lastname":"Holden"},
           {"_id":"1",
            "firstname":"Sam",
            "lastname":"Olsen"},
           {"_id":"2",
            "firstname":"James",
            "lastname":"Swan"}]


students = db.students
students.insert_many(student)
pprint.pprint(students.find_one())

然后是课程文件。请注意字段grades 不再是字符串,而是有效的数组对象?请注意学生 id 是一个字符串,而不是一个整数? (实际上,UUID 或 int 等更强大的数据类型可能更可取)。

文件课程.py

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.pythontest

course = [{"_id":"10",
           "coursename":"Databases",
           "grades": [{ "student_id": "0", "grade": 83.442}, {"student_id": "1", "grade": 45.323}, {"student_id": "2", "grade": 87.435}]}]


courses = db.courses
courses.insert_many(course)
pprint.pprint(courses.find_one())

...最后是聚合管道更改后的聚合文件...

文件聚合.py

from pymongo import MongoClient
import pprint

client = MongoClient("mongodb://127.0.0.1:27017")
db = client.pythontest

pipeline = [
    { "$match": { "grades.student_id": "0" } },
    { "$unwind": "$grades" },
    { "$project": { "coursename": 1, "student_id": "$grades.student_id", "grade": "$grades.grade" } },
    {
        "$lookup":
        {
            "from": "students",
            "localField": "student_id",
            "foreignField": "_id",
            "as": "student"
        }
    },
    {
        "$unwind": "$student"
    },
    { "$project": { "student._id": 0 } },
    { "$match": { "student_id": "0" } }
]

pprint.pprint(list(db.courses.aggregate(pipeline)))

运行程序的输出

> python3 aggregation.py
[{'_id': '10',
  'coursename': 'Databases',
  'grade': 83.442,
  'student': {'firstname': 'Bert', 'lastname': 'Holden'},
  'student_id': '0'}]

程序结束时的数据格式可能不符合预期,但可以通过操作聚合来调整。

** 编辑 **

因此,如果您想从学生那里获取此聚合,而不是从课程中获取它,您仍然可以执行该聚合,但由于数组在课程中,聚合会稍微复杂一些。 $lookup 必须利用管道本身来准备外部数据结构:

学生视角的聚合

db.students.aggregate([
{ $match: { _id: "0" } },
{ $addFields: { "colStudents._id": "$_id" } },
{
    $lookup:
    {
        from: "courses",
        let: { varStudentId: "$colStudents._id"},
        pipeline:
        [
            { $unwind: "$grades" },
            { $match: { $expr: { $eq: ["$grades.student_id", "$$varStudentId" ] } } },
            { $project: { course_id: "$_id", coursename: 1, grade: "$grades.grade", _id: 0} }
        ],
        as: "student_course"
    }
},
{ $project: { _id: 0, student_id: "$_id", firstname: 1, lastname: 1, student_course: 1 } }
])

输出

> python3 aggregation.py
[{'firstname': 'Bert',
  'lastname': 'Holden',
  'student_course': [{'course_id': '10',
                      'coursename': 'Databases',
                      'grade': 83.442}],
  'student_id': '0'}]

【讨论】:

  • 您好!这完美地工作。谢谢!我实际上尝试了精确的聚合,但是当我的 courses.py 文件没有正确完成时,它没有给我任何输出。这正是我来到这里的原因,因为我不知道我的聚合或我的收藏是否有问题。再次感谢您的详尽解释! :D
  • @MadVags - 感谢您的积分!我在帖子中添加了更新。如果从学生的角度来看,我也添加了聚合。它有点复杂,因为数组在外部表中,但希望它会有意义......
【解决方案2】:

我终于可以看看这个了..

TLDR; see Mongo Playground

此解决方案要求您将 grades 存储为实际对象而不是字符串。

考虑以下数据库结构:

db={
  // Collection
  "students": [
    {
      "_id": "0",
      "firstname": "Bert",
      "lastname": "Holden"
    },
    {
      "_id": "1",
      "firstname": "Sam",
      "lastname": "Olsen"
    },
    {
      "_id": "2",
      "firstname": "James",
      "lastname": "Swan"
    }
  ],
  // Collection
  "courses": [
    {
      "_id": "10",
      "coursename": "Databases",
      "grades": [
        {
          student_id: "0",
          grade: 83.442
        },
        {
          student_id: "1",
          grade: 45.325
        },
        {
          student_id: "2",
          grade: 87.435
        }
      ]
    }
  ],
}

您可以使用以下查询来实现您想要的:

db.students.aggregate([
  {
    $match: {
      _id: "0"
    }
  },
  {
    $lookup: {
      from: "courses",
      pipeline: [
        {
          $unwind: "$grades"
        },
        {
          $match: {
            "grades.student_id": "0"
          }
        },
        {
          $group: {
            "_id": "$_id",
            "coursename": {
              $first: "$coursename"
            },
            "grade": {
              $first: "$grades.grade"
            },
            "student_id": {
              $first: "$grades.student_id"
            }
          }
        }
      ],
      as: "student_course"
    }
  }
])

【讨论】:

    猜你喜欢
    • 2011-05-02
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    相关资源
    最近更新 更多