1. jmotor

    19.11.2008 13:47

    Привет всем!

    Иметь TsvectorField на вооружении очевидно удобно.
    Для наследников мы, теоретически, получаем правильный SQL JOIN.
    Застрял на реализации метода get_db_prep_lookup.

    Заготовка для поля, аккумулирующего лексемы объекта, выглядит так:
    from django.db.models import Field

    class TsvectorField(Field):
    """
    PostgreSQL tsvector field.
    """
    def __init__(self, *args, **kwargs):
    kwargs['editable'] = False
    kwargs['null'] = True
    super(TsvectorField, self).__init__(*args, **kwargs)

    def db_type(self):
    from django.conf import settings
    if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql'):
    return 'tsvector'
    return None

    def get_db_prep_lookup(self, lookup_type, value):
    """
    Returns field's value prepared for database lookup.
    """
    if lookup_type == 'tsquery':
    return ['@@ plainto_tsquery(%s)' % value]

    raise TypeError('Dude, TsvectorField has invalid lookup: %s' % lookup_type)
    Результирующий SQL должен содержать примерно следующее:
    ... WHERE tsv @@ plainto_tsquery('firewall') ;
    где 'firewall' – пример запроса, plainto_tsquery – встроенная функция postgres, @@ – оператор.

    Метод get_db_prep_lookup в заготовке не работает:
    FieldError: Join on field 'tsv' not permitted.

    Соответственно django-код запроса выглядит как-то так:
    Trap.objects.filter(tsv__tsquery='firewall')
    TrapClean.objects.filter(tsv__tsquery='switch')
    Уважаемые коллеги, где копать-то?


    django 1.0.x branch:
    http://code.djangoproject.com/svn/django/branches/releases/1.0.X/

    Код моделей на всякий случай:
    from django.db import models
    from string import printable
    from fields import TsvectorField


    class TrapCommon(models.Model):
    """
    Common SNMPTT data.
    See http://www.snmptt.org/docs/snmptt.shtml#LoggingDatabase
    """
    trapoid = models.CharField(max_length=255, blank=True, db_index=True)
    enterprise = models.CharField(max_length=255, blank=True, db_index=True)
    community = models.CharField(max_length=60, blank=True, db_index=True)
    hostname = models.CharField(max_length=255, blank=True, db_index=True)
    agentip = models.CharField(max_length=48, blank=True, db_index=True)
    uptime = models.CharField(max_length=60, blank=True)
    traptime = models.DateTimeField(db_index=True)
    formatline = models.TextField(blank=True)
    tsv = TsvectorField()

    class Meta:
    abstract = True
    ordering = ('-traptime',)
    get_latest_by = 'traptime'

    def __unicode__(self):
    fmt = ''.join([c for c in self.formatline if c in printable])
    return '%s - %s - %s' % (self.traptime, self.hostname.split('.')[0], fmt)


    class Trap(TrapCommon):
    """
    Traps caught by SNMP Trap Translator.
    """
    eventname = models.CharField(max_length=150, blank=True, db_index=True)
    eventid = models.CharField(max_length=150, blank=True, db_index=True)
    category = models.CharField(max_length=60, blank=True, db_index=True)
    severity = models.CharField(max_length=60, blank=True, db_index=True)

    class Meta(TrapCommon.Meta):
    db_table = u'snmptt'


    class TrapClean(Trap):
    """
    Traps caught by SNMP Trap Translator and passed spam filter validation.
    """
    class Meta(Trap.Meta):
    verbose_name = u'SNMP Trap'
    verbose_name_plural = u'SNMP Traps'
    Материал по теме:

    Введение в полнотекстовый поиск в PostgreSQL:
    http://www.sai.msu.su/~megera/postgres/talks/fts_pgsql_intro.html


    Заранее всем спасибо!
  2. Иван Сагалаев

    20.11.2008 16:23

    Это нерешенный пока вопрос. Дело в том, что у Джанги захардкожено то, какие lookup-типы она может принимать, и какие SQL-операторы из них делать. И одного get_db_prep_lookup тут совсем недостаточно. По сути он только умеет форматировать значения, участвующие в запросах, но не позволяет определять новый lookup-тип.

    Я как-то решил грубой силой с monkey-патчингом эту задачку на работе, чтобы можно было писать .filter(time__period='today_future'). Пришлось написать свой менеджер, свой QuerySet, свой Query и свой WhereNode. Код, к сожалению, не под рукой сейчас...

  3. jmotor

    21.11.2008 16:08

    Найдено временное решение!

    fields.py:

    ———————————-8<———————————-
    from django.db.models import Field

    class TsvectorField(Field):
    """
    PostgreSQL tsvector field.
    """
    def __init__(self, *args, **kwargs):
    kwargs['editable'] = False
    kwargs['null'] = True
    super(TsvectorField, self).__init__(*args, **kwargs)

    def db_type(self):
    from django.conf import settings
    if settings.DATABASE_ENGINE.startswith('postgres'):
    return 'tsvector'
    return None
    ———————————-8<———————————-



    В файле __init__.py (в той же директории):

    ———————————-8<———————————-
    def fulltext_search_sql(self, field_name):
    return ('%s @@ plainto_tsquery(%%s)' % field_name)

    def fts_patch():
    """
    Enables Full Text Searching in PostgreSQL 8.3+.
    """
    from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
    DatabaseOperations.fulltext_search_sql = fulltext_search_sql


    fts_patch() # apply patch
    ———————————-8<———————————-



    Соответственно работает так:
    qs = queryset.filter(tsv__search=terms)
    Подсмотрено здесь:
    http://www.djangosnippets.org/snippets/685/

    Иван, спасибо: успокоил по поводу отсутствия правильного решения проблемы прямо сейчас.
  4. Иван Сагалаев

    21.11.2008 16:42

    А! Вам повезло, что операция "search" в Джанге таки есть :-)

bbcode