MongoDB:使用$geoNear查询时未返回所有结果

7

I got this query :

 exports.search = (req, res) => {

  let lat1 = req.body.lat;
  let lon1 = req.body.lng;
  let page = req.body.page || 1;
  let perPage = req.body.perPage || 10;
  let radius = req.body.radius || 100000; // This is not causing the issue, i can remove it and the issue is still here


  var options = { page: page, limit: perPage, sortBy: { updatedDate: -1 } }

  let match = {}

  var aggregate = null;

  if (lat1 && lon1) {

    aggregate = Tutor.aggregate([
      {
        "$geoNear": {
          "near": {
            "type": "Point",
            "coordinates": [lon1, lat1]
          },
          "distanceField": "distance", // this calculated distance will be compared in next section
          "distanceMultiplier": 0.001,
          "spherical": true,
          "key": "loc",
          "maxDistance": radius
        }
      },
      {
        $match: match
      },
      { "$addFields": { "islt": { "$cond": [{ "$lt": ["$distance", "$range"] }, true, false] } } },
      { "$match": { "islt": true } },
      { "$project": { "islt": 0 } }
    ])
    // .allowDiskUse(true);
  } else {
    aggregate = Tutor.aggregate([
      {
        $match: match
      }
    ]);
  }



  Tutor
    .aggregatePaginate(aggregate, options, function (err, result, pageCount, count) {

      if (err) {
        console.log(err)
        return res.status(400).send(err);
      }
      else {

        var opts = [
          { path: 'levels', select: 'name' },
          { path: 'subjects', select: 'name' },
          { path: 'assos', select: 'name' }
        ];
        Tutor
          .populate(result, opts)
          .then(result2 => {
            return res.send({
              page: page,
              perPage: perPage,
              pageCount: pageCount,
              documentCount: count,
              tutors: result2
            });
          })
          .catch(err => {
            return res.status(400).send(err);
          });
      }
    })
};

查询应该检索给定范围内(这是教师模型的一个字段,以公里为单位的整数,表示教师愿意移动的距离)的所有教师,并围绕某个位置(lat1, lon1)。问题在于并非所有文档都被返回。经过多次测试,我注意到只有距离位置约7.5公里以下的导师会被返回,而不是其他人。即使导师距离位置10公里,且范围为15公里,也不会被返回,因为它距离7.5公里更远。我已尝试在两个教师之间(一个被返回,另一个不被返回但应该被返回)切换位置,以查看是否仅此导致了问题,结果确实如此。在我交换他们的位置(lng和loc)后,先前被返回的不再被返回,反之亦然。我真的不明白为什么会发生这种情况。此外,我知道结果大小小于16MB,因为即使使用allowDiskUse:true,我也没有得到所有结果。如果您对我无法获得所有结果的原因有任何其他想法,请不要犹豫!谢谢!PS:这是关于教师模型及其相关字段(loc)的一部分:
import mongoose from 'mongoose';
import validate from 'mongoose-validator';
import { User } from './user';
import mongooseAggregatePaginate from 'mongoose-aggregate-paginate';

var ObjectId = mongoose.Schema.Types.ObjectId;


var rangeValidator = [
    validate({
        validator: (v) => {
            v.isInteger && v >= 0 && v <= 100;
        },
        message: '{VALUE} is a wrong value for range'
    })
];



var tutorSchema = mongoose.Schema({
    fullName: {
        type: String,
        trim: true,
        minlength: [1, 'Full name can not be empty'],
        required: [true, 'Full name is required']
    },
    location: {
        address_components: [
            {
                long_name: String,
                short_name: String,
                types: String
            }
        ],
        description: String,
        lat: Number,
        lng: Number

    },
    loc: {
        type: { type: String },
        coordinates: []
    },


});

tutorSchema.plugin(mongooseAggregatePaginate);
tutorSchema.index({ "loc": "2dsphere" });
var Tutor = User.discriminator('Tutor', tutorSchema);


module.exports = {
    Tutor
};

用户模型正在使用两个索引。一个是ID,另一个是这个;
db['system.indexes'].find() Raw Output
{
  "v": 2,
  "key": {
    "loc": "2dsphere"
  },
  "name": "loc_2dsphere",
  "background": true,
  "2dsphereIndexVersion": 3,
  "ns": "verygoodprof.users"
}

1
为了检查是否是16MB的问题,您可以使用allowDiskUse:true,它将使用硬盘作为存储。如果使用此选项获得所有结果,则是16MB的问题。否则,查询存在问题。 - Aabid
1
请参考以下链接:https://docs.mongodb.com/manual/reference/command/aggregate/#aggregate-data-using-external-sort - Aabid
它是否使用 allowDiskUse:true 运行? - Aabid
1
你在 shell 直接运行查询时是否正常工作?问题是否具体涉及距离乘数和最大距离字段?你的 mongoose 和 mongo 服务器版本是多少? - s7vr
1
我会确保索引被正确建立。如果可行的话,删除集合,添加文档和索引,然后再次尝试查询。同时,在shell中尝试并包含“mongoose.set('debug',true)”以检查发送到mongo的查询。祝你好运! - s7vr
显示剩余10条评论
3个回答

2

谢谢您的回答,但我只收到了大约20份文件(应该是30-40份左右)。 - Louis
你是对的!!!这就是问题的所在:o Geonear返回了超过100个文档,但由于我之后还有其他过滤器,实际上只返回了约30个。所以我以为这不是问题所在。但它确实是!太愚蠢了,我的天啊浪费了那么多时间...谢谢! - Louis
没问题,我也不认为在你的情况下其他过滤器会影响$geonear的结果,它可能对其他人有所帮助... - shivam dhankani

1
您正在使用 spherical: true,其语义与平面地理空间查询不同。
此外,由于您正在使用 GeoJSON 对象而不是纯坐标,因此必须以米为单位提供距离(或以千米为单位,因为您使用了 .001 倍数)。
当使用 spherical: true 时,它使用 $nearSphere 查询:https://docs.mongodb.com/manual/reference/operator/query/nearSphere/#op._S_nearSphere 使用 spherical: false 时,Mongo 使用 $near 查询:https://docs.mongodb.com/manual/reference/operator/query/near/#op._S_near 由于您正在使用 Lat/Lng,即平面坐标,因此应禁用球形选项。

谢谢您的回答。然而,禁用球形选项是不允许的:错误消息:'geoNear命令失败:{ok:0.0,errmsg:“2dsphere索引必须具有spherical:true”,code:17301,codeName:“Location17301”}' - Louis
1
如果是这种情况,那么我们就存在不一致的问题。你正在基于Mongo 4文档构建查询,但你的服务器是3.x版本... - Bruno Ferreira
是的,服务器版本是3.4.10。所以我猜解决方案之一就是升级服务器版本?我不能避免吗?难道没有其他方法可以在当前版本下完成我想要的事情吗? - Louis
不是真的,但我猜问题不在于$geoNear,而在于其他聚合步骤。你能否尝试删除最后3个步骤,并将它们替换为{"$match": {"$lt": ["$distance", "$range"]}} - Bruno Ferreira

0

如果在 $geoNear 中设置了 "spherical": true,则返回的距离单位为弧度,因此您需要将 "distanceMultiplier": 6371 更改为地球半径以获得公里数。


也许我需要修改导师的“范围”字段,该字段以公里表示。 - Louis
我明白你的意思,因为文档上确实是这么说的,但不幸的是,当我输入6371时,只有距离=0的文档被返回,仍然存在一些问题。 - Louis
我也尝试了6371(6371 x 0.001),因为范围字段以公里表示,而距离是以米计算的,但仍然存在相同的问题... - Louis
最大距离应该以米为单位,距离乘数是用于返回结果的,因此如果您希望distancefield以公里为单位,则应该为6371,但它与数据过滤无关。 - Mohammed Essehemy
我不是在谈论maxDistance,而是关于计算结果和查询点之间的距离。当我没有设置distanceMultiplier时,这个距离以米为单位正确计算(而不是弧度)。我已经进行了检查。当我设置为0.001时,它以公里表示。但是,设置为6371时无法计算出正确的距离。虽然谢谢你的尝试,但我不认为这是问题所在。 - Louis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接