Использование полей SearchVectorFields на многих и многих связанных моделях

У меня есть две модели Author и Book, которые связаны через m2m (один автор может иметь много книг, одна книга может иметь много авторов)

Часто нам необходимо запрашивать и сопоставлять записи для входов, используя текстовые строки, в обеих моделях, например: "JRR Tolkien - Return of the King", когда уникальные идентификаторы недоступны.

Я хотел бы проверить, может ли использование SearchVectorField с индексами GIN улучшить время отклика полнотекстового поиска - но поскольку поисковый запрос будет SearchVector(author__name, book__title) Похоже, что обе модели нуждаются в добавлении SearchVectorField.

Это становится сложнее, когда каждая таблица требует обновления, поскольку оказывается, что Postgres Triggers должны быть настроены на обе таблицы, что может сделать обновление чего-либо совершенно несостоятельным.

Вопрос

Какова современная лучшая практика в Django для принятия векторных методов полнотекстового поиска, когда речь идет о моделях, связанных с m2m? Следует ли размещать SearchVectorField через таблицу? Или в каждой модели? Как следует применять триггеры?

Я искал руководства именно по этому вопросу - но никто, похоже, не упоминает m2ms, когда говорит о SearchVectorFields. Я нашел этот старый вопрос

Кроме того, если Postgres действительно не является путем вперед в современном Django, я также буду рад направить вас в сторону чего-то более подходящего/поддерживаемого/документированного. В нашем случае мы используем Postgres 11.6.

Repro

from django.db import models
from django.contrib.postgres.search import SearchVectorField
from django.contrib.postgres.indexes import GinIndex

class Author(models.Model):
    name = models.CharField(max_length=100, unique=True)
    main_titles = models.ManyToManyField(
        "Book",
        through="BookMainAuthor",
        related_name="main_authors",
    )
    search = SearchVectorField(null=True)

class BookMainAuthor(models.Model):
    """The m2m through table for book and author (main)"""

    book = models.ForeignKey("Book", on_delete=models.CASCADE)
    artist = models.ForeignKey("Author", on_delete=models.CASCADE)

    class Meta:
        unique_together = ["book", "author"]

class Book(models.Model):
    title = models.CharField(max_length=100, unique=True)
    search = SearchVectorField(null=True)

Наконец-то получилось. Я предполагаю, что вам нужно искать по запросу, содержащему автора и название книги одновременно. И вы не сможете разделить их, чтобы посмотреть на таблицу Book для части запроса "книга" и то же самое для Author.

Да, создание индекса полей из отдельных таблиц невозможно в PostgreSQL. Я не считаю это слабостью PostgreSQL, просто это очень необычный случай, когда вам действительно нужен такой индекс. В большинстве случаев есть другие решения, не худшие по эффективности. Конечно, вы всегда можете посмотреть на ElasticSearch, если по какой-то причине вы уверены, что это необходимо.

Я посоветую вам такой подход. Вы можете сделать BookMainAuthor с такой структурой:

class BookMainAuthor(models.Model):
    """The m2m through table for book and author (main)"""

    book = models.ForeignKey("Book", on_delete=models.CASCADE)
    artist = models.ForeignKey("Author", on_delete=models.CASCADE)
    book_full_name = models.CharField(max_length=200, unique=True)
    search = SearchVectorField(null=True)

    class Meta:
        unique_together = ["book", "author"]

Как мне кажется, не должно вызвать затруднений ведение поля book_full_name, которое будет содержать как имена автора, так и названия книг с соответствующим разделителем в нем. Все остальное - дело учебника.

Из моего опыта, если таблица BookMainAuthor будет содержать не более 10M записей, то на среднем одном сервере (например, как AX161 из здесь) все будет просто отлично.

Вернуться на верх