#mongodb #mongodb-query #geolocation #aggregation-framework
#mongodb #mongodb-запрос #геолокация #агрегация-фреймворк
Вопрос:
У меня есть пользователь коллекции, рестораны, продукты, которые я хочу запросить, этот пользователь увидит в своем приложении близлежащие рестораны (5 км), которые открыты: true, и покажет продукты каждого ресторана, которые доступны и одобрены = true и архивированы: false.
let kmToRadian = function(miles){
var earthRadiusInMiles = 6378
return miles / earthRadiusInMiles
}
var user = dbo.collection('users').findOne({_id: id})
var query = {
"location": {
$geoWithin: {
$centerSphere: [ [22.222, 22.222], 5 / 6378.1] //sample coordinates
}
}
}
db.products.aggregate([
{
$lookup: {
from: "restaurants",
localField: "restaurants",
foreignField: "_id",
as: "restaurant"
}
},
{
$match : {
"restaurant.isOpen": true,
"isApproved": true,
"isAvailable": true,
"isArchived": false
}
},
{
$project: {
"restaurant.isOpen": 1,
"isApproved": 1,
"isAvailable": 1,
"isArchived": 1,
"name": 1
}
}
])
Здесь я получаю продукты isAvailable, IsApproved :true и IsArchived false . Теперь я хочу получить ресторан в 5 км поблизости.
Пользователи коллекции
id: 1
name: "Robert"
location: Object
type: "Point",
coordinates: Array
0: 11.111 //sample coordinates
1: 11.111 //sample coordinates
id: 2
name: "Jason"
location: Object
type: "Point",
coordinates: Array
0: 22.222 //sample coordinates
1: 22.222 //sample coordinates
Коллекция ресторанов
id: 1
name: "Burger King"
location: Object
type: "Point",
coordinates: Array
0: 11.111 //sample coordinates
1: 11.111 //sample coordinates
isOpen: true
id: 2
name: "McDonald's"
location: Object
type: "Point",
coordinates: Array
0: 22.222 //sample coordinates
1: 22.222 //sample coordinates
isOpen: true
id: 3
name: "Chick-fil-A"
location: Object
type: "Point",
coordinates: Array
0: 22.333 //sample coordinates
1: 22.333 //sample coordinates
isOpen: true
Коллекционные Продукты
id: 1
name: "Breakfast Whopper Jr."
price: "$1.29"
isAvailable: true
isApproved: true
createdAt: Tues Dec 01 2020 09:15:19 GMT 0800
updatedAt: Tues Dec 01 2020 09:15:19 GMT 0800
isArchived: false
shop: ObjectId('1')
id: 2
name: "Big Mac"
price: "$4.35"
isAvailable: true
isApproved: true
createdAt: Tues Dec 01 2020 09:15:19 GMT 0800
updatedAt: Tues Dec 01 2020 09:15:19 GMT 0800
isArchived: false
shop: ObjectId('2')
id: 3
name: "Spicy Chicken Sandwich"
price: "$3.29"
isAvailable: true
isApproved: true
createdAt: Tues Dec 01 2020 09:15:19 GMT 0800
updatedAt: Tues Dec 01 2020 09:15:19 GMT 0800
isArchived: false
restaurant: ObjectId('3')
Выходной сигнал:
если Роберт находится в координатах [ 22.222, 22.222].
Роберт увидит рестораны McDonald’s и Chick-fil-A, потому что isOpen: правда.
Покажите их продукты, такие как Биг Мак и сэндвич с пряной курицей, потому что isAvailable, IsApproved: true и IsArchived: false.
Комментарии:
1. @Minsky Привет, могу ли я получить предложения о том, что использовать для этого типа запросов.
2.
db.products.aggregate([ { $lookup: { from: 'restaurants', localField: 'restaurants', foreignField: '_id', as: 'shop' }, }, { $match: { 'restaurant.isOpen': true } } ])
Следующее, что я хочу получить, это доступные и одобренные продукты ресторана, которые являются true, а IsArchived — false. Я все делаю правильно? @Minsky
Ответ №1:
Не ответ, просто некоторые идеи, которые вы должны оценить самостоятельно. Следующий конвейер:
- Соответствует
isApproved:true, isAvailable:true, isArchived:true
, поэтому он предполагает и предлагает использовать этот индекс:
db.products.createIndex({
isAvailable: 1, isApproved: 1, isArchived: 1
})
- Как только количество документов будет хорошо сужено, поиск не должен занимать так много времени. Здесь индекс не требуется, пока
foreignField:_id
используется, в противном случае я бы создал индекс для этого поля. - Сопоставление $
isOpen:true
будет медленным, но позже улучшит производительность, если это сработает.pipeline
это$lookup
может помочь повысить производительность, добавив этот индекс (ИМХО)db.restaurants.createIndex({ _id: 1, isOpen: 1 })
- Я прокомментировал поля, которые мы отфильтровываем в match, потому что мы знаем значение и можем быть установлены, что, вероятно, повышает производительность (не могу это доказать).
- Мы проецируем
location
, потому что нам нужно это поле - Используйте geo, чтобы найти то, что находится рядом, это было почти правильно. Этот шаг будет довольно медленным, потому что в «geo» нет индекса, и создать его в этом запросе невозможно.
db.products.aggregate([
{
$match: {
"isApproved": true,
"isAvailable": true,
"isArchived": false
}
},
{
$lookup: {
from: "restaurants",
foreignField: "_id",
localField: "restaurant",
as: "restaurant"
}
},
{
$match: {
"restaurant.isOpen": true
}
},
{
$project: {
"restaurant.isOpen": 1,
//"isApproved": 1,
//"isAvailable": 1,
//"isArchived": 1,
"name": 1,
"restaurant.location": 1
}
},
{
$match: {
"restaurant.location": {
$geoWithin: {
$centerSphere: [
[
22.222,
22.222
],
5/6378.1]
}
}
}}
])
Я должен сказать, что я не эксперт, так что вам решать тестировать и улучшать. Кроме того, пожалуйста, включайте только формат документов JSON, чтобы мы могли сразу протестировать. Мне пришлось сделать это вручную для ТЕКУЩЕЙ ВЕРСИИ.
(Также полезно помнить, что что-то не очень полезное, но быстрое и хорошо завершенное лучше, чем очень сложное лаговое приложение. Опять же, вы узнаете, как это работает, и я совсем не эксперт. )
Комментарии:
1. Привет, @Minsky извините за поздний ответ. Он не вернул мне рестораны, которые находятся рядом с бездействием. он возвращает мне пустой массив.