Browse Source

wip #844: untested PgPartialIndex class

Rafał Pitoń 8 years ago
parent
commit
fceaed9a96

+ 91 - 0
misago/core/pgutils.py

@@ -1,5 +1,96 @@
 from django.core.paginator import Paginator
+from django.db.models import Index
 from django.db.migrations.operations import RunSQL
+from django.utils.six import text_type
+
+
+class PgPartialIndex(Index):
+    suffix = 'part'
+    max_name_length = 31
+
+    def __init__(self, fields=[], name=None, where=None):
+        if not where:
+            raise ValueError('partial index requires WHERE clause')
+        self.where = where
+
+        super(PartialIndex, self).__init__(fields, name)
+
+    def set_name_with_model(self, model):
+        table_name = model._meta.db_table
+
+        column_names = sorted(self.where.keys())
+        where_items = []
+        for key in sorted(self.where.keys()):
+            where_items.append('{}:{}'.format(key, repr(self.where[key])))
+
+        # The length of the parts of the name is based on the default max
+        # length of 30 characters.
+        hash_data = [table_name] + where_items + [self.suffix]
+        self.name = '%s_%s_%s' % (
+            table_name[:11],
+            column_names[0][:7],
+            '%s_%s' % (self._hash_generator(*hash_data), self.suffix),
+        )
+
+        assert len(self.name) <= self.max_name_length, (
+            'Index too long for multiple database support. Is self.suffix '
+            'longer than 3 characters?'
+        )
+        self.check_name()
+
+    def __repr__(self):
+        if self.where is not None:
+            where_items = []
+            for key in sorted(self.where.keys()):
+                where_items.append('='.join([
+                    key,
+                    repr(self.where[key])
+                ]))
+            return '<%(name)s: fields=%(fields)s, where=%(where)s>' % {
+                'name': self.__class__.__name__,
+                'fields': "'{}'".format(', '.join(self.fields)),
+                'where': "'{}'".format(', '.join(where_items)),
+            }
+        else:
+            return super(PartialIndex, self).__repr__()
+
+    def deconstruct(self):
+        path, args, kwargs = super(PartialIndex, self).deconstruct()
+        kwargs['where'] = self.where
+        return path, args, kwargs
+
+    def get_sql_create_template_values(self, model, schema_editor, using):
+        parameters = super(PartialIndex, self).get_sql_create_template_values(model, schema_editor, '')
+        parameters['extra'] = self.get_sql_extra(schema_editor)
+        return parameters
+
+    def get_sql_extra(self, schema_editor):
+        quote_name = schema_editor.quote_name
+        quote_value = schema_editor.quote_value
+
+        clauses = []
+        for field, condition in self.where.items():
+            fieldname = None
+            compr = None
+            if field.endswith('__lt'):
+                fieldname = field[:-4]
+                compr = '<'
+            elif field.endswith('__gt'):
+                fieldname = field[:-4]
+                compr = '>'
+            elif field.endswith('__lte'):
+                fieldname = field[:-5]
+                compr = '<='
+            elif field.endswith('__gte'):
+                fieldname = field[:-5]
+                compr = '>='
+            else:
+                fieldname = field
+                compr = '='
+
+            clauses.append('{} {} {}'.format(
+                quote_name(fieldname), compr, quote_value(condition)))
+        return ' WHERE {}'.format(' AND '.join(clauses))
 
 
 class CreatePartialIndex(RunSQL):

+ 64 - 0
misago/threads/migrations/0006_redo_partial_indexes.py

@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-21 17:52
+from __future__ import unicode_literals
+
+import django.contrib.postgres.indexes
+from django.contrib.postgres.operations import BtreeGinExtension
+from django.db import migrations
+
+import misago.core.pgutils
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('misago_threads', '0005_index_search_document'),
+    ]
+
+    operations = [
+        migrations.RunSQL(
+            "DROP INDEX misago_post_has_open_reports_partial",
+            """
+                CREATE INDEX misago_post_has_open_reports_partial
+                ON misago_threads_post(has_open_reports)
+                WHERE has_open_reports = TRUE
+            """,
+        ),
+
+        migrations.AddIndex(
+            model_name='post',
+            index=misago.core.pgutils.PgPartialIndex(fields=['has_open_reports'], name='misago_thre_has_ope_92be2c_part', where={'has_open_reports': True}),
+        ),
+        migrations.AddIndex(
+            model_name='post',
+            index=misago.core.pgutils.PgPartialIndex(fields=['is_hidden'], name='misago_thre_is_hidd_29a6b4_part', where={'is_hidden': False}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'weight'], name='misago_thre_weight_78926f_part', where={b'weight': 2}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'weight'], name='misago_thre_weight_525ed7_part', where={b'weight': 1}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'weight'], name='misago_thre_weight_5810c3_part', where={b'weight': 0}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'weight'], name='misago_thre_weight__1981ff_part', where={b'weight__lt': 2}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'has_reported_posts'], name='misago_thre_has_rep_8867fa_part', where={b'has_reported_posts': True}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'has_unapproved_posts'], name='misago_thre_has_una_c66880_part', where={b'has_unapproved_posts': True}),
+        ),
+        migrations.AddIndex(
+            model_name='thread',
+            index=misago.core.pgutils.PgPartialIndex(fields=[b'is_hidden'], name='misago_thre_is_hidd_0aef24_part', where={b'is_hidden': False}),
+        ),
+    ]

+ 10 - 1
misago/threads/models/post.py

@@ -10,6 +10,7 @@ from django.utils import six, timezone
 from django.utils.encoding import python_2_unicode_compatible
 
 from misago.conf import settings
+from misago.core.pgutils import PgPartialIndex
 from misago.core.utils import parse_iso8601_string
 from misago.markup import finalise_markup
 from misago.threads.checksums import is_post_valid, update_post_checksum
@@ -90,7 +91,15 @@ class Post(models.Model):
 
     class Meta:
         indexes = [
-            GinIndex(fields=['search_vector'])
+            PgPartialIndex(
+                fields=['has_open_reports'],
+                where={'has_open_reports': True},
+            ),
+            PgPartialIndex(
+                fields=['is_hidden'],
+                where={'is_hidden': False},
+            ),
+            GinIndex(fields=['search_vector']),
         ]
 
         index_together = [

+ 32 - 0
misago/threads/models/thread.py

@@ -4,6 +4,7 @@ from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 
 from misago.conf import settings
+from misago.core.pgutils import PgPartialIndex
 from misago.core.utils import slugify
 
 
@@ -85,6 +86,37 @@ class Thread(models.Model):
     )
 
     class Meta:
+        indexes = [
+            PgPartialIndex(
+                fields=['weight'],
+                where={'weight': 2},
+            ),
+            PgPartialIndex(
+                fields=['weight'],
+                where={'weight': 1},
+            ),
+            PgPartialIndex(
+                fields=['weight'],
+                where={'weight': 0},
+            ),
+            PgPartialIndex(
+                fields=['weight'],
+                where={'weight__lt': 2},
+            ),
+            PgPartialIndex(
+                fields=['has_reported_posts'],
+                where={'has_reported_posts': True},
+            ),
+            PgPartialIndex(
+                fields=['has_unapproved_posts'],
+                where={'has_unapproved_posts': True},
+            ),
+            PgPartialIndex(
+                fields=['is_hidden'],
+                where={'is_hidden': False},
+            ),
+        ]
+
         index_together = [
             ['category', 'id'],
             ['category', 'last_post_on'],