编辑:问题可能是路径问题。我当前的查询如下所示:
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”之外,它与各种方式的工作代码行相同。
原始问题:
我正在使用express和mongo构建一个MEAN堆栈应用程序。我无法弄清楚如何正确地路由到嵌套文档。
这是我的项目架构:
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 },
});
我可以使用/projects
和GET
以及POST
与/projects/:project_id
,GET
和PUT
成功路由到DEL
使用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/techDetails
和projects/: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
我可以成功检查路线是否与Postman一起使用,两者都会收到回复。现在的问题是,而不是res.send
我希望res.json
使用Project.find
(或类似)来获取techDetails
。
然而还有另一种选择:
B)将techDetails文档放入自己的模式中,然后在项目中填充一系列ID。
然而,这似乎更复杂,所以如果可以,我宁愿避免这样做。欢迎任何想法和建议。如果我需要更多代码,请告诉我。
答案 0 :(得分: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 },
});
不要将techDetails架构注册为mongoose.model
,因为它是一个子文档。将它放在一个单独的文件中,并在项目模型文件(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回答更详细。我的回答是使用嵌入。
答案 1 :(得分: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)');
});
});
它的不同之处是什么?即'techDetails',
行中的Project.findById
参数。根据mongoose API,它充当select语句。唯一的另一个主要区别是我修改了原始代码中的拼写错误(project_id
写了project_Id
。可疑......)。如果我使用的是VS或者其他东西而不是notepad ++,我可能会注意到这一点,但它是我首选的编码领域。
可以返回res.json(project.techDetails)
并删除'techDetails',
选择参数,但我可能无法对此进行测试。
修改:将techDetails
迁移到单独的文件意味着它们不再使用objectId
生成,这对PUT和DEL至关重要。我可能已经能够在数组声明中使用一对简单的花括号来解决它们,但直到我将其重新迁移回项目模式之后我才想到这一点...... / p>