Я создаю чат внутри своего сайта. Для хранения данных использую коллекции Chat, User, Messages.
Я хочу, чтобы результаты были в массиве, содержащем:
[{
username (another one, not me)
last update
last message
}]
В модели чата у меня есть только chatid
и array of two members
, поэтому мне нужно прокрутить коллекцию пользователей, чтобы получить user name
, используя user id
из нее. Я хочу сохранить в массиве все имена (в будущем я также хотел бы перебирать сообщения, чтобы получать последние сообщения для каждого chatid
). Проблема в том, что когда я возвращаю список чатов, он пуст. Я думаю, мне нужно как-то использовать Promise
, но я не совсем уверен, как это должно работать.
Chat.find({ members: userId })
.then(chats => {
let chatsList = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
User.findOne({ _id: guestId })
.then(guest => {
let chatObj = {};
name = guest.name;
chatsList.push(name);
console.log("chatsList", chatsList)
})
.catch(err => console.log("guest err =>", err))
})
return res.json(chatsList)
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
В вашей модели чатов должно быть два пользователя-отправителя и получателя, и ваш первый запрос должен быть Chats.find({sender : {$in : 'массив, содержащий ваш идентификатор пользователя и еще один идентификатор пользователя'}, Receiver: {$in : 'массив, содержащий ваш идентификатор пользователя и еще один идентификатор пользователя'}})
@fjc в очереди return res.json(chatsList)
@JackOfAshes-MohitGawande Я храню его в одном массиве - members
. members = [id1,id2]
, это для того, чтобы получить гостя чата, который я использую guestId
@Angelzzz: так в каком порядке вы будете следовать в очереди let guestId = chat.members[1 - chat.members.indexOf(userId)];
вы принимаете какой-то порядок. Верно? как вы убедитесь, что кто-то является гостем, если вы используете приложение, которое будет гостем, и если кто-то еще использует приложение, вы будете гостем.
@VishwaDeepakSingh есть массив с двумя пользователями (индекс 0 и 1). Если я пользователь 0, то гость - это пользователь 1 и наоборот :), поэтому guestId всегда является идентификатором гостя, а не моим
@Angelzzz: guestId — это совершенно другой атрибут? Спрашиваю, потому что я вижу в коде, что вы управляете этим chat.members[1 - chat.members.indexOf(userId)]
так.
Пожалуйста, забудьте о guestid. Я уверен, что в этой строке это работает нормально. Не понимаю, почему. Вы спрашиваете об этом. В этом случае он ничего не делает. В этом примере это может быть жестко запрограммировано на любое число.
@Angelzzz: не могли бы вы проверить, работает ли решение, представленное в ответах?
Действительно, Обещание.все — это то, что вы ищете:
Chat.find({ members: userId })
.then(chats => {
let userPromises = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
userPromises.push(User.findOne({ _id: guestId }));
});
return Promise.all(userPromises).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
хотя, вероятно, было бы лучше сделать один вызов БД со списком идентификаторов ($in
запрос). Что-то вроде этого:
Chat.find({ members: userId })
.then(chats => {
let ids = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
ids.push(guestId);
});
return User.find({_id: {$in: ids}}).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
Вы можете дополнительно проверить, есть ли у каждого id
соответствующий guest
.
Спасибо! Можете, пожалуйста, подробнее рассказать об одиночном вызове БД? Как это сделать?
что, если я хочу сделать chatsList массивом объектов и добавить в него chatId?
Вы можете попробовать это:
Chat.find({ members: userId }).then(chats => {
let guestHashMap = {};
chats.forEach(chat => {
let guestId = chat.members.filter(id => id != userId)[0];
// depending on if your ID is of type ObjectId('asdada')
// change it to guestHashMap[guestId.toString()] = true;
guestHashMap[guestId] = true;
})
return Promise.all(
// it is going to return unique guests
Object.keys(guestHashMap)
.map(guestId => {
// depending on if your ID is of type ObjectId('asdada')
// change it to User.findOne({ _id: guestHashMap[guestId] })
return User.findOne({ _id: guestId })
}))
})
.then(chats => {
console.log(chats.map(chat => chat.name))
res.json(chats.map(chat => chat.name))
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
как это может помочь? Меня устраивает, как я получаю guestId, все работает хорошо
Это вообще не отвечает на вопрос.
Это не дает ответа на вопрос. Чтобы подвергнуть критике или запросить разъяснения у автора, оставьте комментарий под его публикацией. - Из обзора
@Towkir - не могли бы вы просмотреть еще раз. Извините, я не смог понять вопрос с первой попытки.
@Angelzzz: Мой Плохой.
@Angelzzz: в таком случае не могли бы вы проголосовать за мой ответ. Я потерял свою репутацию из-за моего безответственного поведения здесь.
Вы сталкиваетесь с проблемами параллелизма. Например, запуск chats.forEach
, а внутри forEach
запуск User.findOne().then
: оператор return
уже выполнен до того, как обещание User.findOne()
разрешено. Вот почему ваш список пуст.
Вы можете получить более читаемый и работающий код, используя async/await
:
async function getChatList() {
const chats = await Chat.find({members: userId});
const chatsList = [];
for (const chat of chats) {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
const guest = await User.findOne({_id: guestId});
chatsList.push(guest.name);
}
return chatsList;
}
Затем код для фактической отправки списка чатов пользователю:
try {
return res.json(await getChatList());
} catch (err) {
// handle errors;
}
Куда ты возвращаешься
chatsList
и где пусто?