代码之家  ›  专栏  ›  技术社区  ›  Louis

MongoDB,如何使用$geoNear检索给定范围内的文档

  •  0
  • Louis  · 技术社区  · 6 年前

    要检索的文档是具有一个范围(整数值)的教程,该范围对应于它们要移动的距离。例如,如果一个顾客在巴黎瓦伦街48号找导师,我们应该只找到可以去巴黎瓦伦街48号的导师( 搜索位置和导师位置之间的距离必须小于导师范围

    你知道怎么做吗?查询是一个纬度和一个经度(位置),导师还有一个位置(lat和lng)和一个范围(他能走多远)。

    以下是导师模式的简化版本:

    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 tutorSchema = mongoose.Schema({
        location: {
            address_components: [
                {
                    long_name: String,
                    short_name: String,
                    types: String
                }
            ],
            description: String,
            lat: Number,
            lng: Number
    
        },
        range: {
            type: Number,
            default: 15
        },
        loc: {
            type: { type: String },
            coordinates: []
        }
    });
    
    tutorSchema.plugin(mongooseAggregatePaginate);
    tutorSchema.index({ "loc": "2dsphere" });
    module.exports = {
        Tutor
    };
    

    这是我到目前为止的问题(不考虑范围, ) :

     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 || 10000;
    
      let levelsIn = req.body.levels && req.body.levels.length !== 0 ? req.body.levels.map(level => {
        return ObjectID(level);
      }) : null;
      let subjectsIn = req.body.subjects && req.body.subjects.length !== 0 ? req.body.subjects.map(subject => {
        return ObjectID(subject);
      }) : null;
    
      var options = { page: page, limit: perPage,  sortBy: { updatedDate: -1 } }
    
      const isAdmin = req.user ? req.user.role === "admin" || req.user.role === "super-admin" : false;
    
      let match = {}
    
      if (levelsIn) match.levels = { $in: levelsIn };
      if (subjectsIn) match.subjects = { $in: subjectsIn }
      if (typeof req.body.activated !== "undefined") match.profileActivated = req.body.activated;
      if (req.body.from) match.createdAt = { $gte: new Date(req.body.from) };
      if (req.body.to) {
        if (match.createdAt) match.createdAt.$lte = new Date(req.body.to);
        else match.createdAt = { $lte: new Date(req.body.to) };
      }
    
      var aggregate = null;
    
      if (!isAdmin) {
        match.activated = true
        match.profileActivated = true
        match.profileOnline = true
      }
    
      if (lat1 && lon1) {
    
       match.$expr = {
            $lt: ["$distance", "$range"] // "calculated_distance < tutor_range"
         }
    
           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
           }
         },
         {
           $match: match
         }
         ]);
      } else {
        aggregate = Tutor.aggregate([
          {
            $match: match
          }
        ]);
      }
    
      Tutor
        .aggregatePaginate(aggregate, options, function (err, result, pageCount, count) {
          if (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);
              });
          }
        })
    };
    

    谢谢你的帮助!

    1 回复  |  直到 6 年前
        1
  •  1
  •   Shaharyar    6 年前

    您只需要在管道中添加一个过滤器来进行比较 distance range 领域:

    {
        $match: {
            $expr: {
                $lt: ["$distance", "$range"] // "calculated_distance < tutor_range"
            }
        }
    }
    

    完成查询:

    aggregate = Tutor.aggregate([{
            "$geoNear": {
                "near": {
                    "type": "Point",
                    "coordinates": [lon1, lat1]
                },
                "distanceField": "distance", // this calculated distance will be compared in next section
                "spherical": true
            }
        },
        {
            $match: {
                $expr: {
                    $lt: ["$distance", "$range"]
                }
            }
        }
    ]);