Здравствуйте, друзья.
В инпуте чата полезно иметь строчку с любимыми эмодзи пользователя. Вроде такой:
Как сейчас это реализовано у меня
0. Для хранения любимых эмодзи пользователя используем отдельную небольшую таблицу:
CREATE TABLE top_used_emojis (
user_id BIGINT PRIMARY KEY,
emojis TEXT[] NOT NULL
);
1. Пользователь отправляет новое сообщение в чат.
2. Сообщение сохраняется в таблицу сообщений. Пусть будет chat_messages.
CREATE TABLE chat_messages (
user_id BIGINT NOT NULL,
text TEXT,
...
)
К таблице добавлен простейший индекс, на бумаге он обещал облегчать поиск по тексту.
CREATE INDEX chat_messages_text_index ON chat_messages (text);
3. В коде приложения выполняется простой поиск совпадений для того, чтобы определить: есть ли вообще в новом сообщении эмодзи.
4. Если эмодзи нет, то ничего не трогаем.
5. Если эмодзи найдены, из приложения вызывается pgplsql-функция, принимающая на вход
user_id
, вычисляющая топ-10 наиболее часто встречающихся эмодзи в текстах сообщений пользователя и сохраняющая результат в поле
emojis
таблицы
top_used_emojis
по ключу
user_id
.
В своей работе функция использует вспомогательную таблицу, где хранятся известные нам эмодзи
CREATE TABLE emojis (
emoji TEXT PRIMARY KEY /* Здесь лежит сама эмодзи */
);
А вот и тело функции
CREATE OR REPLACE FUNCTION updateTopUsedEmojis (BIGINT) RETURNS TEXT[] AS '
DECLARE
_user_id ALIAS FOR $1;
query_result TEXT[];
BEGIN
WITH last_top_used AS (SELECT emoji, count(*)::INT AS count
FROM chat_messages cm
JOIN emojis e
ON (cm.text LIKE ''%'' || e.emoji || ''%'')
WHERE cm.user_id = _user_id
GROUP BY e.emoji
ORDER BY count DESC
LIMIT 10)
INSERT INTO top_used_emojis (user_id, emojis)
VALUES ( _user_id,
(SELECT array_agg(emoji) FROM last_top_used)::TEXT[]
)
ON CONFLICT (user_id)
DO UPDATE
SET emojis = (SELECT array_agg(emoji) FROM last_top_used)::TEXT[]
RETURNING emojis::TEXT[]
INTO query_result;
RETURN query_result;
END;
'LANGUAGE plpgsql;
6. Всё. На мелких объёмах это работает.
Необходимые уточнения
- Начиная с какого-то момента, мы, понятно, будем выполнять эту процедуру не чаще, чем 1 раз в N времени. При добавлении каждого сообщения - это слишком.
- "Любимые" и "наиболее часто встречающиеся" - не совсем одно и то же, но нам норм.
- У меня пока некоторые трудности с EXPLAIN - seqscan, вроде, научился распознавать, а косты пока не косты.
- Предполагаемое количество пользователей - 1млн, предполагаемое среднее количество сообщений у каждого пользователя - 100
Места, которые не дают мне покоя