Piszę coś w rodzaju Messengera i zastanawia mnie jak przechowywać wiadomości.
Obecnie wygląda to tak:
- FE wysyła wiadomość przez WebSocket do BE
- BE dokonuje autentykacji i zapisuje wiadomość w bazie danych (MongoDB) - docelowo zapis chcę zrobić asynchroniczny
- BE wysyła wiadomość do wskazanego w niej odbiorcy
W bazie mam wtedy coś takiego
@Document(collection = CollectionNames.MESSAGES)
public class MessageEntity {
@Id
private final String id;
private final String sender;
private final String receiver;
private final String payload;
private final LocalDateTime sentAt;
}
No i z grubsza jest ok, ale mam dwa dodatkowe przypadki, czyli
- Pobranie historii rozmowy z danym użytkownikiem, tutaj sytuacja jest w miarę prosta, z bazy wyciągam wszelkie wiadomości między dwoma użytkownikami
public List<MessageEntity> findMessagesBetween(String firstUser, String secondUser) {
final var query = new Query(new Criteria().orOperator(messagesBetween(firstUser, secondUser), messagesBetween(secondUser, firstUser)));
return mongoTemplate.find(query, MessageEntity.class);
}
private Criteria messagesBetween(String first, String second) {
return new Criteria().andOperator(Criteria.where("sender").is(first), Criteria.where("receiver").is(second));
}
Pozostaje wrzucenie tego w Set
by pozbyć się duplikatów i zrobienie sortowania od najstarszej do najnowszej wiadomości.
- Pobranie historii wszystkich rozmów, analogicznie do Messengera, widzę z kim ostatnio rozmawiałem i ostatnią wiadomość jaką wysłałem
public class ConversationSummary {
private final String conversationWith;
private final String lastMessage;
private final LocalDateTime lastMessageAt;
}
Tutaj sprawa się nieco komplikuje, bo muszę :
- Wyciągnąć wszystkie wiadomości które zawierają id zalogowanego użytkownika
- Znaleźć w tych wiadomościach wszystkich odbiorców z którym dany użytkownik rozmawiał
- Zebrać wiadomości między każdym takim rozmówcą i użytkownikiem
- Znaleźć w każdym takim zbiorze najnowszą wiadomość
- Zebrać to do
List<ConversationSummary>
i posortować
I nawet to działa, ale zastanawiam się czy nie ma lepszego sposobu?
Drugie podejście o którym myślałem to zrobienie encji Conversation
, w której mam wszystkie wiadomości między danymi dwoma użytkownikami. Taka encja byłaby tworzona by pierwszej wiadomości wysłanej między nimi a następnie byłaby modyfikowana. Problem jaki widzę - jednocześnie dwóch użytkowników modyfikuje tę samą encję na bazie, bo nowa wiadomość == dodanie czegoś do listy wiadomości wewnątrz tej encji Conversation
. Dodatkowo, powiedzmy że Ci użytkownicy wymienili już kilka tysięcy wiadomości, wtedy wyciaganie tego za każdym razem z bazy i updateowanie może być kosztowne.