mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FIX: Update category tag stats with new or deleted (#21531)
The old method updated only existing records, without considering that new tags might have been created or some tags might not exist anymore. This was usually not a problem because the stats were also updated by other code paths. However, the ensure consistency job should be more solid and help when other code paths fail or after importing data. Also, update category tag stats too should happen when updating other category stats as well.
This commit is contained in:
parent
809bab5782
commit
f0ec1fad8c
@ -44,23 +44,38 @@ class CategoryTagStat < ActiveRecord::Base
|
|||||||
|
|
||||||
# Recalculate all topic counts if they got out of sync
|
# Recalculate all topic counts if they got out of sync
|
||||||
def self.update_topic_counts
|
def self.update_topic_counts
|
||||||
|
# Add new records or update existing records
|
||||||
DB.exec <<~SQL
|
DB.exec <<~SQL
|
||||||
UPDATE category_tag_stats stats
|
WITH stats AS (
|
||||||
SET topic_count = x.topic_count
|
SELECT topics.category_id as category_id,
|
||||||
FROM (
|
|
||||||
SELECT COUNT(topics.id) AS topic_count,
|
|
||||||
tags.id AS tag_id,
|
tags.id AS tag_id,
|
||||||
topics.category_id as category_id
|
COUNT(topics.id) AS topic_count
|
||||||
FROM tags
|
FROM tags
|
||||||
INNER JOIN topic_tags ON tags.id = topic_tags.tag_id
|
INNER JOIN topic_tags ON tags.id = topic_tags.tag_id
|
||||||
INNER JOIN topics ON topics.id = topic_tags.topic_id
|
INNER JOIN topics ON topics.id = topic_tags.topic_id
|
||||||
AND topics.deleted_at IS NULL
|
AND topics.deleted_at IS NULL
|
||||||
AND topics.category_id IS NOT NULL
|
AND topics.category_id IS NOT NULL
|
||||||
GROUP BY tags.id, topics.category_id
|
GROUP BY topics.category_id, tags.id
|
||||||
) x
|
)
|
||||||
WHERE stats.tag_id = x.tag_id
|
INSERT INTO category_tag_stats(category_id, tag_id, topic_count)
|
||||||
AND stats.category_id = x.category_id
|
SELECT category_id, tag_id, topic_count FROM stats
|
||||||
AND x.topic_count <> stats.topic_count
|
ON CONFLICT (category_id, tag_id) DO
|
||||||
|
UPDATE SET topic_count = EXCLUDED.topic_count
|
||||||
|
SQL
|
||||||
|
|
||||||
|
# Delete old records
|
||||||
|
DB.exec <<~SQL
|
||||||
|
DELETE FROM category_tag_stats
|
||||||
|
WHERE (category_id, tag_id) NOT IN (
|
||||||
|
SELECT topics.category_id as category_id,
|
||||||
|
tags.id AS tag_id
|
||||||
|
FROM tags
|
||||||
|
INNER JOIN topic_tags ON tags.id = topic_tags.tag_id
|
||||||
|
INNER JOIN topics ON topics.id = topic_tags.topic_id
|
||||||
|
AND topics.deleted_at IS NULL
|
||||||
|
AND topics.category_id IS NOT NULL
|
||||||
|
GROUP BY topics.category_id, tags.id
|
||||||
|
)
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -24,6 +24,7 @@ task "categories:move_topics", %i[from_category to_category] => [:environment] d
|
|||||||
|
|
||||||
puts "Updating category stats..."
|
puts "Updating category stats..."
|
||||||
Category.update_stats
|
Category.update_stats
|
||||||
|
CategoryTagStat.update_topic_counts
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "", "Done!", ""
|
puts "", "Done!", ""
|
||||||
|
39
spec/models/category_tag_stat_spec.rb
Normal file
39
spec/models/category_tag_stat_spec.rb
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe CategoryTagStat do
|
||||||
|
fab!(:category) { Fabricate(:category) }
|
||||||
|
fab!(:tag) { Fabricate(:tag) }
|
||||||
|
fab!(:topic) { Fabricate(:topic, category: category, tags: [tag]) }
|
||||||
|
|
||||||
|
describe "#update_topic_counts" do
|
||||||
|
it "creates new records" do
|
||||||
|
CategoryTagStat.destroy_all
|
||||||
|
|
||||||
|
expect { CategoryTagStat.update_topic_counts }.to change { CategoryTagStat.count }.by(1)
|
||||||
|
category_tag_stat = CategoryTagStat.last
|
||||||
|
expect(category_tag_stat.category_id).to eq(category.id)
|
||||||
|
expect(category_tag_stat.tag_id).to eq(tag.id)
|
||||||
|
expect(category_tag_stat.topic_count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates existing records" do
|
||||||
|
CategoryTagStat.last.update(topic_count: 10)
|
||||||
|
|
||||||
|
expect { CategoryTagStat.update_topic_counts }.not_to change { CategoryTagStat.count }
|
||||||
|
category_tag_stat = CategoryTagStat.last
|
||||||
|
expect(category_tag_stat.category_id).to eq(category.id)
|
||||||
|
expect(category_tag_stat.tag_id).to eq(tag.id)
|
||||||
|
expect(category_tag_stat.topic_count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "deletes old records" do
|
||||||
|
CategoryTagStat.last.update(tag_id: Fabricate(:tag).id)
|
||||||
|
|
||||||
|
expect { CategoryTagStat.update_topic_counts }.not_to change { CategoryTagStat.count }
|
||||||
|
category_tag_stat = CategoryTagStat.last
|
||||||
|
expect(category_tag_stat.category_id).to eq(category.id)
|
||||||
|
expect(category_tag_stat.tag_id).to eq(tag.id)
|
||||||
|
expect(category_tag_stat.topic_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user