【问题标题】:Mongoose geoNear maxDistance ResultsMongoose geoNear maxDistance 结果
【发布时间】:2016-05-10 22:24:29
【问题描述】:

我正在使用 node.js 和猫鼬。我的数据库中有 4 个对象,坐标为 lng/lat。在现实生活中,这些位置彼此相距 1 或 2 英里。

module.exports.locationsListByDistance = function(req, res) {
    var lng = parseFloat(req.query.lng);
    var lat = parseFloat(req.query.lat);
    var point = {
        type: "Point",
        coordinates: [lng, lat]
    };
    var geoOptions =  {
        spherical: true,
        maxDistance: 20 / 3963,
        num: 10
    };
    Loc.geoNear(point, geoOptions, function(err, results, stats) {
        var locations;
        console.log('Geo Results', results);
        console.log('Geo stats', stats);
        if (err) {
            console.log('geoNear error:', err);
            sendJsonResponse(res, 404, err);
        } else {
            locations = buildLocationList(req, res, results, stats);
            sendJsonResponse(res, 200, locations);
        }
    });
};

这是我的代码。当Loc.geoNear 运行时,在回调函数results 中返回一个空数组。我可以让它真正返回对象的唯一方法是设置maxDistance: 2500,然后它将检索2个具有荒谬距离的对象。

现在显然,2500 是一个巨大的数字,这意味着零。我需要彼此相距 20 英里,这就是为什么我通过 20 / 3963 转换为弧度,但生成的 maxDistance 是如此之小,以至于 mongo 什么也没有返回。有任何想法吗?

编辑 1 (添加文档)

{
    "_id": {
        "$oid": "5714204af42b2b9004313d7c"
    },
    "name": "Starcups",
    "address": "125 High Street, Reading, RG6 1PS",
    "rating": 3,
    "facilities": [
        "Hot drinks",
        "Food",
        "Premium wifi"
    ],
    "coords": {
        "lng": -84.135166,
        "lat": 34.190996
    },
    "openingTimes": [
        {
            "days": "Monday - Friday",
            "opening": "7:00am",
            "closing": "7:00pm",
            "closed": false
        },
        {
            "days": "Saturday",
            "opening": "8:00am",
            "closing": "5:00pm",
            "closed": false
        },
        {
            "days": "Sunday",
            "closed": true
        }
    ],
    "reviews": [
        {
            "author": "Kenny Hall",
            "rating": 5,
            "timestamp": "13 April 2016",
            "reviewText": "What a great place. I can't say enough good things about it."
        },
        {
            "author": "Charlie Chaplin",
            "rating": 3,
            "timestamp": "16 June 2015",
            "reviewText": "It was okay. Coffee wasn't great, but the wifi was fast."
        }
    ]
}

EDIT 2(添加模型/模式)

var mongoose = require('mongoose');

var reviewSchema = new mongoose.Schema({
    author: String,
    rating: {
        type: Number,
        required: true,
        min: 0,
        max: 5
    },
    reviewText: String,
    createdOn: {
        type: Date,
        "default": Date.now
    }
});

var openingTimeSchema = new mongoose.Schema({
    days: {
        type: String,
        required: true
    },
    opening: String,
    closing: String,
    closed: {
        type: Boolean,
        required: true
    }
});

var locationSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    address: String,
    rating: {
        type: Number,
        "default": 0,
        min: 0,
        max: 5
    },
    facilities: [String],
    // Always store coordinates longitude, latitude order
    coords: {
        type: [Number],
        index: '2dsphere'
    },
    openingTimes: [openingTimeSchema],
    reviews: [reviewSchema]
});

mongoose.model('Location', locationSchema);

【问题讨论】:

  • 你有什么类型的索引? 2d 还是 2dsphere?
  • 另外请发布示例 MongoDB 文档
  • @Saleem 这是一个 2dsphere。我将使用文档编辑帖子。
  • @Saleem 很高兴知道这些差异,谢谢。但是,如果您在下面查看我的 cmets 到 Anonymous-SOS 答案,Mongod 仍然会返回不切实际的值。

标签: node.js mongodb mongoose


【解决方案1】:

所以最初我认为 MongoDB 是在 弧度 中工作的,所以我一直在尝试为此进行所有这些转换。但是经过更多的研究,https://docs.mongodb.com/manual/reference/command/geoNear/#dbcmd.geoNear,它说如果spherical: true 那么它使用米。

所以我只是写了一个新的转换方法,所以当我在查询中的 km 中设置 maxDistance 时,我会转换它并改用 m,现在一切效果很好!

var meterConversion = (function() {
    var mToKm = function(distance) {
        return parseFloat(distance / 1000);
    };
    var kmToM = function(distance) {
        return parseFloat(distance * 1000);
    };
    return {
        mToKm : mToKm,
        kmToM : kmToM
    };
})();

/* GET list of locations */
module.exports.locationsListByDistance = function(req, res) {
    var lng = parseFloat(req.query.lng);
    var lat = parseFloat(req.query.lat);
    var maxDistance = parseFloat(req.query.maxDistance);
    if ((!lng && lng !== 0) || (!lat && lat !== 0) || !maxDistance) {
        console.log('locationsListByDistance missing params');
        sendJsonResponse(res, 404, {
            "message" : "lng, lat and maxDistance query parameters are all required"
        });
        return;
    }
    var point = {
        type: "Point",
        coordinates: [lng, lat]
    };
    var geoOptions =  {
        spherical: true,
        maxDistance: meterConversion.kmToM(maxDistance),
        num: 10
    };
    Loc.geoNear(point, geoOptions, function(err, results, stats) {
        var locations;
        console.log('Geo Results', results);
        console.log('Geo stats', stats);
        if (err) {
            console.log('geoNear error:', err);
            sendJsonResponse(res, 404, err);
        } else {
            locations = buildLocationList(req, res, results, stats);
            sendJsonResponse(res, 200, locations);
        }
    });
};

var buildLocationList = function(req, res, results, stats) {
    var locations = [];
    results.forEach(function(doc) {
        locations.push({
            distance: meterConversion.mToKm(doc.dis), 
            name: doc.obj.name,
            address: doc.obj.address,
            rating: doc.obj.rating,
            facilities: doc.obj.facilities,
            _id: doc.obj._id
        });
    });
    return locations;
};

特别感谢@Saleem 和其他帮助我找到解决方案的人。

【讨论】:

  • 编写一个函数来将“m”转换为“km”,反之亦然,这对我来说看起来有点矫枉过正。只需distance: doc.dis / 1000
【解决方案2】:

我怀疑您的文档结构存在问题。 2dsphere 需要 GeoJSON 格式的文档。

你的文档应该是这样的

"coords" : {
    "type" : "Point",
    "coordinates" : [ 
        -84.135166,
        34.190996
    ]
}

假设您的文档是:

{
  "name": "Starcups",
  "address": "125 High Street, Reading, RG6 1PS",
  "rating": 3,
  "facilities": [
    "Hot drinks",
    "Food",
    "Premium wifi"
  ],
  "location": {
    "type": "Point",
    "coordinates": [
      -84.135166,
      34.190996
    ]
  },
  "openingTimes": [
    {
      "days": "Monday - Friday",
      "opening": "7:00am",
      "closing": "7:00pm",
      "closed": false
    },
    {
      "days": "Saturday",
      "opening": "8:00am",
      "closing": "5:00pm",
      "closed": false
    },
    {
      "days": "Sunday",
      "closed": true
    }
  ],
  "reviews": [
    {
      "author": "Kenny Hall",
      "rating": 5,
      "timestamp": "13 April 2016",
      "reviewText": "What a great place. I can't say enough good things about it."
    },
    {
      "author": "Charlie Chaplin",
      "rating": 3,
      "timestamp": "16 June 2015",
      "reviewText": "It was okay. Coffee wasn't great, but the wifi was fast."
    }
  ]
}

您正在尝试查找距点[ -84.105146 , 34.190096 ] 5000 米内的所有文档

db.collection.aggregate(
[
    {
        '$geoNear': {
            'near': {
                'type': 'Point',
                'coordinates': [ -84.105146 , 34.190096 ]
            },
            'spherical': true, 
            'distanceField': 'dist',
            'maxDistance': 5000         
        }
    }
]);

如果您在location 上创建了2dsphere 索引,上面的查询将返回上面发布的文档,因为它位于半径5000 米内

{ 
    "_id" : ObjectId("57327c856b37a28ec9221392"), 
    "name" : "Starcups", 
    "address" : "125 High Street, Reading, RG6 1PS", 
    "rating" : 3.0, 
    "facilities" : [
        "Hot drinks", 
        "Food", 
        "Premium wifi"
    ], 
    "location" : {
        "type" : "Point", 
        "coordinates" : [
            -84.135166, 
            34.190996
        ]
    }, 
    "openingTimes" : [
        {
            "days" : "Monday - Friday", 
            "opening" : "7:00am", 
            "closing" : "7:00pm", 
            "closed" : false
        }, 
        {
            "days" : "Saturday", 
            "opening" : "8:00am", 
            "closing" : "5:00pm", 
            "closed" : false
        }, 
        {
            "days" : "Sunday", 
            "closed" : true
        }
    ], 
    "reviews" : [
        {
            "author" : "Kenny Hall", 
            "rating" : 5.0, 
            "timestamp" : "13 April 2016", 
            "reviewText" : "What a great place. I can't say enough good things about it."
        }, 
        {
            "author" : "Charlie Chaplin", 
            "rating" : 3.0, 
            "timestamp" : "16 June 2015", 
            "reviewText" : "It was okay. Coffee wasn't great, but the wifi was fast."
        }
    ], 
    "dist" : 2766.0558990550367
}

【讨论】:

  • 这里面,好像latlng之前,对吗?
  • 我的 lat 是 34,long 是 -84。那就是"coordinates" : [ -84.1401930, 34.2073200 ]
  • 在我的模型/模式中,我有 coords: { type: [Number], index: '2dsphere' } 我是否还需要更改它?因为我将数据库中的文档更改为您在上面发布的格式,而我的 geoNear 查询仍然返回数以千计的距离。
  • 我编辑了我的原始帖子。底部是我的架构。所以我需要改变它的一部分,对吗?
  • 查看我更新的帖子。希望它会回答你的问题。有关如何创建 2d 索引,请参阅stackoverflow.com/questions/17485036/…
【解决方案3】:

检查您的 mongod 配置中的 distanceMultiplier

您也可以使用 geoNear 的 distanceMultiplier 选项在 mongod 进程中转换弧度,而不是在您的应用程序代码中。见距离乘数。

【讨论】:

  • 我使用了 distanceMultiplier 的英里值 3963,并且没有使用控制器逻辑来转换弧度,但它给出了类似的反馈。
  • 好的。我仍然认为它是以米为单位的,这就是为什么你在 2500 及以上获得良好结果的原因。
  • 没有maxDistancedistanceMultiplier,数据库返回所有对象,它们的值超过2,000(即2386.126、2488.271 等)。因此,尽管我读过所有关于 Mongo 以弧度工作的信息,但它似乎在其他一些单位中工作。
  • distanceMultiplier 仅在使用 2d 索引时使用。
  • 看看这个example。它使用球形,距离以米为单位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
  • 2017-07-18
  • 2015-04-23
  • 2011-11-28
  • 2017-03-19
  • 2014-08-09
  • 1970-01-01
相关资源
最近更新 更多