SELECT record.id, count(*)
FROM record
JOIN record_tags ON (record_tags.record_id = record.id)
WHERE record_tags.tag_id IN (1,2,3) GROUP BY 1 ORDER BY 2 DESC
В структуре данных "многие-ко-многим" получите все записи, связанные с искомыми тегами, а сортировка выдаст количество совпадений (одной записи с разными тегами)
Обычно просто навешивается уникальный индекс. Если прям длинный текст надо сравнивать - то можно контрольную сумму посчитать. Если "нечеткий поиск" нужен - это наверно уже движок полнотекстового поиска надо прикручивать.
for line in file:
Вот этот цикл прочитывает файл построчно до конца и при повторном заходе во второй итерации внешнего цикла не возвращает ни одной строки.
Ну вообще похоже на то, как Django миграции импортируют: они из питона вообще неидентифицируемы, потому что идентификатор с цифирек не может начинаться. Но ведь как-то работает! Вывод - нужно почитать документацию про импорт в питоне.
Я бы сделал так:
Создал бы пул очередей (10 штук), на каждую запустил бы по потоку, который бы ждал сообщения из очереди и запускал бы нужный запрос. А в основном потоке раз в 0.2 секунды пулял бы по 1 запросу в одну очередь в цикле, на каждой итерации переходя к следующей очереди. Итого, все запросы координируются через общий "писатель", а "читатели" обрабатывают ответы каждый в своем потоке.
Сделайте свойство у объектов моделей, которое будет возвращать значения разных полей. Запилите аннотацию (qs.annotate()), которая вернет разные поля под одним названием.
Именно в python многопроцессность позволяет задействовать несколько ядер в вычислениях на python, в случае многопоточности зачастую (но не всегда) всё упирается в одно ядро из-за Global Interpreter Lock (который является спасительным кругом в море рейс-кондишнов и прочих прелестей параллельной обработки данных)
Делаете последовательно три запроса к разным индексам, по-разному составленным и по-разному обрабатываемым. Добавляете исправление опечаток и подсказки - и всё. Не нужно никаких особых "функциональностей" из коробки.