From f7e85b788cfa810cf0fb71e65693a04c0c9d4831 Mon Sep 17 00:00:00 2001 From: Klaas van Schelven Date: Thu, 27 Feb 2025 21:58:14 +0100 Subject: [PATCH] Tags: count at issue-level (model impl. only) --- tags/migrations/0001_initial.py | 5 +++-- tags/models.py | 12 +++++++++++- tags/tests.py | 13 +++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tags/migrations/0001_initial.py b/tags/migrations/0001_initial.py index 3cdf744..685ea1c 100644 --- a/tags/migrations/0001_initial.py +++ b/tags/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.19 on 2025-02-27 12:04 +# Generated by Django 4.2.19 on 2025-02-27 19:46 from django.db import migrations, models import django.db.models.deletion @@ -9,9 +9,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("events", "0019_event_storage_backend"), ("issues", "0010_issue_list_indexes"), ("projects", "0011_fill_stored_event_count"), + ("events", "0019_event_storage_backend"), ] operations = [ @@ -82,6 +82,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), + ("count", models.PositiveIntegerField(default=0)), ( "issue", models.ForeignKey( diff --git a/tags/models.py b/tags/models.py index b4f82ea..db0a743 100644 --- a/tags/models.py +++ b/tags/models.py @@ -18,7 +18,7 @@ https://docs.sentry.io/platforms/python/enriching-events/tags/ from django.db import models -from django.db.models import Q +from django.db.models import Q, F from projects.models import Project from tags.utils import deduce_tags @@ -79,12 +79,18 @@ class IssueTag(models.Model): value = models.ForeignKey(TagValue, blank=False, null=False, on_delete=models.CASCADE) issue = models.ForeignKey('issues.Issue', blank=False, null=True, on_delete=models.SET_NULL, related_name='tags') + count = models.PositiveIntegerField(default=0) class Meta: # searching: by value, then Issue (is this so though? .qs is already on the other!) unique_together = ('value', 'issue') indexes = [ models.Index(fields=['issue', 'value']), # make lookups by issue (for detail pages) faster + # a point _could_ be made for ['issue', 'value' 'count'] we'll probably order by the latter in the end. + # but I suspect that in practice the sorting on by count can be done easily for a small number of items + # (and I suspect that if there are very many items, sorting is not useful, because the sparse information + # does not lead to much insight... but I might be wrong (a few tags with many values, and a long tail would + # be the counter-example)) ] @@ -156,3 +162,7 @@ def store_tags(event, issue, tags): IssueTag.objects.bulk_create([ IssueTag(project_id=event.project_id, value=tag_value, issue=issue) for tag_value in tag_value_objects ], ignore_conflicts=True) + + IssueTag.objects.filter(project_id=event.project_id, value__in=tag_value_objects, issue=issue).update( + count=F('count') + 1 + ) diff --git a/tags/tests.py b/tags/tests.py index 18eaa26..d6d3d5b 100644 --- a/tags/tests.py +++ b/tags/tests.py @@ -22,17 +22,19 @@ class TagsTestCase(DjangoTestCase): self.assertEqual(self.event.tags.count(), 0) def test_store_1_tags(self): - with self.assertNumQueries(6): + with self.assertNumQueries(7): store_tags(self.event, self.issue, {"foo": "bar"}) self.assertEqual(self.event.tags.count(), 1) self.assertEqual(self.issue.tags.count(), 1) self.assertEqual(self.event.tags.first().value.value, "bar") + + self.assertEqual(self.issue.tags.first().count, 1) self.assertEqual(self.issue.tags.first().value.key.key, "foo") def test_store_5_tags(self): - with self.assertNumQueries(6): + with self.assertNumQueries(7): store_tags(self.event, self.issue, {f"k-{i}": f"v-{i}" for i in range(5)}) self.assertEqual(self.event.tags.count(), 5) @@ -40,3 +42,10 @@ class TagsTestCase(DjangoTestCase): self.assertEqual({"k-0", "k-1", "k-2", "k-3", "k-4"}, {tag.value.key.key for tag in self.event.tags.all()}) self.assertEqual({"v-0", "v-1", "v-2", "v-3", "v-4"}, {tag.value.value for tag in self.event.tags.all()}) + + def test_store_single_tag_twice_on_issue(self): + store_tags(self.event, self.issue, {"foo": "bar"}) + store_tags(create_event(self.project, self.issue), self.issue, {"foo": "bar"}) + + self.assertEqual(self.issue.tags.first().count, 2) + self.assertEqual(self.issue.tags.first().value.key.key, "foo")