Redis to coś więcej niż sklep typu klucz-wartość.
Więc chcesz:
- wiadomości na czacie,
- Dwuosobowe dyskusje,
- nie wspomniałeś o ograniczeniach czasowych, więc załóżmy, że archiwizujesz wiadomości po pewnym czasie,
- Nie mówisz też, czy chcesz rozdzielać „wątki” między dwie osoby, np. fora lub ciągłe wiadomości, np. Facebook. Zakładam ciągłość.
Dla każdego użytkownika musisz przechowywać wysyłane przez niego wiadomości. Powiedzmy, że APP_NAMESPACE:MESSAGES:<USER_ID>:<MESSAGE_ID>
. Dodajemy tutaj identyfikator użytkownika, abyśmy mogli łatwo pobrać wszystkie wiadomości wysłane przez jednego użytkownika.
A dla każdych dwóch użytkowników musisz śledzić ich konwersacje. Jako klucza możesz po prostu użyć ich identyfikatorów użytkownika APP_NAMESPACE:CONVERSATIONS:<USER1_ID>-<USER2_ID>
. Aby mieć pewność, że zawsze otrzymujesz tę samą, wspólną rozmowę dla dwóch użytkowników, możesz sortować ich identyfikatory alfabetycznie, dzięki czemu użytkownicy 132 i 145 będą mieli 132:145 jako klucz rozmowy
Co więc przechowywać w „rozmowach”? Użyjmy listy:[messageKey, messageKey, messageKey]
.
Ok, ale czym jest teraz wiadomośćKlucz? Kombinacja powyższego identyfikatora użytkownika i identyfikatora wiadomości (abyśmy mogli uzyskać aktualną wiadomość).
Więc w zasadzie potrzebujesz dwóch rzeczy:
- Zapisz wiadomość i nadaj jej identyfikator
- Zapisz odniesienie do tej wiadomości w odpowiedniej rozmowie.
W przypadku węzła i standardowego klienta redis/hiredis byłoby to coś takiego (pominę oczywiste błędy itp., i napiszę ES6. Jeśli nie możesz jeszcze czytać ES6, po prostu wklej go do babel):
// assuming the init connects to redis and exports a redisClient
import redisClient from './redis-init';
import uuid from `node-uuid`;
export function storeMessage(userId, toUserId, message) {
return new Promise(function(resolve, reject) {
// give it an id.
let messageId = uuid.v4(); // gets us a random uid.
let messageKey = `${userId}:${messageId}`;
let key = `MY_APP:MESSAGES:${messageKey}`;
client.hmset(key, [
"message", message,
"timestamp", new Date(),
"toUserId", toUserId
], function(err) {
if (err) { return reject(err); }
// Now we stored the message. But we also want to store a reference to the messageKey
let convoKey = `MY_APP:CONVERSATIONS:${userId}-${toUserId}`;
client.lpush(convoKey, messageKey, function(err) {
if (err) { return reject(err); }
return resolve();
});
});
});
}
// We also need to retreive the messages for the users.
export function getConversation(userId, otherUserId, page = 1, limit = 10) {
return new Promise(function(resolve, reject) {
let [userId1, userId2] = [userId, otherUserId].sort();
let convoKey = `MY_APP:CONVERSATIONS:${userId1}-${userId2}`;
// lets sort out paging stuff.
let start = (page - 1) * limit; // we're zero-based here.
let stop = page * limit - 1;
client.lrange(convoKey, start, stop, function(err, messageKeys) {
if (err) { return reject(err); }
// we have message keys, now get all messages.
let keys = messageKeys.map(key => `MY_APP:MESSAGES:${key}`);
let promises = keys.map(key => getMessage(key));
Promise.all(promises)
.then(function(messages) {
// now we have them. We can sort them too
return resolve(messages.sort((m1, m2) => m1.timestamp - m2.timestamp));
})
.catch(reject);
});
});
}
// we also need the getMessage here as a promise. We could also have used some Promisify implementation but hey.
export function getMessage(key) {
return new Promise(function(resolve, reject) {
client.hgetall(key, function(err, message) {
if (err) { return reject(err); }
resolve(message);
});
});
}
Teraz jest to prymitywne i nieprzetestowane, ale to jest sedno tego, jak możesz to zrobić.