我有以下两种模式:
var SchemaOne = new mongoose.Schema({
id_headline: { type: String, required: true },
tags: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tag' }]
});
var tagSchema = new mongoose.Schema({
_id: { type: String, required: true, index: { unique: true } }, // value
name: { type: String, required: true }
});
如您所见,在第一个模式中有一个对第二个模式的引用数组。
我的问题是:
假设在我的后端服务器中,我收到一个标签数组(只是id),并且在创建SchemaOne文档之前,我需要验证收到的标签是否已存在于数据库中,如果没有,则创建它们。只有在将所有标记存储在数据库中之后,我才可以将此接收的数组分配给要创建的SchemaOne文档的标记数组。
我不确定如何实现这个?你能伸出援助之手吗?
答案 0 :(得分:1)
因此,我们假设您已将输入发送到您的服务器,基本上解析为:
var input = {
"id_headline": "title",
"tags": [
{ "name": "one" },
{ "name": "two" }
]
};
正如您所说,您不确定是否存在任何“标签”条目,但当然“名称”对于查找关联对象也是唯一的。
您基本上要做的就是“查找”“标签”中的每个元素,并将文档与“标签”模型中的对象一起使用。这里理想的方法是.findOneAndUpdate()
,“upsert”选项设置为true
。这将在未找到它的集合中创建文档,并且无论如何都将使用创建的引用返回文档内容。
请注意,在保存主“SchemaOne”对象之前,您希望确保在首先解析这些数组项。 async
库有一些方法可以帮助构建它:
async.waterfall(
[
function(callback) {
async.map(input.tags,function(tag,callback) {
Tag.findOneAndUpdate(
{ "name": tag.name },
{ "$setOnInsert": { "name": tag.name } },
{ "upsert": true, "new": true },
callback
)
},callback);
},
function(tags,callback) {
Model.findOneAndUpdate(
{ "id_headline": input.id_headline },
{ "$addToSet": {
"tags": { "$each": tags.map(function(tag) { return tag._id }) }
}},
{ "upsert": true, "new": true },
callback
)
}
],
function(err,result) {
// if err then do something to report it, otherwise it's done.
}
)
因此async.waterfall
是一种特殊的流控制方法,它将从参数数组中指定的每个函数返回的结果传递给下一个,直到执行结束,您可以选择传入列表中最终函数的结果。它基本上是“级联”或“瀑布”结果到每一步。这是希望将“标签”创建的结果传递给主模型创建/修改。
第一个执行阶段中的async.map
查看输入数组中的每个元素。因此,对于“tags”中包含的每个项目,调用.findOneAndUpdate()
方法以查找并可能创建(如果未找到)集合中指定的“tag”条目。
由于.map()
的输出将是这些文档的数组,因此它只是传递到下一个阶段。因此,每次迭代都会返回一个文档,当迭代完成后,您将获得所有文档。
.findOneAndUpdate()
与“upsert”的下一个用法是可选的,当然认为具有匹配“id_headline”的文档可能存在也可能不存在。同样的情况也是如此,如果它在那里则处理“更新”,如果不存在则则简单地创建它。如果知道文档不存在,您可以选择.insert()
或.create()
,但“更新”操作会提供一些有趣的选项。
这里是$addToSet
的用法,如果文档已经存在,那么指定的项目将“添加”到已经存在的任何内容,当然还有“set”,任何项目已经存在礼物不会是新增的。请注意,当使用原子运算符添加到数组时,此处仅需要_id
字段,因此使用.map()
函数。
关于“更新”的另一种情况可能是使用$set
原子操作简单地“替换”数组内容,如果它意图只存储输入中提到的那些项而不是其他项。< / p>
以类似的方式,在“标签”中“创建”/“查找”项目时显示的$setOnInsert
确保在“创建/插入”对象时只有实际的“修改”,并且删除服务器上的一些写入开销。
因此,至少对“标签”条目使用.findOneAndUpdate()
的基本原则是处理此问题的最佳方式。这避免了双重处理,例如:
这意味着对来回通信的数据库进行两次操作,使用"upserts"的操作简化为每个项目的单个请求。