我有一组文档,其中“标记”字段从空格分隔的标记列表切换到单个标记的数组。我想将以前的空格分隔字段更新为所有数组,如新的传入数据。
我也遇到了$ type选择器的问题,因为它将类型操作应用于单个数组元素,这些元素是字符串。所以按类型过滤只会返回所有内容。
如何将每个看起来像第一个示例的文档转换为第二个示例的格式?
{
"_id" : ObjectId("12345"),
"tags" : "red blue green white"
}
{
"_id" : ObjectId("54321"),
"tags" : [
"red",
"orange",
"black"
]
}
答案 0 :(得分:2)
我们无法使用$type
运算符来过滤我们的文档,因为数组中元素的类型是" string"并且如文档中所述:
当应用于数组时,$ type匹配任何指定BSON类型的内部元素。例如,当匹配$ type:' array'时,如果字段具有嵌套数组,则文档将匹配。它不会返回字段本身是数组的结果。
但幸运的是MongoDB还提供了$exists
运算符,可以在这里使用数字数组索引。
现在我们如何更新这些文件?
好吧,从MongoDB版本< = 3.2开始,我们唯一的选择是mapReduce()
,但首先让我们看看即将发布的MongoDB版本中的另一种选择。
从MongoDB 3.4开始,我们可以$project
我们的文档并使用$split
运算符将我们的字符串拆分为一个子字符串数组。
请注意,仅拆分那些"标签"这是字符串,我们需要一个逻辑$cond
ition处理来只分割字符串的值。此处的条件为$eq
,当字段的true
等于$type
时,其评估为"string"
。顺便说一句$type
这里是3.4中的新内容。
最后,我们可以使用$out
管道阶段运算符覆盖旧集合。 但我们需要明确指定在$project
阶段中包含其他字段。
db.collection.aggregate(
[
{ "$project": {
"tags": {
"$cond": [
{ "$eq": [
{ "$type": "$tags" },
"string"
]},
{ "$split": [ "$tags", " " ] },
"$tags"
]
}
}},
{ "$out": "collection" }
]
)
使用mapReduce
,我们需要使用Array.prototype.split()
在 map function 中发出子串数组。我们还需要使用"查询"来过滤我们的文档。选项。从那里我们需要迭代"结果"数组和$set
"标记"的新值使用3.2中新增的bulkWrite()
方法或现在已弃用的Bulk()
使用批量操作(如果我们在2.6或3.0上显示here.
db.collection.mapReduce(
function() { emit(this._id, this.tags.split(" ")); },
function(key, value) {},
{
"out": { "inline": 1 },
"query": {
"tags.0": { "$exists": false },
"tags": { "$type": 2 }
}
}
)['results']