Как лучше всего разработать схему MongoDB для службы на основе местоположения - PullRequest
0 голосов
/ 08 мая 2020

Я ищу подходящую базу данных, поддерживающую поиск на основе местоположения, обнаружил, что MongoDB поддерживает Geo JSON Объекты.

Это приложение Store Locator, где пользователь может осмотреться и выбрать ближайший к нему магазин для заказа продуктов.

Простая схема поставщика:

const VendorSchema = new Schema({
    address: {
        type: String,
        required: [true, 'Please add address']
    },
    formattedAddress: {
        type: String
    },
    location: {
        type: {
          type: String,
          enum: ['Point']
        },
        // GeoJSON Points
        coordinates: {
          type: [Number],
          index: '2dsphere'
        },
        formattedAddress: String,
        street: String,
        city: String,
        state: String,
        zipcode: String,
        country: String
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

В базе данных будет много товаров FMCG, которые могут продавать несколько поставщиков.

Схема продукта:

const ProductSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    desc: {
        type: String,
        required: true
    },
    price: Number,
    createdAt: {
      type: Date,
      default: Date.now
    }
});

Продавец может продавать несколько продуктов, и продукт может продаваться многими поставщиками, существует N-to-N взаимосвязь между поставщиками и продуктами.

Итак, я думал о создании новой схемы, VendorProduct Schema:

const VendorProductSchema = new Schema({
    price: Number,
    discountVal: Number,
    vendor : { 
        type: ObjectId, 
        ref: 'Vendor' 
    },
    createdAt: {
      type: Date,
      default: Date.now
    }
});

Вот где становится сложно / сложно:

Пользователи могут либо искать Продавца / Магазинов вокруг себя, либо могут напрямую искать Товар .

Если вас попросят найти Продавцов, он будет искать прямо в коллекции Продавцов с определенным радиусом.

Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})

Но когда пользователь ищет продукт:

Система должна отправить обратно найденные сведения о продукте (ах) вместе с ближайшими продавцами, которые их продают - от самого близкого до самого дальнего, в определенном радиусе.

По этой причине я думал о сохранении Geo JSON поставщика сведения о местоположении продукта в схеме VendorProduct.

VendorProduct Schema с Geo JSON подробности:

const VendorProductSchema = new Schema({
    price: Number,
    discountVal: Number,
    vendor : { 
        type: ObjectId, 
        ref: 'Vendor' 
    },
    location: {
        type: {
          type: String,
          enum: ['Point']
        },
        // GeoJSON Points
        coordinates: {
          type: [Number],
          index: '2dsphere'
        }
    },
    createdAt: {
      type: Date,
      default: Date.now
    }
});

Допустим, пользователь выполняет поиск с ключевым словом ONIONS. Он будет найден в коллекции продуктов, будет использовать ProductID и, в свою очередь, фильтровать их в коллекции VendorProducts на основе географических координат. Таким образом, я могу получить информацию о продукте, такую ​​как описание, изображения из коллекции продуктов и поставщика, информацию о ценах из коллекции продуктов VendorProducts.

Это только помните, что один продукт может продаваться несколькими поставщиками. Когда пользователь выполняет поиск по названию продукта - может быть N продавцов этого продукта по разным ценам. Но чтобы найти только ближайших поставщиков, я думал о том, чтобы сохранить информацию о местоположении поставщика для продукта в VendorProductSchema.

НЕСКОЛЬКО ВОПРОСОВ:

  1. Правильно ли использовать MongoDB для такой вариант использования?
  2. Это правильный дизайн и подход?
  3. Какова временная сложность таких поисков географического местоположения в MongoDB?

Ответы [ 3 ]

2 голосов
/ 18 мая 2020

Насколько я понимаю, вы должны сохранять только координаты местоположения только в схеме поставщика, поскольку они связаны с поставщиками. Ваша старая схема VendorProduct в порядке

Случай 1: Пользователь ищет ближайших поставщиков

Вам необходимо запустить запрос, чтобы найти поставщиков на основе местоположения пользователя и предоставить пользователю список поставщиков

Для этого вам необходимо запустить

Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})

Случай 2: Пользователь ищет продукты, которые он / она может найти поблизости

В этом случае также сначала выполните поиск ближайших поставщиков по вышеуказанному запросу ( Помните, что поблизости не будет ни одного поставщика, в противном случае вам нужно уменьшить радиус поиска ).

Как только вы найдете всех ближайших поставщиков, сохраните их идентификаторы в массиве, назовем его

vendorArray = [vendor_id1, vendor_id2.....]

Затем выполните поиск продуктов в схеме VendorProduct

VendorProduct.find({
  vendor : {
    $in: vendorArray
  }
})

Вышеупомянутое решение было просто в качестве альтернативы хранению информации о местоположении в VendorProductSchema

Теперь вам нужно получить сведения о продукте после получения идентификаторов продуктов из коллекции vendorProduct, я бы посоветовал вам добавить имя продукта или любое другое поле в vendorProduct, по которому вы хотите выполнить поиск или отфильтровать

Обратная сторона текущего подхода Есть несколько недостатков при хранении координат местоположения в VendorProduct

  • Вы экономите избыточные данные в нескольких коллекциях, и вы будете поддерживать их при добавлении / редактировании / удалении записей VendorProduct
  • VendorProduct Коллекция будет содержать многократное количество записей по сравнению с коллекцией Vendor, запросы geo json требуют большей вычислительной мощности чем обычный c поиск.
  • Когда ваша база данных масштабируется, более эффективно запускать geo json запрос для нескольких тысяч записей в Vendor вместо 100 тысяч записей в VendorProduct
0 голосов
/ 15 мая 2020
  1. Правильно ли использовать MongoDB для такого варианта использования?

Да, я не понимаю, почему бы и нет. MongoDB поддерживает географию JSON и позволяет множеством способов легко запрашивать эту информацию.

Правильный ли это дизайн и подход?

Если я понимаю вашу ситуацию:

Пользователи могут искать поставщиков / магазинов поблизости или также можно искать продукт напрямую. Поскольку это услуга на основе местоположения - система должна отправлять продукты, которые находятся рядом с пользователем, поэтому мы думали о сохранении данных местоположения Vendor Geo JSON в схеме VendorProduct, чтобы система могла запрашивать только продукты, которые находятся рядом с пользователем, с данными поставщика.

Учитывая заданную точку c (местоположение пользователя), найдите поставщиков в пределах заданного радиуса, у которых есть доступные продукты.

Поскольку у продуктов может быть несколько поставщиков, и поставщики находятся там, где находятся местоположения, нет причин дублировать местонахождение продавца. Если вы сохранили информацию о местонахождении поставщика в двух местах, например о поставщиках и продуктах, то у вас больше не будет единой точки истины для вашего приложения. Это становится большим беспорядком, пытаясь синхронизировать эти документы c. Если вы хотите что-либо разделить, я бы выделил расположение поставщиков в отдельную индексированную коллекцию (vendorGEO в приведенном ниже примере) и скорректировал бы текущие документы поставщиков, чтобы вместо этого указывать на эту новую коллекцию. Таким образом, вы можете запросить поставщиков, которые находятся рядом с пользователем, затем выбрать поставщиков, а затем выбрать только тех поставщиков, которые вам нужны, и выполнить поиск по их продуктам.

Вот пример оболочки mon go для поиска всех местоположений, без сортировки, на основе радиуса (окружности) точки (местоположения пользователя)

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

db.vendorGEO.find({location: {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})

миля: 1 /3963,2 радиан км: 1 / 6378,15 радиан

Официальная документация: https://docs.mongodb.com/manual/reference/operator/query/centerSphere/index.html

Вместо этого вы можете использовать Polygon. Многоугольник был бы более полезным, если бы у вас была заранее определенная область, например, в пределах города:

Многоугольник, для которого нужны четыре точки и исходная начальная точка. Обратите внимание на способ задания координат с помощью вложенных массивов:

db.collection.find({location: {$geoWithin: {$geometry: {type: "Polygon", coordinates: [[[point 1], [point 2], [point 3], [point 4], [point 1 again]]]}}}})

Чтобы легко хранить информацию о многоугольнике, сохраните их в отдельной коллекции

db.areas.insertOne({name: "Example Location", area: {type: "Polygon", coordinates: [[[longA, latA], [longB, latB], [longC, latC], [longD, latD], [longA, latA]]]}})

Чтобы определить, находится ли точка внутри многоугольник, поиск точки внутри многоугольника со ссылкой на определенное поле области

db.areas.find({area: {$geoIntersects: {$geometry: {type: "Point", coordinates: [longA, latA]}}}})
Какова временная сложность таких поисков географического местоположения в MongoDB?

При использовании $ geoIntersects или $ geoWithin вам не нужен индекс, но желательно добавить его, чтобы ускорить запросы .

Что касается временной сложности, вы можете увидеть, что происходит под капотом, используя команду .explain(), чтобы глубже погрузиться в планирование / оптимизацию запросов.

0 голосов
/ 10 мая 2020

Я знаю только базовые c MongoDB, но я заметил, что вы храните данные о местоположении в коллекции VendorProduct. В этом случае, если, скажем, поставщик редактирует свои координаты, необходимо обновить несколько документов.

Вместо этого вы можете сохранить местоположение в самой коллекции поставщика. Во время запроса вы можете выполнить вложенный запрос для получения продуктов с заданным именем, где у поставщиков значения координат меньше x, y. Синтаксис-

https://docs.mongodb.com/manual/tutorial/query-embedded-documents/

...