首页 文章

MongoDB / CouchDB:将表连接到自身

提问于
浏览
2

我有一个关系数据模型我正在考虑迁移到MongoDB或CouchDB,我试图了解查询是如何工作的 . 假设我有两个实体,Employees和Projects,以及一个名为Assignments的多对多连接表 . 我想查询两个用户合作的所有项目 . 在SQL中,我可以这样做:

SELECT DISTINCT a1.project_id
FROM assignments a1, assignments a2
WHERE a1.project_id = a2.project_id
AND a1.employee_id = ?
AND a2.employee_id = ?

假设我有Employee,Project和Assignment“文档”,我将如何在NoSQL中执行此操作?或者你会以不同的方式构建文档,这会如何影响查询?

我很高兴听到Mongo的查询API和Couch的map / reduce方法的答案 .

3 回答

  • 0

    我只能分享沙发的观点,我,嗯,每隔一段时间放松一下 . 首先,在处理基于文档的系统时,您几乎总是有意忘记SQL .

    其次,您可以选择更改结构并将实体数量减少到不需要连接的最小值,或者您可以使用将不同类型的文档组合到单个结果集中的视图 .

    前者(重新设计)是首选方法,因为连接本质上是nosql的外来方法,因为规范化不是那里的要求 . 基于文档不是关系型的 .

  • 3

    在MongoDB中,你可以这样做 . 我使用交互式JavaScript Shell .

    首先创建一些用户:

    > db.so.employee.insert({name: "Joe"})
    > db.so.employee.insert({name: "Moe"})
    > db.so.employee.insert({name: "Bart"})
    > db.so.employee.insert({name: "Homer"})
    > db.so.employee.find()
    { "_id" : ObjectId("4de35ccbcc0379536e1ac43b"), "name" : "Joe" }
    { "_id" : ObjectId("4de35ccfcc0379536e1ac43c"), "name" : "Moe" }
    { "_id" : ObjectId("4de35cd3cc0379536e1ac43d"), "name" : "Bart" }
    { "_id" : ObjectId("4de35cd7cc0379536e1ac43e"), "name" : "Homer" }
    

    现在创建一些项目

    > db.so.project.insert({name: "Web App A"})
    > db.so.project.insert({name: "Web App B"})
    > db.so.project.insert({name: "Web App C"})
    > db.so.project.find();
    { "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
    { "_id" : ObjectId("4de35d13cc0379536e1ac440"), "name" : "Web App B" }
    { "_id" : ObjectId("4de35d15cc0379536e1ac441"), "name" : "Web App C" }
    

    将用户添加到项目中

    > db.so.project.update({name: "Web App A"}, {$push: {employees: ObjectId('4de35ccbcc0379536e1ac43b') }})
    > db.so.project.update({name: "Web App A"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
    > db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
    > db.so.project.update({name: "Web App C"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
    > db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35cd3cc0379536e1ac43d') }})
    > db.so.project.update({name: "Web App C"}, {$push: {employees: ObjectId('4de35cd3cc0379536e1ac43d') }})
    > db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35cd7cc0379536e1ac43e') }})
    
    > db.so.project.find()
    { "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "employees" : [
        ObjectId("4de35ccbcc0379536e1ac43b"),
        ObjectId("4de35ccfcc0379536e1ac43c")
    ], "name" : "Web App A" }
    { "_id" : ObjectId("4de35d15cc0379536e1ac441"), "employees" : [
        ObjectId("4de35ccfcc0379536e1ac43c"),
        ObjectId("4de35cd3cc0379536e1ac43d")
    ], "name" : "Web App C" }
    { "_id" : ObjectId("4de35d13cc0379536e1ac440"), "employees" : [
        ObjectId("4de35cd3cc0379536e1ac43d"),
        ObjectId("4de35cd7cc0379536e1ac43e")
    ], "name" : "Web App B" }
    

    如果你现在想要找到所有项目“Joe”

    > db.so.project.find({employees: ObjectId('4de35ccbcc0379536e1ac43b') }, {name: 1})
    { "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
    

    找到Joe OR Moe工作的所有项目

    > db.so.project.find({employees: {$in: [ObjectId('4de35ccbcc0379536e1ac43b'), ObjectId('4de35ccfcc0379536e1ac43c')] }}, {name: 1})
    { "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
    { "_id" : ObjectId("4de35d15cc0379536e1ac441"), "name" : "Web App C" }
    { "_id" : ObjectId("4de35d13cc0379536e1ac440"), "name" : "Web App B" }
    

    找到Joe AND moe工作的所有项目

    > db.so.project.find({employees: {$all: [ObjectId('4de35ccbcc0379536e1ac43b'), ObjectId('4de35ccfcc0379536e1ac43c')] }}, {name: 1})
    { "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
    

    要获得特定项目的所有雇员,您需要两个查询 . 从项目C中寻找所有员工 .

    > db.so.project.find({name: "Web App C"}, {employees: 1})
    { "_id" : ObjectId("4de35d15cc0379536e1ac441"), "employees" : [
        ObjectId("4de35ccfcc0379536e1ac43c"),
        ObjectId("4de35cd3cc0379536e1ac43d")
    ] }
    

    在您的应用程序中,您可以根据返回值构建新查询并构建此查询:

    > db.so.employee.find({_id: {$in: [ObjectId('4de35ccfcc0379536e1ac43c'), ObjectId('4de35cd3cc0379536e1ac43d')] }})
    { "_id" : ObjectId("4de35ccfcc0379536e1ac43c"), "name" : "Moe" }
    { "_id" : ObjectId("4de35cd3cc0379536e1ac43d"), "name" : "Bart" }
    

    我希望这有助于理解MongoDB如何工作以及如何 Build 关系 . 我在这里使用手动解除引用 . 这意味着我直接保存ObjectID并手动获取它 . 还存在“DBRef”,然后您的驱动程序会为您获取它 .

  • 2

    您正在寻找的是有效的N:M映射 . 在这种情况下,您尝试将表映射到自身,但这与尝试将“Employees”映射到“Projects”没有太大区别 .

    有一个很长的答案over here关于我,我不会在这里重复 .

    在您的具体情况下,我认为您需要重新设计一些数据 . 您有一个包含两个数据点的 assignments 表: projectIDemployeedID . 这是将N个项目加入M员工的经典表格 .

    在MongoDB中,通常根本没有"table" . 如果要将员工分配给项目,则只需在项目本身中存储 employeeID 数组 .

    { name: 'projectx', emps: [ 1, 2, 3] }
    { name: 'projecty', emps: [ 3, 2] }
    

    看起来您的查询基本上是"find all projects where employee 2 and 3 are working together" . MongoDB有一个$all query operator,它将通过上述结构为您完成此操作 .

    请注意,这只是一个查询 . 在进行模式设计时,重要的是要查看整个系统并确定 all 重要查询 .

相关问题