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

我正在从给定的地理空间点查询半径内的所有节点,并想知道这些节点之间的关系,所以我正在与ways集合一起尝试从以前的地理空间查询中检索包含任何节点的所有方式。

然后,我尝试使用way字段中包含的节点ID将地理空间坐标包括在loc.coordinates文档中(由于某种原因它已经有一个loc.nodes字段为空)。连同this answer中提供的帮助,我来了以下代码:

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
}

但是最后的管道什么也没返回。
$ 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)

我想知道我在这里做错了什么,为什么mgo无法做我想做的事情。

为了完整起见,这里是测试定义:
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
      ]
   }
}

这就是我希望通过查询传递给管道的示例输出:
{
   "_id":492464922,
   "tags":{
      "maxspeed":"20",
      "surface":"asphalt",
      "highway":"residential",
      "oneway":"yes",
      "name":"Avenida 1"
   },
   "loc":{
      "type":"Polygon",
      "coordinates":[
         -83.5047254,
         10.0984515,
         -83.5052237,
         10.0987132,
         -83.5056339,
         10.0989286
      ],
      "nodes":[
         445848963,
         4844871065,
         432568566
      ]
   }
}

最佳答案

这是因为聚合管道中有错字。该运算符称为$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
}

关于mongodb - 带有聚合的mgo,使用另一个查询进行过滤和字段更改,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48492358/

10-13 04:23