将Mongo中的某些字段从String转换为Array

时间:2016-08-20 02:45:58

标签: mongodb mapreduce mongodb-query aggregation-framework

我有一组文档,其中“标记”字段从空格分隔的标记列表切换到单个标记的数组。我想将以前的空格分隔字段更新为所有数组,如新的传入数据。

我也遇到了$ type选择器的问题,因为它将类型操作应用于单个数组元素,这些元素是字符串。所以按类型过滤只会返回所有内容。

如何将每个看起来像第一个示例的文档转换为第二个示例的格式?

{
    "_id" : ObjectId("12345"),
    "tags" : "red blue green white"
}
{
    "_id" : ObjectId("54321"),
    "tags" : [
        "red",
        "orange",
        "black"
    ]
}

1 个答案:

答案 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']