MongoDB — Загадка запроса — Ссылки на документы или вложенные документы

У меня возникла небольшая проблема с некоторыми данными, которые я храню в своей MongoDB (Примечание: я использую mongoose в качестве ODM). У меня есть две схемы:

mongoose.model('Buyer',{
  credit: Number,
})

а также

mongoose.model('Item',{
  bid: Number,
  location: { type: [Number], index: '2d' }
})

Покупатель/Товар будет иметь ассоциацию родитель/потомок с отношением один ко многим. Я знаю, что могу настроить элементы как встроенные вложенные документы в документ покупателя или я могу создать два отдельных документа со ссылками идентификаторов объектов друг на друга.

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

Кажется, чтобы удовлетворить первым критериям, я должен встроить Items в качестве поддокумента, чтобы я мог сравнить два числа. Но, чтобы сравнивать местоположения с помощью запроса geoNear, кажется, было бы лучше разделить документы, иначе я не могу выполнить geoNear для каждого поддокумента.

Есть ли способ выполнить обе задачи с этими данными? Если да, то как мне структурировать данные? Если нет, есть ли способ выполнить один запрос, а затем второй запрос по результату первого запроса?

Спасибо за вашу помощь!


person jordan    schedule 27.04.2015    source источник


Ответы (2)


Существует еще один вариант (помимо встраивания и нормализации) для хранения иерархий в mongodb, который сохраняет их как древовидные структуры. В этом случае вы должны хранить покупателей и товары в отдельных документах, но в одной коллекции. Каждому документу Товара потребуется поле, указывающее на его документ Покупателя (родительский), а родительское поле каждого документа Покупателя будет иметь значение null. Документы, на которые я ссылаюсь, объясняют несколько реализаций, которые вы можете выбрать.

person Nocturno    schedule 06.05.2015

Если ваши элементы хранятся в двух отдельных коллекциях, лучшим вариантом будет написать свою собственную функцию и вызвать ее с помощью mongoose.connection.db.eval('some code...');. В таком случае вы можете выполнить свою расширенную логику на стороне сервера.

Вы можете написать что-то вроде этого:

var allNearItems = db.Items.find(
    { location: {
        $near: {
            $geometry: {
              type: "Point" ,
              coordinates: [ <longitude> , <latitude> ]
            },
            $maxDistance: 100
        }
    }
}); 
var res = [];
allNearItems.forEach(function(item){
    var buyer = db.Buyers.find({ id: item.buyerId })[0];
    if (!buyer) continue;
    if (item.bid < buyer.credit) {
        res.push(item.id);
    }
});
return res;

После оценки (поместите его в вызов mongoose.connection.db.eval("...")) вы получите массив идентификаторов элементов.

Используйте его с осторожностью. Если ваш массив allNearItems будет слишком большим или вы будете запрашивать его очень часто, вы можете столкнуться с проблемами производительности. Команда MongoDB на самом деле отказалась от прямого выполнения кода js, но оно все еще доступно в текущей стабильной версии.

person kreig    schedule 12.05.2015