2016-03-24 3 views
1

Я знаю, что это противоречит дизайну MongoDB и его модели No-SQL, но я пытаюсь искать документы в одной коллекции, а затем использовать поле ID из результатов поиска соответствующие записи в другой коллекции. Эффективно пытается подражать соединению.MongoDB, Mongoose результат от одного Найти, чтобы найти другую коллекцию

//query is irrelevant to question 
var results = collectionOne.find(query).limit(limit); 

var a = []; 

results.forEach(function(r) 
{ 
    var aquery = { id : {$eq : r.id}}; 
    collectionTwo.find(aquery).limit(limit).exec(function, b) 
    { 
     if (err) 
     { 
      res.render('error', 
      { 
       status : 500 
      }); 
     } 
     else 
     { 
      a.push(b); 
     } 
    }); 
}); 
res.jsonp(a); 
+0

Вы можете использовать агрегирование с $ lookup для выполнения этой серверной части – profesor79

ответ

6

Хотя существуют новые возможности, такие как MongoDB $lookup, что делает «своего рода присоединиться», ваша конкретная операция не нуждается в этом. Все, что вы делаете здесь, возвращает результаты из другой коллекции, основанной на том, где значение _id было привязано к предыдущему.

Для этого лучшим вариантом является выпуск один другой запрос в другой коллекции с использованием $in.

// Mongoose turns a cursor to an array by default in the callback method 
collectionOne.find(query,{ "_id": 1}).limit(limit).exec(function(err,results) { 

    // Just get array of _id values 
    var ids = results.map(function(el) { return el._id }); 

    // Not sure if you really mean both collections have the same primary key 
    // I'm presuming two different fields being "id" as opposed to "_id" 
    collectionTwo.find({ "id": { "$in": ids } },function(err,items) { 
     // matching results are here 
    }) 

}) 

Всё.

Все, что вам сделать, это вернуть первые результаты запроса _id значений как «список» только что, то поставить этот аргумент $in на соответствующем поле в целевой коллекции

Если вы действительно хотели «присоединиться "и есть MongoDB 3.2 доступны, то вы можете использовать $lookup как этот

collectionOne.aggregate([ 
    { "$match": query }, 
    { "$limit": limit }, 
    { "$lookup": { 
     "from": "collectionTwo", 
     "localField": "_id", 
     "foreignField": "id", 
     "as": "twoItems" 
    }} 
]) 

это фактический„присоединился результат“, и, хотя вы могли возможно использовать его только возвратить совпавшие результаты от collectionTwo, то я Persona lly не будет. Это дорогостоящее упражнение даже на сервере, и дальнейшая фильтрация, необходимая для фактического возврата этого формата, в конечном итоге будет стоить еще больше.

Вы также можете прочитать о .populate() в mongoose, что на самом деле является «обратным» типом запроса. Вместо этого процесс состоит в том, чтобы хранить массив (или регулярное поле, но массив в этом случае) значений ObjectId, указывающих на первичный ключ объектов в соответствующей коллекции. Поэтому, если для collectionTwo было «много» значений, они будут храниться в массиве в пределах collectionOne документов.

Опять же, это «эмуляция соединения», а не реальное соединение. Результат будет похож на $lookup, и снова на самом деле не «просто» результаты от collectionTwo, а «объединенная» версия, которую вам также необходимо будет фильтровать.

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

+0

@ user3424480 Не уверен, что вы думаете о нем. Если вы имеете в виду, что когда «источник» использует «ObjectId», значит, «цель» тоже должна быть, тогда, конечно, это так. Это справедливо для любого типа BSON. –

Смежные вопросы