【问题标题】:How create a new array field with the aggregate framework如何使用聚合框架创建新的数组字段
【发布时间】:2014-07-20 14:43:05
【问题描述】:

我开始使用 mongoDb,但遇到了一个简单的用例。

假设我有一个集合“aCollection”,其中包含如下条目:

{ 
    _id: ObjectId(123),
    lat: 48,56623,
    long: 2,56332
}

我想用这样的条目创建一个新集合:

{ 
    _id: ObjectId(123),
    lat: 48,56623,
    long: 2,56332,
    geometry : { 
        type: "Point",
        coordinates: [48,56623, 2,56332]
    }
}

我想到了聚合框架:

db.aCollection.aggregate([{$project: { 
    _id: 1,
    lat: 1,
    long: 1,
    geometry: { 
        type: {$concat: ["Point"]},
        coordinates: ["$lat", "$long"]
    }
}}])

但它不起作用,我得到了这个异常:

“异常:对象表达式中不允许的字段类型数组(在'坐标'处)”

以下聚合正在运行,但未产生预期结果:

db.aCollection.aggregate([{$project: { 
    _id: 1,
    lat: 1,
    long: 1,
    geometry: { 
        type: {$concat: ["Point"]},
        coordinates: "$lat"
    }
}}])

您将如何继续创建此集合 1) 用聚合框架 2) 没有聚合框架

谢谢

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您不需要聚合功能。 findforEachinsert 是一种可能的方法:

    db.aCollection.find().forEach( function(myDoc) {
      myDoc.geometry = {type: "Point", coordinates: [myDoc.lat, myDoc.long]};
      db.newCollection.insert(myDoc);
    });
    

    它为每个文档调用一个单独的插入,但如果你有一个小集合,它又快又脏。

    【讨论】:

      【解决方案2】:

      好的,首先让我们了解聚合框架的作用。它只查询可用数据并返回结果。它不会修改原始文件!这就是您编写的第二段代码执行聚合、投影并显示聚合查询成功运行的一些结果的原因。

      要进行您尝试做的更改,您可以尝试 Martin Konecny 的回答中的“快速而肮脏”的方法,也可以修改用于上传数据的代码并执行新的上传。

      由于我看到您在纬度/经度值中使用逗号而不是使用小数点,所以我想说,如果稍后您将尝试查询此地理空间数据,您最好再次正确上传。

      【讨论】:

        【解决方案3】:

        在现代 MongoDB 版本中,最有效的方法是使用现有文档属性简单地标记数组。 MongoDB 3.2 中引入了数组的直接表示法:

        db.collection.aggregate([
          { "$project": {
            "lat": 1,
            "long": 1,
            "geometry": {
              "type": { "$literal": "Point" },
              "coordinates": [ "$lat", "$long" ]
            }
          }},
          { "$out": "newcollection" }
        ])
        

        或者甚至使用$addFields 简单地将新属性“附加”到文档中:

        db.collection.aggregate([
          { "$addFields": {
            "geometry": {
              "type": { "$literal": "Point" },
              "coordinates": [ "$lat", "$long" ]
            }
          }},
          { "$out": "newcollection" }
        ])
        

        如果您使用的是 MongoDB 2.6 及更高版本,则可以使用聚合框架执行此操作,并避免在客户端程序中循环结果以创建新集合。

        这里帮助您的主要功能是$out 运算符,用于将输出发送到新集合。但也要有点聪明才能创建您需要的数组。

        db.collection.aggregate([
            { "$project": {
                "lat": 1,
                "long": 1,
                "type": { "$literal": ["lat","long"] }
            }},
            { "$unwind": "$type" },
            { "$group": {
                "_id": "$_id",
                "lat": { "$first": "$lat" },
                "long": { "$first": "$long" },
                "coordinates": {
                    "$push": {
                        "$cond": [
                            { "$eq": [ "$type", "lat" ] },
                            "$lat",
                            "$long"
                        ]
                    }
                }
            }},
            { "$project": {
                "lat": 1,
                "long": 1,
                "geometry": { 
                    "type": { "$literal": "Point" },
                    "coordinates": "$coordinates"
                }
            }},
            { "$out": "newcollection" }
        ])
        

        因此,这使用了$literal 运算符,以便在管道的头部指定一个新数组。该运算符会将内容完全按照提供的方式放入文档属性中。因此不允许变量替换,因此是“字面量”。

        为了创建“coordintes”数组,我们简单地展开第一个数组,该数组本质上创建了两个在“type”中具有不同值的文档。然后在$group 阶段使用它来有条件地$push 将“$lat”或“$long”值添加到该数组中。

        最后再次使用$project 完成文档结构,然后$out 将所有输出发送到新集合。


        请注意,这仅在您打算创建新集合并避免“通过网络”发送流量时才有意义。这不能纯粹在聚合框架中使用来重新塑造您的文档,以便在同一聚合管道中执行“地理空间”查询,因为“地理空间”查询仅在实际索引集合时才有效.

        因此,这可能会帮助您根据需要创建一个新集合,但至少它可以作为示例(或实际上是两个示例)说明如何使用聚合框架从不同的值中创建一个数组。

        【讨论】:

        • 谢谢!到目前为止非常有帮助:) 如果人们担心为不存在的字段创建“null”值,我们总是可以使用“$filter”将它们过滤掉。
        【解决方案4】:

        看起来 MongoDB 3.2 提供了相当简单和优雅的方式来使用聚合框架创建 GeoJSON 点。

        我们必须每天两次对大约 200 万条记录执行转换,因此聚合框架是最快且可能最可靠的方法。

        下面是一个 Mongoose 示例,说明如何将地理定位数据从具有经度/纬度的集合转换为具有 GeoJSON 点的集合。

        Locations
            .aggregate([
                {
                    $project : {
                        _id: 0,
                        "location": {
                            "type": { $literal: "Point" }, 
                            "coordinates": ["$longitude", "$latitude"]
                        }
                    }
                },
                {
                    $out : 'test_1'
                }])
            .exec(function(err,data) {
                if (err) {
                    console.error(err);
                } else {
                    console.log("Done transforming.");
                }
            });
        

        【讨论】:

          猜你喜欢
          • 2022-10-12
          • 1970-01-01
          • 2013-03-19
          • 2013-10-23
          • 1970-01-01
          • 2020-04-08
          • 2013-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多