기본 배지 쿼리

이것은 기본 배지의 SQL 쿼리 및 트리거 정보(가능한 경우)에 대한 참고 가이드입니다.

핵심 배지

:2nd_place_medal: 기념일 (Anniversary)

(이 배지는 날짜를 선택하기 위해 백엔드에서 몇 가지 추가 마법이 포함되어 있지만, 어쨌든 포함하겠습니다)

 start_date = start_date.iso8601(6)
    end_date = end_date.iso8601(6)

      SELECT u.id
        FROM users AS u
        JOIN posts AS p ON p.user_id = u.id
        JOIN topics AS t ON p.topic_id = t.id
       WHERE u.id > 0
         AND u.active
         AND NOT u.staged
         AND (u.silenced_till IS NULL OR u.silenced_till < '#{start_date}')
         AND (u.suspended_till IS NULL OR u.suspended_till < '#{start_date}')
         AND u.created_at <= '#{start_date}'
         AND NOT p.hidden
         AND p.deleted_at IS NULL
         AND p.created_at BETWEEN '#{start_date}' AND '#{end_date}'
         AND t.visible
         AND t.archetype <> 'private_message'
         AND t.deleted_at IS NULL
         AND NOT EXISTS (SELECT 1 FROM user_badges AS ub WHERE ub.user_id = u.id AND ub.badge_id = #{Badge::Anniversary} AND ub.granted_at BETWEEN '#{start_date}' AND '#{end_date}')
         AND NOT EXISTS (SELECT 1 FROM anonymous_users AS au WHERE au.user_id = u.id)
       GROUP BY u.id
      HAVING COUNT(p.id) > 0
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 감사 (여러 포스트에 대한 좋아요 수)

:3rd_place_medal: 감사, :2nd_place_medal: 존경, :1st_place_medal: 경외 배지는 동일한 패턴을 따르지만 p.like_countHAVING COUNT(*) 값이 다릅니다.

SELECT p.user_id, CURRENT_TIMESTAMP AS granted_at
      FROM posts AS p
      WHERE p.like_count >= #{like_count}
        AND (:backfill OR p.user_id IN (:user_ids))
      GROUP BY p.user_id
      HAVING COUNT(*) > #{post_count}
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 자서전가 (Autobiographer)

SELECT u.id user_id, CURRENT_TIMESTAMP granted_at
    FROM users u
    JOIN user_profiles up on u.id = up.user_id
    WHERE bio_raw IS NOT NULL AND LENGTH(TRIM(bio_raw)) > 10 AND
          uploaded_avatar_id IS NOT NULL AND
          (:backfill OR u.id IN (:user_ids) )
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 편집되거나 생성될 때

:3rd_place_medal: 기본 (신뢰 수준)

:3rd_place_medal: 기본, :3rd_place_medal: 멤버, :2nd_place_medal: 정기, :1st_place_medal: 리더 배지는 모두 동일한 패턴을 따르지만 trust_level 값이 다릅니다.

SELECT u.id user_id, current_timestamp granted_at FROM users u
      WHERE trust_level >= #{level.to_i} AND (
        :backfill OR u.id IN (:user_ids)
      )
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 신뢰 수준을 변경할 때

:3rd_place_medal: 인증 (Certified) 및 :2nd_place_medal: 면허 (Licensed)

이 배지들은 SQL 쿼리가 없습니다. Discourse Narrative Bot의 일부이며, 사용자가 인터랙티브 튜토리얼(Discobot)을 완료할 때 프로그래밍 방식으로 부여됩니다.


:3rd_place_medal: 편집자 (Editor)

 SELECT p.user_id, min(p.id) post_id, min(p.created_at) granted_at
    FROM badge_posts p
    WHERE p.self_edits > 0 AND
        (:backfill OR p.id IN (:post_ids) )
    GROUP BY p.user_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 포스트를 편집하거나 생성할 때

:3rd_place_medal: 열성 팬 (Enthusiast)

:3rd_place_medal: 열성 팬, :2nd_place_medal: 애호가, :1st_place_medal: 충실한 팬 배지는 동일한 패턴을 따르지만 HAVING COUNT(*) 임계값이 다릅니다.

WITH consecutive_visits AS (
        SELECT user_id
             , visited_at
             , visited_at - (DENSE_RANK() OVER (PARTITION BY user_id ORDER BY visited_at))::int s
          FROM user_visits
      ), visits AS (
        SELECT user_id
             , MIN(visited_at) "start"
             , DENSE_RANK() OVER (PARTITION BY user_id ORDER BY s) "rank"
          FROM consecutive_visits
      GROUP BY user_id, s
        HAVING COUNT(*) >= #{days}
      )
      SELECT user_id
           , "start" + interval '#{days} days' "granted_at"
        FROM visits
       WHERE "rank" = 1
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 첫 이모지 (First Emoji)

SQL 쿼리는 없습니다. CookedPostProcessor#grant_badges를 통해 자격을 갖춘 포스트가 처리되는 순간 배지가 부여됩니다.

기준: cooked 포스트에는 aside.quote 블록 안에 있지 않은 img.emoji 요소가 적어도 하나 포함되어야 합니다.换句话说, 포스트 본문에 직접 입력된 이모지는 계산되지만, 인용된 문단에만 나타나는 이모지는 계산되지 않습니다.

매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 포스트 쿠킹 (CookedPostProcessor)

:3rd_place_medal: 첫 플래그 (First Flag)

SELECT pa1.user_id, pa1.created_at granted_at, pa1.post_id
    FROM (
      SELECT pa.user_id, MIN(pa.id) id
      FROM post_actions pa
      JOIN badge_posts p on p.id = pa.post_id
      WHERE post_action_type_id IN (
        SELECT f.id
        FROM flags f
        WHERE name != 'like'
        AND score_type IS FALSE
        AND require_message IS FALSE
      )
      AND (:backfill OR pa.post_id IN (:post_ids))
      GROUP BY pa.user_id
    ) x
    JOIN post_actions pa1 on pa1.id = x.id
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트에 대해 조치할 때

:3rd_place_medal: 첫 좋아요 (First Like)

SELECT pa1.user_id, pa1.created_at granted_at, pa1.post_id
    FROM (
      SELECT pa.user_id, MIN(pa.id) id
      FROM post_actions pa
      JOIN badge_posts p on p.id = pa.post_id
      WHERE post_action_type_id = 2 AND
        (:backfill OR pa.post_id IN (:post_ids) )
      GROUP BY pa.user_id
    ) x
    JOIN post_actions pa1 on pa1.id = x.id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트에 대해 조치할 때

:3rd_place_medal: 첫 링크 (First Link)

SELECT l.user_id, l.post_id, l.created_at granted_at
    FROM
    (
      SELECT MIN(l1.id) id
      FROM topic_links l1
      JOIN badge_posts p1 ON p1.id = l1.post_id
      JOIN badge_posts p2 ON p2.id = l1.link_post_id
      WHERE NOT reflection AND p1.topic_id <> p2.topic_id AND not quote AND
        (:backfill OR ( p1.id in (:post_ids) ))
      GROUP BY l1.user_id
    ) ids
    JOIN topic_links l ON l.id = ids.id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트를 편집하거나 생성할 때

:3rd_place_medal: 첫 멘션 (First Mention)

SELECT acting_user_id AS user_id, MIN(target_post_id) AS post_id, MIN(p.created_at) AS granted_at
    FROM user_actions
    JOIN posts p ON p.id = target_post_id
    JOIN topics t ON t.id = topic_id
    JOIN categories c on c.id = category_id
    WHERE action_type = 7
      AND NOT read_restricted
      AND p.deleted_at IS  NULL
      AND t.deleted_at IS  NULL
      AND t.visible
      AND t.archetype <> 'private_message'
      AND (:backfill OR p.id IN (:post_ids))
    GROUP BY acting_user_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트를 편집하거나 생성할 때

:3rd_place_medal: 첫 원박스 (First Onebox)

SQL 쿼리는 없습니다. CookedPostProcessor#grant_badges를 통해 포스트 쿠킹 중에 부여됩니다.

기준: cooked 포스트는 적어도 하나의 원박스(onebox)를 생성해야 합니다. 원박스는 단독 줄에 있는 맨 URL이 임베드 가능한 리소스로 해석될 때 생성되는 풍부한 링크 미리보기입니다.

매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 포스트 쿠킹 (CookedPostProcessor)

:3rd_place_medal: 첫 인용 (First Quote)

SELECT ids.user_id, q.post_id, p3.created_at granted_at
    FROM
    (
      SELECT p1.user_id, MIN(q1.id) id
      FROM quoted_posts q1
      JOIN badge_posts p1 ON p1.id = q1.post_id
      JOIN badge_posts p2 ON p2.id = q1.quoted_post_id
      WHERE (:backfill OR ( p1.id IN (:post_ids) ))
      GROUP BY p1.user_id
    ) ids
    JOIN quoted_posts q ON q.id = ids.id
    JOIN badge_posts p3 ON q.post_id = p3.id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트를 편집하거나 생성할 때

:3rd_place_medal: 첫 이메일 답변 (First Reply-by-Email)

SQL 쿼리는 없습니다. CookedPostProcessor#grant_badges를 통해 포스트 쿠킹 중에 부여됩니다.

기준: post.is_reply_by_email?true여야 합니다. 즉, 포스트가 웹 인터페이스를 통해而不是 Discourse 알림 이메일에 답변하여 제출된 것입니다. 사이트에 수신 이메일 답변 기능이 활성화되어 있어야 합니다.

매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 포스트 쿠킹 (CookedPostProcessor)

원본 커뮤니티 제안 'Reply by email' badge - #3 by lrossouw

:3rd_place_medal: 첫 공유 (First Share)

SELECT views.user_id, i2.post_id, i2.created_at granted_at
    FROM
    (
      SELECT i.user_id, MIN(i.id) i_id
      FROM incoming_links i
      JOIN badge_posts p on p.id = i.post_id
      JOIN users u on u.id = i.user_id
      GROUP BY i.user_id
    ) as views
    JOIN incoming_links i2 ON i2.id = views.i_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 매일 업데이트

:3rd_place_medal: 이달의 신규 사용자 (New User of the Month)

배지에 대해 등록된 SQL 쿼리는 없습니다. 일일 예약 작업(Jobs::GrantNewUserOfTheMonthBadges)에 의해 부여됩니다. 이 작업은 매일 실행되지만 이전 달력에 대한 것만 수여하며, 한 달에 한 번만 수여합니다.

자격 요건: 후보자는 이전 달력 달에 계정을 생성하고, 활성 상태이며 스테이지 상태가 아니어야 하며, 관리자나 모더레이터가 아니어야 하고, 정지되어 있지 않아야 합니다. 또한 적어도 2개의 다른 주제에서 총 2개의 포스트를 작성하고, 적어도 2개의 좋아요를 받아야 합니다.

점수 계산: 후보자는 가중 좋아요 점수로 순위가 결정됩니다. 받은 각 좋아요는 좋아요를 준 사람의 신뢰 수준에 따라 가중치가 부여됩니다:

좋아요를 준 사람 가중치
관리자 또는 모더레이터 3.0
신뢰 수준 4 2.0
신뢰 수준 3 1.5
신뢰 수준 2 1.0
신뢰 수준 1 0.25
신뢰 수준 0 0.1

최종 점수는 SUM(가중 좋아요) / (5 + 포스트 수)입니다. 포스트 수로 나누는 것은 다작하는 포스터의 이점을 완화합니다. 한 달에 최대 2명의 사용자가 배지를 받을 수 있습니다. 각 수상자에게는 시스템 메시지도 전송됩니다.

매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 일일 예약 작업 (이전 달력 달에 대해 수여)

:3rd_place_medal: 좋은 답변 (포스트에 대한 좋아요)

:3rd_place_medal: 좋은 답변, :2nd_place_medal: 훌륭한 답변, :1st_place_medal: 멋진 답변 배지는 모두 동일한 패턴을 따르지만 p.like_count 임계값이 다릅니다.

SELECT p.user_id, p.id post_id, CURRENT_TIMESTAMP granted_at
      FROM badge_posts p
      WHERE p.post_number > 1 AND p.like_count >= #{count.to_i} AND
        (:backfill OR p.id IN (:post_ids) )
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트에 대해 조치할 때

:3rd_place_medal: 좋은 공유 (링크 공유)

:3rd_place_medal: 좋은 공유, :2nd_place_medal: 훌륭한 공유, :1st_place_medal: 멋진 공유 배지는 동일한 패턴을 따르지만 HAVING COUNT(*) 임계값이 다릅니다.

SELECT views.user_id, i2.post_id, CURRENT_TIMESTAMP granted_at
      FROM
      (
        SELECT i.user_id, MIN(i.id) i_id
        FROM incoming_links i
        JOIN badge_posts p on p.id = i.post_id
        JOIN users u on u.id = i.user_id
        GROUP BY i.user_id,i.post_id
        HAVING COUNT(DISTINCT(i.ip_address, i.current_user_id)) >= #{count}
      ) as views
      JOIN incoming_links i2 ON i2.id = views.i_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 매일 업데이트

:3rd_place_medal: 좋은 주제 (주제에 대한 좋아요)

:3rd_place_medal: 좋은 주제, :2nd_place_medal: 훌륭한 주제, :1st_place_medal: 멋진 주제 배지는 모두 동일한 패턴을 따르지만 p.like_count 임계값이 다릅니다.

SELECT p.user_id, p.id post_id, CURRENT_TIMESTAMP granted_at
      FROM badge_posts p
      WHERE p.post_number = 1 AND p.like_count >= #{count.to_i} AND
        (:backfill OR p.id IN (:post_ids) )
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트에 대해 조치할 때

:3rd_place_medal: 사랑에 빠짐 (하루 최대 좋아요)

:3rd_place_medal: 사랑에 빠짐, :2nd_place_medal: 더 깊은 사랑, :1st_place_medal: 사랑에 미치다 배지는 모두 동일한 패턴을 따르지만 HAVING COUNT(*) 임계값이 다릅니다.

SELECT gdl.user_id, CURRENT_TIMESTAMP AS granted_at
      FROM given_daily_likes AS gdl
      WHERE gdl.limit_reached
        AND (:backfill OR gdl.user_id IN (:user_ids))
      GROUP BY gdl.user_id
      HAVING COUNT(*) >= #{count}
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 인기 링크 (링크 클릭)

:3rd_place_medal: 인기 링크, :2nd_place_medal: 뜨거운 링크, :1st_place_medal: 유명한 링크는 모두 동일한 패턴을 따르지만 tl.clicks 임계값이 다릅니다.

SELECT tl.user_id, post_id, CURRENT_TIMESTAMP granted_at
        FROM topic_links tl
        JOIN badge_posts p ON p.id = post_id
       WHERE NOT tl.internal
         AND tl.clicks >= #{count}
      GROUP BY tl.user_id, tl.post_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 매일 업데이트

:3rd_place_medal: 홍보대사 (초대)

:3rd_place_medal: 홍보대사, :2nd_place_medal: 캠페이너, :1st_place_medal: 챔피언 배지는 모두 동일한 패턴을 따르지만 초대받은 사람에게 필요한 신뢰 수준 값과 HAVING COUNT(*) 임계값이 다릅니다.

SELECT u.id user_id, CURRENT_TIMESTAMP granted_at
      FROM users u
      WHERE u.id IN (
        SELECT invited_by_id
        FROM invites i
        JOIN invited_users iu ON iu.invite_id = i.id
        JOIN users u2 ON u2.id = iu.user_id
        WHERE i.deleted_at IS NULL
        AND i.invited_by_id <> u2.id
        AND u2.active
        AND u2.trust_level >= #{trust_level.to_i}
        AND u2.silenced_till IS NULL
        GROUP BY invited_by_id
        HAVING COUNT(*) >= #{count.to_i}
      ) AND u.active AND u.silenced_till IS NULL AND u.id > 0 AND
      (:backfill OR u.id IN (:user_ids) )
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 가이드라인 읽기 (Read Guidelines)

 SELECT user_id, read_faq granted_at
    FROM user_stats
    WHERE read_faq IS NOT NULL AND (user_id IN (:user_ids) OR :backfill)
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 편집되거나 생성될 때

:3rd_place_medal: 독자 (Reader)

SELECT id user_id, CURRENT_TIMESTAMP granted_at
    FROM users
    WHERE id IN
    (
      SELECT pt.user_id
      FROM post_timings pt
      JOIN badge_posts b ON b.post_number = pt.post_number AND
                            b.topic_id = pt.topic_id
      JOIN topics t ON t.id = pt.topic_id
      LEFT JOIN user_badges ub ON ub.badge_id = 17 AND ub.user_id = pt.user_id
      WHERE ub.id IS NULL AND t.posts_count > 100
      GROUP BY pt.user_id, pt.topic_id, t.posts_count
      HAVING COUNT(*) >= t.posts_count
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거

:3rd_place_medal: 감사합니다 (준 좋아요 + 받은 좋아요)

:3rd_place_medal: 감사합니다, :2nd_place_medal: 돌려주기, :1st_place_medal: 공감 배지는 us.likes_givenHAVING COUNT(*) 값이 다른 동일한 패턴을 따릅니다.

SELECT us.user_id, CURRENT_TIMESTAMP granted_at
      FROM user_stats AS us
      INNER JOIN posts AS p ON p.user_id = us.user_id
      WHERE p.like_count > 0
        AND us.likes_given >= #{likes_given}
        AND (:backfill OR us.user_id IN (:user_ids))
      GROUP BY us.user_id, us.likes_given
      HAVING COUNT(*) > #{likes_received}
매일 폐지 쿼리 실행
쿼리가 포스트를 대상으로 함
트리거 매일 업데이트

:3rd_place_medal: 환영 (Welcome)

SELECT p.user_id, MIN(post_id) post_id, MIN(pa.created_at) granted_at
    FROM post_actions pa
    JOIN badge_posts p on p.id = pa.post_id
    WHERE post_action_type_id = 2 AND
        (:backfill OR pa.post_id IN (:post_ids) )
    GROUP BY p.user_id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트에 대해 조치할 때

:3rd_place_medal: 위키 편집자 (Wiki Editor)

SELECT pr2.user_id, pr2.post_id, pr2.created_at granted_at
    FROM
    (
      SELECT MIN(pr.id) id
      FROM post_revisions pr
      JOIN badge_posts p on p.id = pr.post_id
      WHERE p.wiki
          AND NOT pr.hidden
          AND (:backfill OR p.id IN (:post_ids))
      GROUP BY pr.user_id
    ) as X
    JOIN post_revisions pr2 ON pr2.id = X.id
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트를 편집하거나 생성할 때

Discourse 리액션

:3rd_place_medal: 첫 리액션 (First Reaction)

SELECT user_id, created_at AS granted_at, post_id
  FROM (
           SELECT ru.post_id, ru.user_id, ru.created_at,
                  ROW_NUMBER() OVER (PARTITION BY ru.user_id ORDER BY ru.created_at) AS row_number
           FROM discourse_reactions_reaction_users ru
                JOIN badge_posts p ON ru.post_id = p.id
           WHERE :backfill
              OR ru.post_id IN (:post_ids)
       ) x
  WHERE row_number = 1
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함 :white_check_mark:
트리거 사용자가 포스트를 편집하거나 생성할 때

Discourse 해결됨

:3rd_place_medal: 해결됨! (Solved!)

 SELECT post_id, user_id, created_at AS granted_at
  FROM (
           SELECT p.id AS post_id, p.user_id, dsst.created_at,
              ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY dsst.created_at) AS row_number
           FROM discourse_solved_solved_topics dsst
              JOIN badge_posts p ON dsst.answer_post_id = p.id
              JOIN topics t ON p.topic_id = t.id
           WHERE p.user_id <> t.user_id -- OP가 해결한 주제 무시
              AND (:backfill OR p.id IN (:post_ids))
       ) x
  WHERE row_number = 1
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 포스트를 편집하거나 생성할 때

:2nd_place_medal: 상담사 (Guidance Counsellor)

:2nd_place_medal: 상담사, :1st_place_medal: 만물통, :1st_place_medal: 해결사 기관 배지는 모두 동일한 패턴을 따르지만 HAVING COUNT (*) >= 임계값이 다릅니다:

SELECT p.user_id, MAX(pcf.created_at) AS granted_at
   FROM post_custom_fields pcf
        JOIN badge_posts p ON pcf.post_id = p.id
        JOIN topics t ON p.topic_id = t.id
   WHERE pcf.name = 'is_accepted_answer'
     AND p.user_id <> t.user_id -- OP가 해결한 주제 무시
     AND (:backfill OR p.id IN (:post_ids))
   GROUP BY p.user_id
   HAVING COUNT(*) >= #{min_count}
매일 폐지 쿼리 실행 :white_check_mark:
쿼리가 포스트를 대상으로 함
트리거 사용자가 포스트를 편집하거나 생성할 때

Discourse Github

이것들은 읽을 수 없습니다. :upside_down:

소스:

This is awesome @JammyDodger thank for doing this helpful topic! :slight_smile: