2017-10-20 125 views
0

编辑:这可能是问题与路径问题。我目前的查询是这样的:路由到子文档与快递4和猫鼬

router.route('/projects/:project_id/techDetails') 
    .get(function(req, res) { 
    Project.findById(req.params.project_Id, function(err, project) { 
     if (err) 
      return res.send(err); 
     res.json(project); 
     console.log('get success (project techDetails)'); 
    }); 
}); 

这会返回null。尽管除了向路由添加`/ techDetails'之外,它在各种方式中都与代码的工作线相同。

原题:

我建设有快递和蒙戈平均栈的应用程序。我无法弄清楚如何正确地路由到嵌套文档。

这里是我的项目模式

const ProjectSchema = new Schema({ 
    idnumber: { type: Number, required: true }, 
    customername: String, 
    projectdetails: String, 
    jobaddress: String, 
    techDetails: [{ 
    scope: String, 
    edgedetail: String, 
    lamination: String, 
    stonecolour: String, 
    slabnumber: String, 
    slabsupplier: String, 
    purchaseordernum: String, 
    splashbacks: String, 
    apron: String, 
    hotplate: String, 
    sink: String, 
    sinkdetails: String, 
    tappos: String 
    }], 
    sitecontactname: String, 
    sitecontactnum: String, 
    specialreq: String, 
    install_date: String, 
    created_on: { type: Date, default: Date.now }, 
    created_by: { type: String, default: 'SYSTEM' }, 
    active: { type: Boolean, default: true }, 
    flagged: { type: Boolean, default: false }, 
}); 

我可以与GETPUTDEL成功路线/projectsGETPOST/projects/:project_id

使用PUT路由和项目的_ID我可以将新条目推送到项目的techDetails子数组。所产生的JSON数据看起来是这样的:

{ 
"_id": "59e577e011a3f512b482ef13", 
"idnumber": 52, 
"install_date": "10/20/2017", 
"specialreq": "some...", 
"sitecontactnum": "987654321", 
"sitecontactname": "bill", 
"jobaddress": "123 st", 
"projectdetails": "some stuff", 
"customername": "B Builders", 
"__v": 16, 
"flagged": false, 
"active": true, 
"created_by": "SYSTEM", 
"created_on": "2017-10-17T03:24:16.423Z", 
"techDetails": [ 
    { 
     "scope": "Howitzer", 
     "edgedetail": "12mm", 
     "lamination": "No", 
     "stonecolour": "Urban™", 
     "slabnumber": "1", 
     "slabsupplier": "Caesarstone", 
     "purchaseordernum": "no", 
     "splashbacks": "No", 
     "apron": "No", 
     "hotplate": "N/A", 
     "sink": "N/A", 
     "sinkdetails": "no", 
     "tappos": "no", 
     "_id": "59e577e011a3f512b482ef14" 
    }, 
    { 
     "scope": "kitchen", 
     "edgedetail": "12mm", 
     "lamination": "etc", 
     "_id": "59e7da445d9d7e109c18f38b" 
    }, 
    { 
     "scope": "Vanity", 
     "edgedetail": "12mm", 
     "lamination": "No", 
     "stonecolour": "Linen™", 
     "slabnumber": "1", 
     "slabsupplier": "Caesarstone", 
     "purchaseordernum": "1", 
     "splashbacks": "No", 
     "apron": "No", 
     "hotplate": "N/A", 
     "sink": "N/A", 
     "sinkdetails": "no", 
     "tappos": "woo", 
     "_id": "59e81e3324fb750fb46f8248" 
    }//, more entries omitted for brevity 
    ] 
} 

正如你所看到的一切至今工作正常。但是现在我需要编辑和删除这个techDetails数组中的单个条目。我还想直接使用projects/:project_id/techDetailsprojects/:project_id/techDetails/:techdetails_id转发给他们。

从我可以看到有两种方法。我可以:

A)使用mergeParams的techDetails的新路由文件。这是我目前正在尝试的方法,但是我无法弄清楚如何完成.find以返回所有techDetails,因为我只能使用Project模型模式,并且我不确定如何访问子文档。

摘录从我routes.js

const techDetails = require('./techDetails'); 
//other routes here 

//see techdetails file 
router.use('/projects/:project_id/techdetails', techDetails); 

//here lies an earlier, failed attempt 
/* router.route('/projects/:project_id/techdetails/:techDetails_id') 
.get(function(req, res) { 
    Project.findById(req.params.project_id.techDetails_id, function(err, 
project) { 
     if (err) 
      return res.send(err); 
     res.json(project.techDetails); 
     console.log('get success (techDetails)'); 
    }); 
    }) 
; */ 

和我techdetails.js

const express = require('express'); 
const Project = require('./models/project'); 
const router = express.Router({mergeParams: true}); 

router.get('/', function (req, res, next) { 
/* Project.find(function(err, techDetails) { 
    if (err) 
     return res.send(err); 
    res.json(techDetails); 
    console.log('get success (all items)'); 
    }); */ 
    res.send('itemroutes ' + req.params); 
}) 

router.get('/:techDetails_id', function (req, res, next) { 
    res.send('itemroutes ' + req.params._id) 
}) 

module.exports = router 

我可以成功地检查路由与邮差工作,都将获得响应。现在的问题是,而不是res.send我想用res.jsonProject.find(或类似)来获得techDetails

但也有另一种选择:

B)把techDetails记录到它自己的架构,然后填充的ID内的项目的数组。

但是,这似乎更复杂,所以我宁愿避免这样做,如果我可以。

欢迎任何想法和建议。让我知道是否需要更多我的代码。

回答

0

所以,我来的解决方案是A)和B)的组合。我使用了一个单独的路由文件,并在路由器声明中放置了({mergeParams: true}),并且我为techDetails嵌套模型创建了一个单独的文件,但未声明它。不过,我不相信这些实际上都有意义......但无论如何。

工作的代码,我结束了与是,在我的路线:

router.use('/projects/:project_id/techDetails', TechDetails); 

techDetails.js:

const router = express.Router({mergeParams: true}); 

router.route('/') 
    .get(function(req, res) { 
    Project.findById(req.params.project_id, 
     'techDetails', function(err, project) { 
     if (err) 
      return res.send(err); 
     res.json(project); 
     console.log('get success (project techDetails)'); 
    }); 
}); 

什么是它有什么不同?即Project.findById行中的'techDetails',参数。根据mongoose API,这起到了选择声明的作用。唯一的另一个主要区别是我修正了原始代码中的错字(project_id写成project_Id。可疑...)。我可能会注意到这一点,如果我使用VS或其他东西,而不是记事本++,但它是我的首选编码领域。

它可能会返回res.json(project.techDetails)并删除'techDetails',选择参数,但我可能不会测试这一点。

编辑:原来迁移techDetails到一个单独的文件,意味着他们不再与objectId s,这是PUT和DEL至关重要的生成。我可能已经能够在数组声明中用一对简单的花括号解决它们了,但是直到我将它重新迁移回项目模式之后,我才想到了这一点......

0

在这种特殊情况下我会把techDetails在一个独立的模式:

const ProjectSchema = new Schema({ 
    idnumber: { type: Number, required: true }, 
    customername: String, 
    projectdetails: String, 
    jobaddress: String, 
    techDetails: [techDetailsSchema], 
    sitecontactname: String, 
    sitecontactnum: String, 
    specialreq: String, 
    install_date: String, 
    created_on: { type: Date, default: Date.now }, 
    created_by: { type: String, default: 'SYSTEM' }, 
    active: { type: Boolean, default: true }, 
    flagged: { type: Boolean, default: false }, 
}); 

,因为它是一个子文档不注册mongoose.model的techDetails架构。把它放在一个单独的文件中并要求在项目模型文件(const techDetailsSchema = require('./techDetails.model');)中。

我会创造这样的控制器功能:

用GET获取(全部):

module.exports.techDetailsGetAll = function (req, res) { 
    const projectId = req.params.projectId; 

    Project 
    .findById(projectId) 
    .select('techDetails') 
    .exec(function (err, project) { 
     let response = { }; 

     if (err) { 
     response = responseDueToError(err); 
     } else if (!project) { 
     response = responseDueToNotFound(); 
     } else { 
     response.status = HttpStatus.OK; 
     response.message = project.techDetails; 
     } 

     res.status(response.status).json(response.message); 
    }); 
} 

用GET获取(之一):

module.exports.techDetailsGetOne = function (req, res) { 
    const projectId = req.params.projectId; 
    const techDetailId = req.params.techDetailId; 

    Project 
    .findById(projectId) 
    .select('techDetails') 
    .exec(function (err, project) { 
     let response = { }; 

     if (err) { 
     response = responseDueToError(err); 
     } else if (!project) { 
     response = responseDueToNotFound(); 
     } else { 
     let techDetails = project.techDetails.id(techDetailId); 

     if (techDetails === null) { 
      response = responseDueToNotFound(); 
     } else { 
      response.status = HttpStatus.OK; 
      response.message = techDetails; 
     } 
     } 

     res.status(response.status).json(response.message); 
    }); 
} 

用POST添加:

module.exports.techDetailsAddOne = function (req, res) { 
    const projectId = req.params.projectId; 

    let newTechDetails = getTechDetailsFromBody(req.body); 

    Project 
    .findByIdAndUpdate(projectId, 
    { '$push': { 'techDetails': newTechDetails } }, 
    { 
     'new': true, 
     'runValidators': true 
    }, 
    function (err, project) { 
     let response = { }; 

     if (err) { 
     response = responseDueToError(err); 
     } else if (!project) { 
     response = responseDueToNotFound(); 
     } else { 
     response.status = HttpStatus.CREATED; 
     response.message = project.techDetails; // for example 
     } 

     res.status(response.status).json(response.message); 
    }); 
} 

对于PUT

module.exports.techDetailsUpdateOne = function (req, res) { 
    const projectId = req.params.projectId; 
    const techDetailId = req.params.techDetailId; 

    let theseTechDetails = getTechDetailsFromBody(req.body); 
    theseTechDetails._id = techDetailId; // can be skipped if body contains id 

    Project.findOneAndUpdate(
    { '_id': projectId, 'techDetails._id': techDetailId }, 
    { '$set': { 'techDetails.$': theseTechDetails } }, 
    { 
     'new': true, 
     'runValidators': true 
    }, 
    function (err, project) { 
     let response = { }; 

     if (err) { 
     response = responseDueToError(err); 
     res.status(response.status).json(response.message); 
     } else if (!project) { 
     response = responseDueToNotFound(); 
     res.status(response.status).json(response.message); 
     } else { 
     project.save(function (err) { 
      if (err) { 
      response = responseDueToError(err); 
      } else { 
      response.status = HttpStatus.NO_CONTENT; 
      } 

      res.status(response.status).json(response.message); 
     }) 
     } 
    }); 
} 

更新和与DELETE删除:

module.exports.techDetailsDeleteOne = function (req, res) { 
    const projectId = req.params.projectId; 
    const techDetailId = req.params.techDetailId; 

    Project 
    .findById(projectId) 
    .select('techDetails') 
    .exec(function (err, project) { 
     let response = { } 

     if (err) { 
     response = responseDueToError(err); 
     res.status(response.status).json(response.message); 
     } else if (!project) { 
     response = responseDueToNotFound(); 
     res.status(response.status).json(response.message); 
     } else { 
     let techDetail = project.techDetails.id(techDetailId); 

     if (techDetail !== null) { 
      project.techDetails.pull({ '_id': techDetailId }); 

      project.save(function (err) { 
      if (err) { 
       response = responseDueToError(err); 
      } else { 
       response.status = HttpStatus.NO_CONTENT; 
      } 

      res.status(response.status).json(response.message); 
      }) 
     } else { 
      response = responseDueToNotFound(); 
      res.status(response.status).json(response.message); 
     } 
     } 
    }); 
} 

最后路由这样:

router 
    .route('/projects') 
    .get(ctrlProjects.projectsGetAll) 
    .post(ctrlProjects.projectsAddOne); 

router 
    .route('/projects/:projectId') 
    .get(ctrlProjects.projectsGetOne) 
    .put(ctrlProjects.projectsUpdateOne) 
    .delete(ctrlProjects.projectsDeleteOne); 

router 
    .route('/projects/:projectId/techDetails') 
    .get(ctrlTechDetails.techDetailsGetAll) 
    .post(ctrlTechDetails.techDetailsAddOne); 

router 
    .route('/projects/:projectId/techDetails/:techDetailId') 
    .get(ctrlTechDetails.techDetailsGetOne) 
    .put(ctrlTechDetails.techDetailsUpdateOne) 
    .delete(ctrlTechDetails.techDetailsDeleteOne); 

当我不断更新独立于文档其余部分的子文档时,我更喜欢这种方式。它不创建一个单独的集合,所以不需要填充。

编辑: This答案会更详细地介绍是否应该使用嵌入或引用。我的答案使用嵌入。

+0

因此,您可以定义一个子文档作为模式,而不是一个模型?谢谢,这使得选项B的方式更容易。我更喜欢避免使用单独的控制器,但随着程序的规模不断扩大,开始使用它们可能是一个好主意。我会让你知道,如果我可以得到它的工作!感谢你及时的答复! – KuroKyo

+0

'response.message = project.techDetails;'返回null和'techDetails'本身返回undefined ...这几乎是我之前遇到的同样的问题。我似乎无法访问嵌套的任何东西。 – KuroKyo

+0

用mongoose.model注册模式将把techDetails放在一个单独的集合中(参见[这里](https://stackoverflow.com/questions/21142524/mongodb-mongoose-how-to-find-subdocument-in-found-document )更多信息)。如果你打算每个项目都有大量的techDetails,那么这可能是你想要的,因为techDetails数组会使项目相当大。或者如果你在项目中分享techDetails。否则,我会去没有一个单独的集合。 Max BSON文档大小为16 MB。 – MikaS