py-phias/aore/search/wordentry.py
2016-04-22 14:30:05 +03:00

156 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
import re
from aore.config import SphinxConfig
from aore.search.wordvariation import WordVariation, VariationType
def cleanup_string(word):
return word.replace('-', '').replace('@', '').replace('#', '')
class WordEntry:
# Варианты распеределния для слов с первыми двумя символами, где:
# 0 - не найдено, 1 - найдено одно, x - найдено много (>1)
# 1st - кол-во слов по LIKE 'word%'
# 2nd - кол-во слов по точному совпадению
#
# 00 - не найдено ничего вообще. Опечатка или дряное слово. Ищем с подсказками (много)
# 01 - найдено одно точное совпадение, но нет лайков. Оставляем как есть.
# -0x - найдено много точных совпадений и... быть не может, там уник.
# 10 - найден один по лайку и ни одного точного. Недопечатка. * и немного подсказок.
# 11 - одно по лайку и одно точное. Нашли. Оставляем слово как есть.
# -1x - одно по лайку и много точных. Быть не может.
# x0 - много по лайку и нет точных. Недопечатка. Немного подсказок и *.
# x1 - много по лайку и один точный. Чет нашли. Как есть и *.
# xx - много по лайку и много точных. Оставляем как есть и *
#
# Теперь по сокращениям. Они работюат отдельно (ПОКА ЧТО)
# 3rd - кол-во слов по точному совпдению по полному сокращению.
# 4th - кол-во слов по точному совпадению по малому сокращению.
#
# 00 - ни найдено нигде. Значит, не сокращение (или с опечаткой). Не обрабатываем.
# 01 - найдено одно малое сокращение. Оставляем как есть (малые и так в словаре)
# 0x - найдено много малых. Не обрабатываем.
# 10 - найдено одно полное и 0 малых. Добавляем малое.
# 11 - найдено одно полное и одно малое. Бывает (допустим, 'сад'). Добавляем как есть.
# -1x - найдено одно полное и куча малых. Ну бред.
# x0 - найдено куча полных и ни одного малого. Добавляем малое.
# x1 - Куча полных и 1 малое. Хз, бывает ли. Не обрабатываем.
# xx - Куча полных и куча малых. Не обрабатываем.
match_types = dict(
MT_MANY_SUGG=['0000'],
MT_SOME_SUGG=['10..', 'x0..'],
MT_LAST_STAR=['100.', 'x.0.'],
MT_AS_IS=['.1..', '...1', '...x'],
MT_ADD_SOCR=['..10', '..x0'],
MT_IS_SOCR=['..01', '..0x']
)
rating_limit_soft = 0.41
rating_limit_soft_count = 6
rating_limit_hard = 0.82
rating_limit_hard_count = 3
def __init__(self, db, word):
self.db = db
self.bare_word = word
self.word = cleanup_string(self.bare_word)
self.word_len = len(self.word)
self.parameters = dict(IS_FREQ=False, SOCR_WORD=None)
self.ranks = self.__init_ranks()
# Заполняем параметры слова
for mt_name, mt_values in self.match_types.items():
self.__dict__[mt_name] = False
for mt_value in mt_values:
self.__dict__[mt_name] = self.__dict__[mt_name] or re.search(mt_value, self.ranks)
# Если ищем по лайку, то точное совпадение не ищем (оно и так будет включено)
if self.MT_LAST_STAR:
self.MT_AS_IS = False
# Строка слишком котроткая, то по лайку не ищем, сфинкс такого не прожует
if self.MT_LAST_STAR and self.word_len < SphinxConfig.min_length_to_star:
self.MT_LAST_STAR = False
self.MT_AS_IS = True
def variations_generator(self, strong, suggestion_func):
default_var_type = VariationType.normal
# Если слово встречается часто, ставим у всех вариантов тип VariationType.freq
if self.parameters['IS_FREQ']:
default_var_type = VariationType.freq
# Добавляем подсказки (много штук)
if self.MT_MANY_SUGG and not strong:
suggs = suggestion_func(self.word, self.rating_limit_soft, self.rating_limit_soft_count)
for suggestion in suggs:
yield WordVariation(self, suggestion[0], default_var_type)
# Добавляем подсказки (немного)
if self.MT_SOME_SUGG and not strong:
suggs = suggestion_func(self.word, self.rating_limit_hard, self.rating_limit_hard_count)
for suggestion in suggs:
yield WordVariation(self, suggestion[0], default_var_type)
# Добавляем звездочку на конце
if self.MT_LAST_STAR:
yield WordVariation(self, self.word + '*', default_var_type)
# Добавляем слово "как есть", если это сокращение, то добавляем как частое слово
if self.MT_AS_IS:
var_t = default_var_type
if self.MT_IS_SOCR:
var_t = VariationType.freq
yield WordVariation(self, self.word, var_t)
# -- Дополнительные функции --
# Добавляем сокращение
if self.MT_ADD_SOCR:
if self.parameters['SOCR_WORD']:
yield WordVariation(self, self.parameters['SOCR_WORD'], VariationType.freq)
def __init_ranks(self):
sql_qry = "SELECT COUNT(*), NULL FROM \"AOTRIG\" WHERE word LIKE '{}%' AND LENGTH(word) > {} " \
"UNION ALL SELECT COUNT(*), NULL FROM \"AOTRIG\" WHERE word='{}' " \
"UNION ALL SELECT COUNT(*), MAX(scname) FROM \"SOCRBASE\" WHERE socrname ILIKE '{}'" \
"UNION ALL SELECT COUNT(*), NULL FROM \"SOCRBASE\" WHERE scname ILIKE '{}'" \
"UNION ALL SELECT frequency, NULL FROM \"AOTRIG\" WHERE word='{}';".format(self.word, self.word_len,
self.word, self.bare_word,
self.bare_word, self.word)
result = self.db.get_rows(sql_qry)
# Проставляем "сокращенное" сокращение, если нашли полное
if not self.parameters['SOCR_WORD']:
self.parameters['SOCR_WORD'] = result[2][1]
# Проверяем, если слово встречается слишком много раз
if len(result) == 5 and result[4][0] > 30000:
self.parameters['IS_FREQ'] = True
# Формируем список найденных величин совпадений:
# result[x]
# x = 0, поиск по неполному совпадению (лайк*), и по длине строки больше исходной
# x = 1, поиск по точному совпадению
# x = 2, поиск по базе сокращений (по полному)
# x = 3, то же, но по краткому
out_mask_list = []
for i in range(0, 4):
if result[i][0] > 1:
out_mask_list.append('x')
else:
out_mask_list.append(str(result[i][0]))
return ''.join(out_mask_list)
def get_type(self):
return ", ".join([x for x in self.match_types if self.__dict__[x]])
def __unicode__(self):
return self.word
def __str__(self):
return str(self.word)