Как выполнять побитовые запросы БД в Django? - PullRequest
6 голосов
/ 29 марта 2012

Как я могу выполнять побитовые запросы к БД с помощью Django?
Я ничего не нашел в документах.
Должен ли я получить набор запросов, а затем выполнить программную фильтрацию?

Если вам интересно, я использую побитовые операции в качестве альтернативы IN () в очень больших и сложных запросах, чтобы повысить производительность. У меня есть БД, содержащая миллионы предметов (записей). В некоторых полях используется двоичное представление свойства элемента.
Например: поле Color может иметь несколько значений, поэтому оно имеет следующую структуру:

0001 - Red
0010 - Green
0100 - Blue
1000 - White

(это двоичные значения)
Поэтому, если элемент имеет красный и синий цвета, поле Цвет будет содержать 0101.
Когда пользователь запрашивает БД, я использую побитовое ИЛИ для поиска совпадений (вместо IN () , что очень медленно).

Ответы [ 3 ]

6 голосов
/ 08 апреля 2015

Вы можете выполнять побитовые операции на уровне базы данных с F объектами .

Если поле неотрицательно, это означает, что условие field & mask > 0 может быть переписано как (field > 0) AND (field >= (field & mask)). Если вы хотите проверить, применимы ли все биты mask ((field & mask) == mask), вы можете создать предыдущее выражение для каждого бита, а затем объединить условия с помощью sql AND. Пожалуйста, посмотрите пример, как это можно сделать. (Пользовательский QuerySet просто для удобства. Если вы используете старые версии django, вы можете реализовать has_one_of и has_all в качестве отдельных функций или методов класса, или лучше PathThroughManager ). Примечание 1 * F - это обходной путь для принудительной установки скобок над побитовой операцией, в противном случае django (как для версии 1.5) выдаст неверный sql (colors >= colors & mask, сравнение имеет более высокий приоритет, поэтому оно будет означать TRUE & mask)

import operator
from django.db import models
from django.db.models import Q, F

_bit = lambda x: 2**(x-1)
RED = _bit(1)
GREEN = _bit(2)
BLUE = _bit(3)
WHITE = _bit(4)


class ItemColorsQuerySet(models.QuerySet):

    def has_one_of(self, colors):
        """
            Only those that has at least one of provided colors
        """
        return self.filter(
            colors__gt=0,
            # field value contains one of supplied color bits
            colors__gte=1 * F('colors').bitand(reduce(operator.or_, colors, 0))
        )

    def has_all(self, colors):
        """
            Has all provided colors (and probably others)
        """
        # filter conditions for all supplied colors: 
        # each one is "field value has bit that represents color"
        colors_q = map(lambda c: Q(colors__gte=1 * F('colors').bitand(c)), colors)
        # one complex Q object merged via sql AND:
        # colors>0 and all color-bit conditions
        filter_q = reduce(operator.and_, colors_q, Q(colors__gt=0))
        return self.filter(filter_q)


class Item(models.Model):

    name = models.CharField(max_length=100, unique=True)
    # can handle many colors using bitwise logic. Zero means no color is set.
    colors = models.PositiveIntegerField(default=0)

    objects = ItemColorsQuerySet.as_manager()
6 голосов
/ 24 апреля 2015

Для postgres db вы можете использовать .extra () params с django orm.

Например:

SomeModel.objects.extra(where=['brand_label & 3 = 3'])
4 голосов
/ 29 марта 2012

Проверьте django-bitfield , он хорошо работает с PostgreSQL (вероятно, также хорошо MySQL)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...