我很难'插入'到我的阵列。下面的代码在我的answers
数组中创建了一些我绝对不想要的重复项,现在很明显$push
将不起作用。我已经尝试过使用我在SO上看到的不同方法一段时间,但没有一个对我有用。使用此网络应用程序,用户可以在网站上查看问题,并回答“是”或“否”response
,并允许他们在任何时候更改(插入)他们的response
意思是在不同的时间在db上发生一种upsert。怎么绕过这个?
var QuestionSchema = Schema ({
title :String,
admin :{type: String, ref: 'User'},
answers :[{type: Schema.Types.Mixed, ref: 'Answer'}]
});
var AnswerSchema = Schema ({
_question :{type: ObjectId, ref: 'Question'},
employee :{type: String, ref: 'User'},
response :String,
isAdmin :{type: Boolean, ref: 'User'}
})
var UserSchema = Schema({
username : String,
isAdmin : {type: Boolean, default: false}
});
module.exports = mongoose.model('Question', QuestionSchema);
module.exports = mongoose.model('Answer', AnswerSchema);
module.exports = mongoose.model('User', UserSchema);
Question.update(
{_id: req.body.id},
{$push: {answers: {_question: req.body.id,
employee: req.body.employee,
response: req.body.response, //this variable changes (yes/no/null)
isAdmin: req.body.isAdmin}}},
{safe: true, upsert: true},
function(err, model) {
}
);
答案 0 :(得分:2)
在我看来,你似乎有点困惑,它反映在你的架构中。你似乎没有完全掌握"嵌入式"之间的差异。和"引用"因为你的架构实际上是无效的#34; mash"这两种技术。
可能最好引导您完成这两项工作。
因此,您应该使用更多类似的内容来代替您定义的架构:
var QuestionSchema = Schema ({
title :String,
admin :{type: String, ref: 'User'},
answers :[AnswerSchema]
});
var AnswerSchema = Schema ({
employee :{type: String, ref: 'User'},
response :String,
isAdmin :{type: Boolean, ref: 'User'}
})
mongoose.model('Question', questionSchema);
注意:Question
是唯一实际模型。 AnswerSchema
完全"嵌入"。
注意"架构的明确定义"将"answers"
中的Question
属性定义为"数组" AnswerSchema
。这就是你如何嵌入并保持对数组内对象内类型的控制。
至于更新,有一个明确的逻辑模式,但你只是没有强制执行它。你需要做的就是"告诉"您不希望"推送"的更新一个新项目,如果有的东西"唯一"数组中的"employee"
已经存在。
另外。这是 NOT 和" upsert"。 Upsert暗示"创建一个新的",这与你想要的不同。你想"推"到现有的"阵列题。如果你离开" upsert"在那里,然后找不到的东西创建一个新的问题。这当然是错的。
Question.update(
{
"_id": req.body.id,
"answers.employee": { "$ne": req.body.employee },
}
},
{ "$push": {
"answers": {
"employee": req.body.employee,
"response": req.body.response,
"isAdmin": req.body.isAdmin
}
}},
function(err, numAffected) {
});
那将会检查" unique"数组成员中的"employee"
已经只有$push
,但它不在那里。
作为奖励,如果您打算允许用户更改他们的答案"然后我们用.bulkWrite()
:
Question.collection.bulkWrite(
[
{ "updateOne": {
"filter": {
"_id": req.body.id,
"answers.employee": req.body.employee,
},
"update": {
"$set": {
"answers.$.response": req.body.response,
}
}
}},
{ "updateOne": {
"filter": {
"_id": req.body.id,
"answers.employee": { "$ne": req.body.employee },
},
"update": {
"$push": {
"answers": {
"employee": req.body.employee,
"response": req.body.response,
"isAdmin": req.body.isAdmin
}
}
}
}}
],
function(err, writeResult) {
}
);
这有效地将两个更新合二为一。第一个尝试更改现有答案并$set
匹配位置的响应,第二个尝试添加新答案,但未在问题中找到答案。
使用"引用"模型你实际上拥有自己集合中Answer
的真实成员。因此,架构的定义如下:
var QuestionSchema = Schema ({
title :String,
admin :{type: String, ref: 'User'},
answers :[{ type: Schema.Types.ObjectId, ref: 'Answer' }]
});
var AnswerSchema = Schema ({
_question :{type: ObjectId, ref: 'Question'},
employee :{type: String, ref: 'User'},
response :String,
isAdmin :{type: Boolean, ref: 'User'}
})
mongoose.model('Answer', answerSchema);
mongoose.model('Question', questionSchema);
N.B 其他参考资料来自User
,例如:
employee :{type: String, ref: 'User'},
isAdmin :{type: Boolean, ref: 'User'}
这些也是非常不正确的,也应该是Schema.Type.ObjectId
,因为他们将"参考" _id
的实际User
字段。但这实际上超出了这个问题的范围,所以如果你在阅读之后仍然没有理解,那么Ask a New Question所以有人可以解释。接下来是答案。
那是"将军"虽然模式的形状,重要的是"ref"
"模型" {,它是注册名称。你可以选择使用现代猫鼬版本中的'Anwser'
字段和#34;虚拟"但我跳过" Adavanced Usage"现在,通过一系列"引用来保持简单"仍然在"_question"
模型中。
在这种情况下,由于Question
模型实际上在其自己的"集合"中,因此操作实际上变为" upserts"。我们只想创造"创造"如果对给定的Answer
ID没有"employee"
响应。
同时使用Promise
链进行演示:
"_question"
这实际上是一个简单的陈述,因为"当匹配" 我们希望用请求的有效负载更改Answer.update(
{ "_question": req.body.id, "employee": req.body.employee },
{
"$set": {
"response": req.body.reponse
},
"$setOnInsert": {
"isAdmin": req.body.isAdmin
}
},
{ "upsert": true }
).then(resp => {
if ( resp.hasOwnProperty("upserted") ) {
return Question.update(
{ "_id": req.body.id, "answers": { "$ne": resp.upserted[0]._id },
{ "$push": { "answers": resp.upserted[0]._id } }
).exec()
}
return;
}).then(resp => {
// either undefined where it was not an upsert or
// the update result from Question where it was
}).catch(err => {
// something
})
数据时,实际上只有当" upserting" 或"创建/插入"当我们实际更改其他数据时,例如"response"
(对于查询表达式的一部分始终隐含创建)和"employee"
明显不应随每个更新请求更改,我们将明确使用{ {3}}所以仅将这两个字段写入实际的"创建"。
在"承诺链"我们实际上是要查看对"isAdmin"
的更新请求是否实际上导致" upsert",当它发生时我们想要附加到Answer
的数组中它还没有存在。与"嵌入式"大致相同。例如,在使用" update"进行修改之前,最好先查看数组是否确实包含该项目。或者,您可以$setOnInsert
在这里,让查询与Question
Question
匹配。但对我来说,这是一个浪费的写作。
这些是你处理这个问题的不同方法。每个都有自己的用例,你可以在其中看到我的一些其他答案的总摘要:
$addToSet
概述了背后的不同方法和原因
Mongoose populate vs object nesting更详细地介绍了"独特阵列upserts" 技术,用于"嵌入式"模型。
不是"必需"阅读,但它可能有助于扩展您对哪种方法最适合您的特定情况的洞察力。
复制它们并将它们放在一个目录中并执行_id
以安装本地依赖项。代码将运行并在数据库中创建集合进行更改。
使用npm install
打开日志记录,因此您应该查看控制台输出并查看它的作用,以及生成的集合,其中答案将记录到相关问题中,并覆盖而不是"复制"那也是意图。
如果必须,请更改连接字符串。但这就是为了它的目的,你应该在这个列表中改变。答案中描述的两种方法都得到了证明。
<强>的package.json 强>
mongoose.set(debug,true)
<强> index.js 强>
{
"name": "colex",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"async": "^2.4.1",
"mongodb": "^2.2.29",
"mongoose": "^4.10.7"
}
}
答案 1 :(得分:0)
在Neill更新他的答案(我使用$pull
&amp; $push
)之前,我的快速解决方法。像他一样工作,但我会标记他的正确,因为我相信它更有效率。
Question.update(
{_id: req.body.id},
{$pull: {answers: { employee: req.body.employee}}},
{safe: true, multi:true, upsert: true},
function(err, model) {
}
);
Question.update(
{_id: req.body.id},
{$push: {answers: {_question: req.body.id,
employee: req.body.employee,
response: req.body.response,
isAdmin: req.body.isAdmin}}},
{safe: true, upsert: true},
function(err, model) {
}
);