具有聚合的mgo,使用另一个查询和字段更改进行过滤

时间:2018-01-28 23:08:46

标签: mongodb go aggregation-framework

我正在使用OpenStreeMap数据转储到MongoDB实例中,以下集合存在nodeswaysrelations

我正在查询给定地理空间点的半径范围内的所有节点,并了解这些节点之间的关系我正在使用ways集合尝试检索包含我之前地理空间中任何节点的所有方法查询。

然后,我试图在way文档中包含地理空间坐标(它已经有loc.coordinates字段,由于某种原因是空的),使用它在字段中包含的节点ID { {1}}。除this answer中提供的帮助外,我还得到了以下代码:

loc.nodes

但最后的管道没有任何回报。

package main

import (
    "fmt"

    mgo "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

// GeoJSON Holds data of geospatial points
type GeoJSON struct {
    Type        string    `json:"-"`
    Coordinates []float64 `json:"coordinates"`
}

type waynodes struct {
    Type        string
    Coordinates []float64
    Nodes       []int
}

// OSMNode Represet a single point in space.
// https://wiki.openstreetmap.org/wiki/Node
//
// A node is one of the core elements in the OpenStreetMap data model. It
// consists of a single point in space defined by its latitude, longitude and
// node id.  A third, optional dimension (altitude) can also be included:
// key:ele (abrev. for "elevation"). A node can also be defined as part of a
// particular layer=* or level=*, where distinct features pass over or under
// one another; say, at a bridge.  Nodes can be used to define standalone point
// features, but are more often used to define the shape or "path" of a way.
type OSMNode struct {
    ID       int                    `bson:"_id"`
    Location GeoJSON                `bson:"loc"`
    Tags     map[string]interface{} `bson:"tags,omitempty"`
}

// OSMWay Represent an ordered list of nodes
// https://wiki.openstreetmap.org/wiki/Way
//
// A way is an ordered list of nodes which normally also has at least one tag
// or is included within a Relation. A way can have between 2 and 2,000 nodes,
// although it's possible that faulty ways with zero or a single node exist. A
// way can be open or closed. A closed way is one whose last node on the way is
// also the first on that way. A closed way may be interpreted either as a
// closed polyline, or an area, or both.
//
// The nodes defining the geometry of the way are enumerated in the correct
// order, and indicated only by reference using their unique identifier. These
// nodes must have been already defined separately with their coordinates.
type OSMWay struct {
    ID       int      `bson:"_id"`
    Location waynodes `bson:"loc"`
    Tags     map[string]interface{}
}

// km2miles convert a distance in kilometers to miles and then return the
// radius of such distance.
func km2miles(dist float64) float64 {
    r := dist * 0.621371
    // https://en.wikipedia.org/wiki/Earth_radius#Fixed_radius
    return r / 3963.2
}

// nodes2list return a string list of node IDs from a list of OSMNode objects
func nodes2list(l []OSMNode) []int {
    var list []int
    for _, v := range l {
        list = append(list, v.ID)
    }
    return list
}

// GetGeoWithinPos Return all points in a given point of Earth within the
// radius of `dist`.
func (db *DB) GetGeoWithinPos(long, lat, dist float64) ([]OSMWay, error) {
    // Look at `nodes` document in our `osm` database
    c := db.m.DB("osm").C("nodes")
    // Query all nodes within a range from a spatial point: It should be
    // equivalent to:
    //     db.nodes.find(
    //         {loc:
    //          {$geoWithin:
    //           {$centerSphere: [[-83.4995983, 10.1033002], 0.186411 / 3963.2]
    //           }
    //          }
    //         }, {"_id": 1});
    var nodesresult []OSMNode
    err := c.Find(bson.M{
        "loc": bson.M{
            "$geoWithin": bson.M{
                "$centerSphere": []interface{}{
                    []interface{}{long, lat}, km2miles(dist),
                },
            },
        },
    }).Select(bson.M{"_id": 1}).All(&nodesresult)

    if err != nil {
        return nil, err
    } else if nodesresult == nil {
        return nil, fmt.Errorf("Nodes not found on %f lat, %f long in a radius of %f km", lat, long, dist)
    } else if nodesresult[0].ID == 0 {
        return nil, fmt.Errorf("Nodes incorrectly unmarshall: %#v", nodesresult[0:3])
    }

    // Prepare a pipeline
    pipe := []bson.M{
        {
            // Match only ways that contains the ID of the nodes
            // from the query on `qsn`
            "$match": bson.M{
                "loc.nodes": bson.M{
                    "$in": nodes2list(nodesresult), // Return []int
                },
            },
        },
        {
            // Now look for the nodes at `nodes` collection present
            // at `loc.nodes` field...
            "$lookup": bson.M{
                "from":         "nodes",
                "localField":   "loc.nodes",
                "foreignField": "_id",
                "as":           "loc.coordinates",
            },
        },
        {
            // ...and set the field `loc.coordinates` with the
            // coordinates of all nodes.
            "$addField": bson.M{
                "loc.coordinates": bson.M{
                    "$reduce": bson.M{
                        "input":        "$loc.coordinates.loc.coordinates",
                        "initialValue": []float64{},
                        "in":           bson.M{"$concatArrays": []string{"$$this", "$$value"}},
                    },
                },
            },
        },
    }
    // Query ways collection
    w := db.m.DB("osm").C("ways")
    var ways []OSMWay
    // Execute the pipeline 
    err = w.Pipe(pipe).All(&ways)
    if ways == nil {
        return nil, fmt.Errorf("Ways not found within %0.2f km/radius (%f mil/radius)", dist, km2miles(dist))
    }
    return ways, err
}

我想知道我在这里做错了什么,为什么mgo不能做我想做的事。

为了完整起见,这里是测试定义:

$ go test
--- FAIL: TestFetchData (1.80s)
        db_test.go:16: from -83.4995983long, 10.1033002lat: Ways not found within 1.00 km/radius (0.000157 mil/radius)

示例文件

这是func TestFetchData(t *testing.T) { db, err := NewDBConn("", "", "localhost", 27017) if err != nil { t.Fatalf("Could not establish connection with MongoDB: %s", err) } // Get data from some location in my hometown _, err := db.GetGeoWithinPos(-83.4995983, 10.1033002, 1.0) if err != nil { t.Fatalf("from -83.4995983long, 10.1033002lat: %s", err) } } 集合中的示例文档:

ways

这是来自{ "_id":492464922, "tags":{ "maxspeed":"20", "surface":"asphalt", "highway":"residential", "oneway":"yes", "name":"Avenida 1" }, "loc":{ "type":"Polygon", "coordinates":[ ], "nodes":[ 445848963, 4844871065, 432568566 ] } } 集合的示例文档:

nodes

这将是我想要返回的示例输出我正在尝试传递给管道:

{
   "_id":445848963,
   "loc":{
      "type":"Point",
      "coordinates":[
         -83.5047254,
         10.0984515
      ]
   }
}

1 个答案:

答案 0 :(得分:1)

这是因为汇总管道中存在拼写错误。该运算符名为$addFields而非$addField(缺少s)。

w.Pipe()的方法调用应该引发Unrecognized pipeline stage name: '$addField'行的错误。但是,您的代码未检查err返回的Pipe()变量。由于您只检查由于错误而为零的变量ways,因此您的方法返回(nil, "Ways not found within %0.2f km/radius (%f mil/radius)");从而屏蔽了管道错误。

我建议先检查内容检查err

err = w.Pipe(pipe).All(&ways)
if err != nil {
  //handle error
}