Как создать запрос к базе данных Postgresql в Django с побитовым сравнением BitXor и фильтрацией по значению расстояния Хэмминга?
Моя задача - разработать функцию для поиска изображений в базе данных с использованием персептивного хэша и сравнения расстояния Хэмминга. Код ниже работает, но из-за того, что фильтрация происходит в среде Django и на Python, код выполняется крайне медленно, в среднем время выполнения составляет более 7 секунд при 1 500 000 записей в базе данных, но на рабочем сервере количество записей будет гораздо больше, и скорость обработки запросов упадет еще больше.
models.py
# models.py
from django.db import models
class Fids(models.Model):
"""
The model stores records with computed image hashes (>6000000 records)
picture = 'https://ae01.alicdn.com/kf/Sc835208f704548b7b230fd12f72850bfU.jpg'
hs8 = '9aca3edfc0c23538'
bin8 = '1001101011001010001111101101111111000000110000100011010100111000'
"""
picture = models.URLField()
hs8 = models.CharField(max_length=50)
bin8 = models.CharField(max_length=64)
service.py
# service.py
import requests
import json
import imagehash
from PIL import Image
from main.models import Fids
def hamming_distance(binary_str1, binary_str2):
"""
The function calculates the Hamming distance
example input:
binary_str1 = '1001101011001010001111101101111111000000110000100011010100111000'
binary_str2 = '1001101011001010001111101101111111000000110000100011010100111000'
"""
if len(binary_str1) != len(binary_str2):
return -1
xor_result = int(binary_str1, 2) ^ int(binary_str2, 2)
hamming_distance = bin(xor_result).count('1')
return hamming_distance
def search(url=None, hamming=None):
"""
Search query processing function
example input:
url = 'https://ae01.alicdn.com/kf/Sc835208f704548b7b230fd12f72850bfU.jpg'
hamming = 8
output:
delay: 8-15 sec
"""
if url is None:
return -1
hamming = 0 if hamming is None else int(hamming)
try:
res = requests.get(url, stream=True).raw
hash = imagehash.phash(Image.open(res), hash_size=8)
str_hash = str(hash)
binary = bin(int(str_hash, 16))[2:].zfill(len(str_hash)*4)
except Exception as er:
return json.dumps({'search': url, 'error': str(er)})
result = []
queryset = Fids.objects.all().values('picture', 'bin8')
for item in queryset:
bin8 = item['bin8']
bin8 = '' if bin8 is None else bin8
distance = hamming_distance(binary, bin8)
if 0 <= distance <= hamming:
result.append({'url': item['picture']})
return json.dumps({'search': url, 'results': result})
Думаю, вам нужно правильно составить SQL-запрос, чтобы фильтрация осуществлялась с использованием базы данных Postgreql-14.
Я нашел агрегатную функцию BitXor в Django: from django.contrib.postgres.aggregates import BitXor
Я считаю, что он выполняет побитовое сравнение, но не понимаю, как его применять, в интернете очень мало примеров, Более того, вам также необходимо отфильтровать запрос по расстройству Хэмминга.
ВSQL также есть оператор #, который также выполняет побитовое сравнение.
Например: SELECT X'eb35b232b08ac33e' # X'eb35b232b08ac33e';
Но я не понимаю, как применить это к моей проблеме.
Помогите мне создать правильный запрос с использованием ORM Django или SQL-запрос для выполнения поиска и фильтрации с использованием побитового сравнения и сравнения результата с заданным значением расстояния Хэмминга.
В результате поиска решения был получен следующий SQL-запрос:
SELECT * FROM main_fids WHERE int7 # 521635964142742 = 1;
Придется добавить поле int7 и хранить хэш в десятичном виде и меньшего размера, но я получаю ошибку из-за большого размера числа. Я не смог понять, как сравнить двоичные и шестнадцатеричные значения, которые я сохранил как Char. Осталось только понять, как сформировать такой запрос в Django и передать поисковое хэш-значение вместо 521635964142742 и значение расстояния Хэмминга, заданное запросом, вместо 1.
Для решения проблемы я нашел следующее решение:
SELECT id, hsi7
FROM main_fids
WHERE length(regexp_replace(((hsi7 # 524383500174776)::bit(64))::text, '0', '', 'g')) < 8 ;
Преобразую результат битового XOR к двоичному типу, затем преобразую его в строку, удаляю нули, подсчитываю количество символов в получившейся строке, получая таким образом расстояние Хэмминга, сравниваю его с заданным значением (в примере 8). Запрос по-прежнему работает очень медленно; возможно, стоит как-то изменить запрос или структуру таблицы, или преобразовать данные к другому типу. Если возможно, посоветуйте, пожалуйста, более элегантное и эффективное решение. Пожалуйста.