Mongoose MongoDB GeoSpatial Query - PullRequest
0 голосов
/ 06 мая 2019

У меня есть коллекция Item, которая может содержать от тысячи до сотен тысяч документов.В этой коллекции я хочу выполнить геопространственные запросы.Используя Mongoose, есть два варианта - find() и конвейер агрегации.Я показал мои реализации обоих ниже:

Модель Mongoose

Для начала, вот соответствующие свойства моей модели Mongoose:

// Define the schema
const itemSchema = new mongoose.Schema({
    // Firebase UID (in addition to the Mongo ObjectID)
    owner: {
        type: String,
        required: true,
        ref: 'User'
    },
    // ... Some more fields
    numberOfViews: {
        type: Number,
        required: true,
        default: 0
    },
    numberOfLikes: {
        type: Number,
        required: true, 
        default: 0
    },
    location: {
        type: {
            type: 'String',
            default: 'Point',
            required: true
        },
        coordinates: {
            type: [Number],
            required: true,
        },
    }
}, {
    timestamps: true
});

// 2dsphere index
itemSchema.index({ "location": "2dsphere" });

// Create the model
const Item = mongoose.model('Item', itemSchema);

Поиск запроса

// These variables are populated based on URL Query Parameters.
const match = {};
const sort = {};

// Query to make.
const query = {
    location: {
        $near: {
            $maxDistance: parseInt(req.query.maxDistance),
            $geometry: {
                type: 'Point',
                coordinates: [parseInt(req.query.lng), parseInt(req.query.lat)]
            }
        }
    },
    ...match
};

// Pagination and Sorting
const options = {
    limit: parseInt(req.query.limit),
    skip: parseInt(req.query.skip),
    sort
};

const items = await Item.find(query, undefined, options).lean().exec();

res.send(items);

Трубопровод агрегации

Предположим, что необходимо рассчитать расстояние:

// These variables are populated based on URL Query Parameters.
const query = {};
const sort = {};

const geoSpatialQuery = {
    $geoNear: {
        near: { 
            type: 'Point', 
            coordinates: [parseInt(req.query.lng), parseInt(req.query.lat)] 
        },
        distanceField: "distance",
        maxDistance: parseInt(req.query.maxDistance),
        query,
        spherical: true
    }
};

const items = await Item.aggregate([
    geoSpatialQuery,
    { $limit: parseInt(req.query.limit) },
    { $skip: parseInt(req.query.skip) },
    { $sort: { distance: -1, ...sort } } 
]).exec();

res.send(items);

Правка - Пример Документально Исправлено

Вот пример документа со всемиего свойств из коллекции Item:

{
   "_id":"5cd08927c19d1dd118d39a2b",
   "imagePaths":{
      "standard":{
         "images":[
            "users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-aafe69c7-f93e-411e-b75d-319042068921-standard.jpg",
            "users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-397c95c6-fb10-4005-b511-692f991341fb-standard.jpg",
            "users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-e54db72e-7613-433d-8d9b-8d2347440204-standard.jpg",
            "users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-c767f54f-7d1e-4737-b0e7-c02ee5d8f1cf-standard.jpg"
         ],
         "profile":"users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-51318c32-38dc-44ac-aac3-c8cc46698cfa-standard-profile.jpg"
      },
      "thumbnail":"users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-51318c32-38dc-44ac-aac3-c8cc46698cfa-thumbnail.jpg",
      "medium":"users/zbYmcwsGhcU3LwROLWa4eC0RRgG3/5cd08927c19d1dd118d39a2b/images/Image-51318c32-38dc-44ac-aac3-c8cc46698cfa-medium.jpg"
   },
   "location":{
      "type":"Point",
      "coordinates":[
         -110.8571443,
         35.4586858
      ]
   },
   "numberOfViews":0,
   "numberOfLikes":0,
   "monetarySellingAmount":9000,
   "exchangeCategories":[
      "Math"
    ],
   "itemCategories":[
      "Sports"
   ],
   "title":"My title",
   "itemDescription":"A description",
   "exchangeRadius":10,
   "owner":"zbYmcwsGhcU3LwROLWa4eC0RRgG3",
   "reports":[],
   "createdAt":"2019-05-06T19:21:13.217Z",
   "updatedAt":"2019-05-06T19:21:13.217Z",
   "__v":0
}

Вопросы

Исходя из вышеизложенного, я хотел задать несколько вопросов.

  1. Есть ли разница в производительности между моими реализациями обычного запроса Mongoose и использованием конвейера агрегации?

  2. Правильно ли сказатьчто near и geoNear очень похожи на nearSphere при использовании индекса 2dsphere с GeoJSON - разве что geoNear предоставляет дополнительные данные и ограничение по умолчанию?То есть, несмотря на разные единицы измерения, оба запроса - концептуально - будут показывать соответствующие данные в пределах определенного радиуса от некоторого местоположения, несмотря на то, что поле называется radius для nearSphere и maxDistance сnear / geoNear.

  3. В моем примере, приведенном выше, как можно снизить потерю производительности при использовании skip, но при этом можно добиться разбиения на страницы как в запросах, так и в агрегации?

  4. Функция find() позволяет необязательный параметр определять, какие поля будут возвращены.Агрегационный конвейер занимает этап $project, чтобы сделать то же самое.Есть ли определенный порядок, в котором $project должен использоваться в конвейере для оптимизации скорости / эффективности, или это не имеет значения?

Я надеюсь, что этот стиль вопроса разрешен согласноПравила переполнения стека.Спасибо.

1 Ответ

1 голос
/ 09 мая 2019

Я попробовал следующий запрос с индексацией 2dsphere. Я использовал конвейер агрегации
для запроса ниже.

db.items.createIndex({location:"2dsphere"})

При использовании конвейера агрегации он дает больше гибкости в наборе результатов. Также конвейер агрегации улучшит производительность при выполнении поисковых запросов, связанных с гео.

db.items.aggregate([
{
 $geoNear: {
    near: { type: "Point", coordinates: [ -110.8571443 , 35.4586858 ] },
    key: "location",
    distanceField: "dist.calculated",
    minDistance: 2, 
    query: { "itemDescription": "A description" }
 }])

По вашему вопросу о $ skip ниже вопрос даст вам более полное представление о работе $ skip. $ skip и $ limit в структуре агрегации

Вы можете использовать $ project в соответствии с вашими потребностями. В нашем случае у нас не было проблем с производительностью при использовании $ project более 10 миллионов данных

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...