node.js - 猫鼬保存多级嵌套对象
问题描述
我尝试实现我的第一个基于猫鼬的 REST API。
我现在尝试了几天,但无法启动并运行它。我想用一组控件保存调查,并为每个控件保存一组 controlProperties。
在不同的场景中,我用控件数组保存调查但没有 controlProperties,有时甚至没有控件数组。
有人可以帮我理解我的错误吗?
非常感谢。
结构如下:
调查 -- 控件数组 -- controlProperty 数组
我的架构文件是:
调查.js
const mongoose = require('mongoose');
const Control = require('./control');
const surveySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
min: 4,
max: 255
},
description: {
type: String,
required: false,
max: 1000
},
closeDate: {
type: Date,
required: false
},
controls: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Control' }]
});
module.exports = mongoose.model('Survey', surveySchema);
控制.js
const mongoose = require('mongoose');
const Survey = require('./survey');
const controlSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
survey: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'},
controlType: {
type: String,
required: true
},
name: {
type: String,
required: true
},
isInput: {
type: Boolean,
required: true
},
order: {
type: Number,
required: true
},
controlProperties: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ControlProperty' }]
});
module.exports = mongoose.model('Control', controlSchema);
控制属性.js
const mongoose = require('mongoose');
const Control = require('./control');
mongoose.Schema.Types.String.checkRequired(v => v != null);
const controlPropertySchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
control: { type: mongoose.Schema.Types.ObjectId, ref: 'Control' },
propertyName: {
type: String,
required: true
},
propertyValue: {
type: String,
required: true
},
order: {
type: Number,
required: true
}
})
module.exports = mongoose.model('ControlProperty', controlPropertySchema);
我接收帖子数据的 node.js 代码是这样的:
/routes/survey.js
router.post("/", (req, res, next) => {
Survey.find({ _id: req.body._id })
.exec()
.then(result => {
if (result.length >= 1) {
return res.status(409).json({
message: "Survey exists"
});
} else {
const survey = new Survey({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
description: req.body.description,
closeDate: req.body.closeDate,
order: req.body.order
});
let controlData = req.body.controls;
let arControls = [];
if(controlData != null) {
for (var i = 0, clen = controlData.length; i < clen; i++) {
let c = controlData[i];
let control = new Control({
_id: new mongoose.Types.ObjectId(),
controlType: c.controlType,
name: c.name,
isInput: c.isInput,
order: c.order
})
let controlPropertyData = c.controlProperties;
let arControlProperty = [];
if(controlPropertyData != null) {
for (var j = 0, cplen = controlPropertyData.length; j < cplen; j++) {
let cp = controlPropertyData[j];
let controlProperty = new ControlProperty({
_id: new mongoose.Types.ObjectId(),
propertyName: cp.propertyName,
propertyValue: cp.propertyValue,
order: cp.order
})
arControlProperty.push(controlProperty);
}
ControlProperty.insertMany(arControlProperty, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
console.log(" " + j + " controlProperties for control " + i + " saved");
control.controlProperties = data;
console.log(data);
});
}
arControls.push(control);
}
Control.insertMany(arControls, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
survey.controls = data;
console.log("controls saved");
console.log(data);
});
}
survey
.save()
.then(result => {
console.log("survey saved");
res.status(201).json(survey);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
}
});
});
示例帖子数据:
{
"name": "TestSurvey",
"description": "This is a test survey",
"controls": [
{
"controlType": "Label",
"name": "Label1",
"isInput": false,
"order": 1,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Large",
"order": 1
},
{
"propertyName": "BackgroundColor",
"propertyValue": "Darkgreen",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "Fill",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "TextColor",
"propertyValue": "White",
"order": 6
},
{
"propertyName": "Text",
"propertyValue": "Paris Work-Life Balance",
"order": 7
}
]
},
{
"controlType": "Label",
"name": "Label2",
"isInput": false,
"order": 2,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Medium",
"order": 1
},
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "Text",
"propertyValue": "Dear [[FirstName]], \nwas your workload on the case 12345 - 67(Company) compliant to the BCG Work Life Balance Ground Rules over the past week ?",
"order": 6
}
]
},
{
"controlType": "PWLBControl",
"name": "PWLB1",
"isInput": true,
"order": 3,
"controlProperties": [
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 1
}
]
},
{
"controlType": "Button",
"name": "button1",
"isInput": false,
"order": 4,
"controlProperties": [
{
"propertyName": "Text",
"propertyValue": "Submit",
"order": 1
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 2
},
{
"propertyName": "IsSubmitButton",
"propertyValue": true,
"order": 3
}
]
},
{
"controlType": "Image",
"name": "image1",
"isInput": false,
"order": 5,
"controlProperties": [
{
"propertyName": "Source",
"propertyValue": "",
"order": 1
},
{
"propertyName": "VerticalOptions",
"propertyValue": "End",
"order": 2
}
]
}
]
}
解决方案
无需为 controlProperties 和控件保留单独的集合。可以嵌入controlPropertySchema
里面controlSchema
,也可以嵌入controlSchema
里面surveySchema
。所以最后我们将只有一个集合进行调查。
这将使在一次插入操作中创建调查成为可能。您还可以在一次读取操作中获取所有调查信息。
另外还有几点建议:
- 最好不要将 _id 字段添加到模式中,mongodb 会处理它。
- 如果存在具有给定 _id 的调查,我看到您使用它。最好使用
name
字段来检查调查是否已经存在。 min
和max
选项用于数字类型,用于字符串类型minlength
和maxlength
使用。文档
所以surveySchema
必须看起来像这样:
const mongoose = require("mongoose");
const controlPropertySchema = new mongoose.Schema({
// _id: mongoose.Schema.Types.ObjectId,
// control: { type: mongoose.Schema.Types.ObjectId, ref: "Control" },
propertyName: {
type: String,
required: true
},
propertyValue: {
type: String,
required: true
},
order: {
type: Number,
required: true
}
});
const controlSchema = new mongoose.Schema({
//_id: mongoose.Schema.Types.ObjectId,
// survey: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'},
controlType: {
type: String,
required: true
},
name: {
type: String,
required: true
},
isInput: {
type: Boolean,
required: true
},
order: {
type: Number,
required: true
},
controlProperties: [controlPropertySchema]
//controlProperties: [{ type: mongoose.Schema.Types.ObjectId, ref: "ControlProperty" }]
});
const surveySchema = mongoose.Schema({
// _id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
minlength: 4,
maxlength: 255
},
description: {
type: String,
required: false,
maxlength: 1000
},
closeDate: {
type: Date,
required: false
},
controls: [controlSchema]
// controls: [{ type: mongoose.Schema.Types.ObjectId, ref: "Control" }]
});
module.exports = mongoose.model("Survey", surveySchema);
现在我们可以使用这个发布路径创建一个调查:(请注意,我们不进行任何转换,因为我们的请求正文的结构与surveySchema相同)
router.post("/surveys", async (req, res) => {
try {
let survey = await Survey.findOne({ name: req.body.name });
if (survey) {
return res.status(400).send("A survey already exists with that name");
}
const result = await Survey.create(req.body);
res.send(result);
} catch (err) {
console.log(err);
if (err.name === "ValidationError") {
return res.status(400).send(err.errors);
}
res.status(500).send("Something went wrong");
}
});
在您的请求正文中,有一个空的propertyValue
,所以我将其更改为"propertyValue": "I was empty"
,还有一个布尔值而不是字符串,所以我将其更改为"propertyValue": "I was true"
您可以使用此更正的请求正文:
{
"name": "TestSurvey",
"description": "This is a test survey",
"controls": [
{
"controlType": "Label",
"name": "Label1",
"isInput": false,
"order": 1,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Large",
"order": 1
},
{
"propertyName": "BackgroundColor",
"propertyValue": "Darkgreen",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "Fill",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "TextColor",
"propertyValue": "White",
"order": 6
},
{
"propertyName": "Text",
"propertyValue": "Paris Work-Life Balance",
"order": 7
}
]
},
{
"controlType": "Label",
"name": "Label2",
"isInput": false,
"order": 2,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Medium",
"order": 1
},
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "Text",
"propertyValue": "Dear [[FirstName]], \nwas your workload on the case 12345 - 67(Company) compliant to the BCG Work Life Balance Ground Rules over the past week ?",
"order": 6
}
]
},
{
"controlType": "PWLBControl",
"name": "PWLB1",
"isInput": true,
"order": 3,
"controlProperties": [
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 1
}
]
},
{
"controlType": "Button",
"name": "button1",
"isInput": false,
"order": 4,
"controlProperties": [
{
"propertyName": "Text",
"propertyValue": "Submit",
"order": 1
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 2
},
{
"propertyName": "IsSubmitButton",
"propertyValue": "I was true",
"order": 3
}
]
},
{
"controlType": "Image",
"name": "image1",
"isInput": false,
"order": 5,
"controlProperties": [
{
"propertyName": "Source",
"propertyValue": "I was empty",
"order": 1
},
{
"propertyName": "VerticalOptions",
"propertyValue": "End",
"order": 2
}
]
}
]
}
推荐阅读
- python - Python 使用 map 和 os.remove 删除多个文件
- javascript - 如何在基类中实现 ES6 javascript 单例函数
- azure-devops - 无法从 Azure DevOps Build Pipeline 中的 Azure PowerShell 任务将 Key Vault 机密作为纯文本读取
- html - Flexbox - 连续两列
- python - 无法登录 Django 测试
- python - 如何找到整个测试套件在 Pytest 中完成所需的时间
- java - 如何在主线程中创建一个具有无限循环的活动 JFrame?
- mysql - 如何将假期插件安装到圆形立方体?
- python - 将列值更改为不重复的列名,并将列表 od 值附加到每个单元格
- laravel - 从资源文件夹中获取图像 - Laravel