Я использую Mongo DB Atlas с node.js и oboe (oboe передает результаты в html). Я новичок во всем этом, просто изучаю все эти технологии, поэтому объяснение в более простых терминах будет оценено.
Цель состоит в том, чтобы выполнить $lookup между двумя коллекциями, в данном случае между коллекцией «обзор» и «место». Я исследовал похожие ответы, но они либо не работали, либо использовали строки, а не ObjectId.
Это довольно просто, просто соедините ObjectId обеих коллекций, но я не могу вытащить данные из «связанной слева» коллекции «мест» при использовании гобоя (см. Код гобоя внизу, FWIW).
Вот посмотрите на документ из обеих коллекций, затем код. Что я делаю не так? Я попытался преобразовать их в строки и объединить с .toString() и .str, а также поставить «place_id.ObjectId» и «_id.ObjectId» для localField и ForeignField. Другое дело, как я могу увидеть, что находится в курсоре, чтобы знать, что я получаю? отладка (курсор.ToArray()) не работает. Заранее спасибо.
review
({
"_id": { "$oid": "5fd27fd9647f7bb815c4c946" },
"place_id": { "$oid": "5fbc37c4fc13ae680b00002b" }, // connect this...
"user_id": { "$oid": "5fbc10ecfc13ae232d000068" },
"title": "Black Forest -- unforgettable!",
"description": "The forest was great.",
"score": { "$numberInt": "5" }
}
place
{
"_id": { "$oid": "5fbc37c4fc13ae680b00002b" }, // connected to _id above
"name": "Black Forest (Schwarzwald)",
"category": "activity",
"city": "Freiburg",
"country": "Germany",
"description": "The Black Forest (German: Schwarzwald [ˈʃvaʁtsvalt] (About this soundlisten)) is a large forested mountain range.]",
"image": { "filename": "1607629020164_black_forest.jpg", "mime": "image/jpeg" },
"state": ""
})
router.get('/', async (req, res, next) => {
debug('get all reviews api');
try {
const q = req.query.q;
const collation = { locale: 'en_US', strength: 1 };
const matchStage = {};
if (q) {
matchStage.$text = { $search: q };
}
const pipeline = [
{
$match: matchStage,
},
{
$lookup: {
from: 'place',
localField: 'place_id',
foreignField: '_id',
as: 'place',
},
},
];
const connection = await db.connect();
const cursor = connection.collection('review').aggregate(pipeline, { collation: collation });
// write the JSON file
res.type('application/json');
res.write('[\n');
for await (const doc of cursor) {
res.write(JSON.stringify(doc));
res.write(',\n');
}
res.end('null]');
} catch (err) {
sendError(err, res);
}
});
Курсор перемещается на гобой и становится «предметом». Я бы ожидал использовать строку шаблона, такую как {item.place.name}
, чтобы получить данные при вводе этого в html. Вот как бы я получил к нему доступ, верно?
const performSearch = () => {
seen = 0;
$('stream-data-spinner').removeClass('d-none');
$('#search-results').html('');
const formData = $('#search-place-form').serialize();
oboe('/api/review?' + formData)
.node('![*]', (item) => {
if (item) {
chunk.push(item);
if (chunk.length >= 1000) {
showChunk(chunk);
}
}
return oboe.drop;
})
.done((_) => {
// show the last chunk
showChunk(chunk);
// hide the spinner
outputSpinner.classList.add('d-none');
})
.fail((res) => {
// show the error
outputSeen.textContent = `ERROR: network error`;
outputSeen.classList.add('text-danger');
outputSpinner.classList.add('text-danger');
});
};
У меня есть наборы образцов данных, если вы это имеете в виду, например, в одном из них есть фиктивные данные airbnb. Тем не менее, я считаю, что мой $lookup работает, потому что я вижу JSON.tringify в отладке, но я не могу получить доступ к нужным мне данным в том, что результаты выводятся в oboe. Я думаю, что ответ Рэя ниже может быть правильным путем, который мне нужно использовать $ unwind. Я бы очень хотел, чтобы вы написали запрос.
Я считаю, что у меня это есть, см. Решение Рэя, $unwind сделал это за меня.
О, вы искали левое соединение? Виноват. Я просто просматривал все элементы с тегами MongoDB, которые включали $search
. Для многих операций поиска $search
как первый этап агрегации быстрее. Этот случай уникален, и я, честно говоря, не прочитал его достаточно. Я напишу запрос. Одну секунду.
Что ж, похоже, вам потребуются два запроса, потому что вы хотите объединить две коллекции, если только вы не объединили их с материализованными представлениями. Ты на 4.4?
Из вашего запроса агрегации MongoDB ваше поле place
представляет собой массив. Вы можете $unwind сгладить его в объект, чтобы ваш код гобоя мог получить к нему доступ.
Читаю о $unwind сейчас. Сейчас я использую гобой и безрезультатно пишу всевозможные странные шаблонные строки, такие как ${item.place[0].city и несколько других вариантов. Похоже ли, что $unwind по-прежнему является решением?
Вот оно. Просто добавил после поиска: `` { $unwind: '$place', },` `` и это сделало работу.
Я думаю, это зависит от того, как вы используете результат агрегации. Исходя из моего опыта, я не предпочитаю хранить поля в виде массива, так как хочу избежать тяжелой обработки полей массива. Код станет запутанным и может быть неэффективным. Обычно я раскручиваю их после $lookup, чтобы использовать оптимизацию $lookup + $unwind.
Вы обязательно должны использовать Atlas Search для этого запроса. У вас там есть бесплатная учетная запись Sandbox? Я могу написать запрос.