Badge for posts with Likes from a specific group

As already mentioned here:

Grant a badge to everyone having posted at least <POST_COUNT> posts in the category <CATEGORY_NAME> that have received at least <LIKE_COUNT> likes by users in the group <TEAM_NAME>. Similar to the bug reporter badge here, but can require more than one like.

SELECT p.user_id, min(p.created_at) granted_at, MIN(p.id) post_id
FROM badge_posts p
JOIN topics t ON t.id = p.topic_id
WHERE category_id = (
        SELECT id FROM categories WHERE name ilike '<CATEGORY_NAME>'
    ) AND (
        SELECT count(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
            AND post_action_type_id = (
                SELECT id FROM post_action_types WHERE name_key = 'like'
            ) AND pa.user_id IN (
                SELECT gu.user_id
                FROM group_users gu
                WHERE gu.group_id = ( SELECT id FROM groups WHERE name ilike '<TEAM_NAME>' ) 
            )
    ) >= <LIKE_COUNT>
    AND p.post_number = 1
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING count(*) >= <POST_COUNT>

Я немного повозился с этим и почти добился успеха, но не смог применить это ко всем категориям так, как хотел. Есть ли простой способ сделать это?

Привет @Firepup650 :slight_smile: Попробуй, возможно, этот вариант. У меня на экземпляре сработал.

<НАЗВАНИЕ КАТЕГОРИИ> = Чувствительное к регистру название категории (не slug)
<ГРУППА> = Название группы (например: Staff, Trust_level_0)
<МИНИМАЛЬНОЕ КОЛИЧЕСТВО ЛАЙКОВ> = минимальное количество лайков, которое вы хотите установить
<ПОРОГ КОЛИЧЕСТВА ПОСТОВ> = минимальное количество постов
SELECT p.user_id, min(p.created_at) granted_at, MIN(p.id) post_id
FROM badge_posts p
JOIN topics t ON t.id = p.topic_id
WHERE t.category_id = (
        SELECT id FROM categories WHERE name ilike '<НАЗВАНИЕ КАТЕГОРИИ>'
    ) AND (
        SELECT count(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
            AND post_action_type_id = (
                SELECT id FROM post_action_types WHERE name_key = 'like'
            ) AND pa.user_id IN (
                SELECT gu.user_id
                FROM group_users gu
                WHERE gu.group_id = ( SELECT id FROM groups WHERE name ilike '<НАЗВАНИЕ ГРУППЫ>' ) 
            )
    ) >= <МИНИМАЛЬНОЕ КОЛИЧЕСТВО ЛАЙКОВ>
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING count(*) >= <ПОРОГ КОЛИЧЕСТВА ПОСТОВ>

Для нескольких категорий можно сделать так:

<НАЗВАНИЯ КАТЕГОРИЙ> = Чувствительные к регистру названия категорий
<ГРУППА> = Название группы (например: Staff, Trust_level_0)
<МИНИМАЛЬНОЕ КОЛИЧЕСТВО ЛАЙКОВ> = минимальное количество лайков, которое вы хотите установить
<ПОРОГ КОЛИЧЕСТВА ПОСТОВ> = минимальное количество постов
SELECT p.user_id, min(p.created_at) granted_at, MIN(p.id) post_id
FROM badge_posts p
JOIN topics t ON t.id = p.topic_id
WHERE t.category_id IN (
        SELECT id FROM categories WHERE name ILIKE ANY (ARRAY['<НАЗВАНИЕ КАТЕГОРИИ 1>', '<НАЗВАНИЕ КАТЕГОРИИ 2>', '<НАЗВАНИЕ КАТЕГОРИИ 3>'])
    ) AND (
        SELECT count(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
            AND post_action_type_id = (
                SELECT id FROM post_action_types WHERE name_key = 'like'
            ) AND pa.user_id IN (
                SELECT gu.user_id
                FROM group_users gu
                WHERE gu.group_id = ( SELECT id FROM groups WHERE name ILIKE '<ГРУППА>' ) 
            )
    ) >= <МИНИМАЛЬНОЕ КОЛИЧЕСТВО ЛАЙКОВ>
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING count(*) >= <ПОРОГ КОЛИЧЕСТВА ПОСТОВ>

Привет, @Lilly!
Оба запроса выглядят отлично, но я хотел бы запустить запрос по всем категориям, если это возможно. Когда я пытался это сделать, постоянно возникали ошибки о том, что подзапрос возвращает несколько строк, поэтому я обратился сюда за помощью.

Вы имеете в виду, что хотите использовать один и тот же запрос для всех категорий?

SELECT p.user_id, min(p.created_at) granted_at, MIN(p.id) post_id
FROM badge_posts p
JOIN topics t ON t.id = p.topic_id
WHERE (
        SELECT count(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
            AND post_action_type_id = (
                SELECT id FROM post_action_types WHERE name_key = 'like'
            ) AND pa.user_id IN (
                SELECT gu.user_id
                FROM group_users gu
                WHERE gu.group_id = ( SELECT id FROM groups WHERE name ILIKE '<GROUP>' ) 
            )
    ) >= <MINIMUM LIKE COUNT>
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING count(*) >= <POST COUNT THRESHOLD>

Кажется, это должно сработать, но возникает ошибка, когда группа, против которой вы запускаете это, — staff. Я пробовал использовать как Staff, так и staff в качестве имени группы и временно установил количество постов и лайков в 1, но система сообщает, что значки не будут выданы. Что я делаю не так?

Хм, я использовал staff в нижнем регистре, и у меня всё сработало. :thinking:

SELECT p.user_id, min(p.created_at) granted_at, MIN(p.id) post_id
FROM badge_posts p
JOIN topics t ON t.id = p.topic_id
WHERE (
        SELECT count(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
            AND post_action_type_id = (
                SELECT id FROM post_action_types WHERE name_key = 'like'
            ) AND pa.user_id IN (
                SELECT gu.user_id
                FROM group_users gu
                WHERE gu.group_id = ( SELECT id FROM groups WHERE name ILIKE 'staff' ) 
            )
    ) >= 1
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING count(*) >= 1

Странно :face_with_spiral_eyes: У меня всё ещё не работает. Попробую запустить это для нескольких других групп, чтобы понять, в чём проблема.

Редактирование: Запустил для другой группы, и запрос всё равно не сработал. Не понимаю, в чём может быть проблема. Не зависит ли это случайно от основных групп?

Редактирование 2: Это бы не сработало, похоже, staff нельзя устанавливать в качестве основной группы.

Думаю, я понял, почему. Я займусь этим после ужина. Мне в любом случае нужна практика SQL. Badge SQL более ограничен по сравнению с PostgreSQL. Я уже справился с частью про подзапросы. :slight_smile:

Я ещё не до конца проснулся, и мне нужно как минимум две чашки чая, чтобы полноценно взяться за запросы по бейджам, но я недавно обсуждал с ботом подобные запросы и считаю, что использование реальных post_action_code_id и group_id предпочтительнее, чем вложенные SELECT-запросы для получения того же результата.

Я сделал это, чтобы получить необходимые таблицы схемы для posts, posts_actions, group_users и groups

SELECT column_name, data_type, character_maximum_length
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE table_name = '<TABLE NAME>';

Затем использовал этот запрос, чтобы получить все идентификаторы групп:

SELECT name, id FROM groups ORDER BY name

После этого я включил все необходимые таблицы схемы и попросил Лолу, то есть GPTbot, использовать реальный идентификатор post_action_code и код group_id. После некоторых обсуждений и внесения исправлений мы пришли к следующему. Снова кажется, что это работает в Data Explorer, но я всё ещё не могу ничего получить из этого в Badge Previewer.

G = group_id
X = минимальное количество лайков
Y = минимальное количество постов
SELECT pa.user_id, MIN(pa.post_id) as post_id, COUNT(pa.post_id) as post_count, COUNT(pa.id) as like_count, MAX(pa.created_at) as granted_at
FROM post_actions pa
JOIN group_users gu ON gu.user_id = pa.user_id
WHERE gu.group_id = G AND pa.post_action_type_id = 2
GROUP BY pa.user_id
HAVING COUNT(pa.post_id) >= Y AND COUNT(pa.id) >= X

Да, я назвал GP4bot Лолой

Я назвал своего Берт. :slight_smile: Хотя у нас сложные отношения.

Думаю, ещё одно ограничение такого типа запроса: использование MIN(p.created_at) granted_at возвращает дату его первого, а не, скажем, десятого. Можно изменить на MAX, но это тоже даст «неверную» дату, если выполнить запрос по историческим данным, где у него уже было больше 10.

Я всё ещё обдумываю этот момент.

У меня был некоторый успех с использованием ROW_NUMBER(), но пока ничего конкретного.

Да, я согласен. Что-то всё ещё кажется неправильным. Я иду спать. :sweat_smile:

Мне это нравится, и это помогает мне заново изучить SQL и научиться писать более качественные запросы. Lola / GPT4bot как помощник по SQL очень полезна, но её нужно направлять и задавать вопросы правильно. Я пытался найти способ предоставить ей доступ к большей части информации о схемах таблиц, чтобы не пришлось описывать её для каждой задачи по запросам. Предоставление информации о схеме таблиц даёт гораздо лучшие результаты. Я пробовал дать ей ссылку на доступные схемы в core, но это заставило её просто бродить по Google.

Мне интересно работать с ней, когда я буду знать, что предпросмотр запросов на значки работает. Мне нужно практиковаться в SQL и в запросах на значки. Кстати, она не может это исправить, и мой Earl Grey у неё всё ещё получается недостаточно горячим. Хотя вчерашний урок по SQL был лучшим свиданием за последние годы. :facepalm:

При использовании этого запроса у нас возникла странная проблема. Похоже, награды получили только сотрудники, хотя я почти уверен, что некоторые не-сотрудники тоже подходят под эти критерии. Это я где-то что-то сломал или проблема в самом запросе?

Да, я знаю, что что-то не так. Я займусь этим, как только мой инстанс обновится с исправлением предпросмотра.

Давайте начнём с чистого листа, так как, думаю, эти обсуждения меня запутали. :slight_smile:

Цель в том, чтобы выдавать значок за определённое количество постов во всех категориях, которые хотя бы раз лайкнул @staff?

Я планировал выдавать это пользователям, у которых есть X сообщений с Y лайками от сотрудников во всех категориях. В моём случае — 10 сообщений и 5 лайков.

После запутанного сбоя, вызванного удалёнными лайками, которые сбили мои тесты, я считаю, что это переработанная версия той, что в исходном посте, и она соответствует вашим критериям: :slight_smile:

SELECT p.user_id, MAX(p.created_at) granted_at
FROM badge_posts p
WHERE (SELECT COUNT(*)
        FROM post_actions pa
        WHERE pa.post_id = p.id
         AND post_action_type_id = 2 
         AND deleted_at IS NULL
         AND pa.user_id IN (SELECT gu.user_id FROM group_users gu WHERE gu.group_id = 3)
       ) >= 5
    AND p.user_id >= 0
GROUP BY p.user_id
HAVING COUNT(*) >= 10

Она работает на основе представления badge_posts, поэтому учитывает только посты из публичных категорий, что, возможно, стоит учесть в зависимости от настроек вашего форума и категорий. Также использование CURRENT_TIMESTAMP для поля granted_at — ещё один вариант, но это, скорее, вопрос личных предпочтений.