2017-10-20 127 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