Начало разработки логики обработки слов.

This commit is contained in:
Jack Stdin 2016-01-16 15:53:13 +03:00
parent 327a1c994e
commit 6c09dd2cdb
9 changed files with 104 additions and 49 deletions

View File

@ -24,6 +24,7 @@ class DBImpl:
def execute(self, sql_query): def execute(self, sql_query):
try: try:
cur = self.get_cursor() cur = self.get_cursor()
print sql_query
cur.execute(sql_query) cur.execute(sql_query)
self.transaction_commit() self.transaction_commit()
except: except:

View File

@ -18,7 +18,7 @@ db_shemas['ADDROBJ'] = DbSchema("ADDROBJ",
db_shemas['SOCRBASE'] = DbSchema("SOCRBASE", ["LEVEL", "SOCRNAME", "SCNAME", "KOD_T_ST"], "kod_t_st", db_shemas['SOCRBASE'] = DbSchema("SOCRBASE", ["LEVEL", "SOCRNAME", "SCNAME", "KOD_T_ST"], "kod_t_st",
"AddressObjectType") "AddressObjectType")
db_shemas['AOTRIG'] = DbSchema("AOTRIG", ["WORD", "TRIGRAMM"], "word", db_shemas['AOTRIG'] = DbSchema("AOTRIG", ["WORD", "TRIGRAMM", "FREQUENCY"], "word",
None) None)
allowed_tables = ["ADDROBJ", "SOCRBASE"] allowed_tables = ["ADDROBJ", "SOCRBASE"]

View File

@ -8,6 +8,7 @@ import sphinxapi
from aore.config import db as dbparams from aore.config import db as dbparams
from aore.dbutils.dbimpl import DBImpl from aore.dbutils.dbimpl import DBImpl
from aore.fias.word import WordEntry
from aore.miscutils.trigram import trigram from aore.miscutils.trigram import trigram
@ -30,44 +31,6 @@ class SphinxSearch:
else: else:
self.client.SetMatchMode(sphinxapi.MA) self.client.SetMatchMode(sphinxapi.MA)
# Types =
class SRankType:
names = dict(
SRANK_EXACTLY_MISSPRINT=['00'], # Точно - опечатка, нужно много подсказок, без word*
SRANK_EXACTLY_TYPING=['01', '11'], # Точно - слово недопечатано, не надо подсказок, только word*
SRANK_PROBABLY_TYPING=['0*'], # Возможно - слово недопечатано, немного подсказок и word*
SRANK_PROBABLY_FOUND=['10'], # Возможно - слово введено точно, немного подсказок, без word*
SRANK_PROBABLY_COMPLEX=['1*'],
# Возможно, слово сложное, есть и точное совпадние, по маске Нужно немного подсказок и word*
SRANK_PROBABLY_SOCR=['1!'] # Возможно - сокращение, не трогаем вообще
)
def __init__(self, rtype):
self.rtype = rtype
for x, y in self.names.iteritems():
self.__dict__[x] = self.rtype in y
def __str__(self):
return ", ".join([x for x in self.names if self.__dict__[x]])
def __get_strong_and_uncomplete_ranks(self, word):
word_len = len(word)
sql_qry = "SELECT COUNT(*) FROM \"AOTRIG\" WHERE word LIKE '{}%' AND LENGTH(word) > {} " \
"UNION ALL SELECT COUNT(*) FROM \"AOTRIG\" WHERE word='{}'".format(
word, word_len, word)
result = self.db.get_rows(sql_qry)
strong_rank = result[1][0]
uncomplete_rank = result[0][0]
if uncomplete_rank > 1000 and word_len < 4:
uncomplete_rank = '!'
else:
if uncomplete_rank > 1:
uncomplete_rank = '*'
return self.SRankType(str(strong_rank) + str(uncomplete_rank))
def __get_suggest(self, word): def __get_suggest(self, word):
word_len = str(len(word) / 2) word_len = str(len(word) / 2)
trigrammed_word = '"{}"/1'.format(trigram(word)) trigrammed_word = '"{}"/1'.format(trigram(word))
@ -95,13 +58,15 @@ class SphinxSearch:
phrase = unicode(phrase).replace('-', '').replace('@', '').lower() phrase = unicode(phrase).replace('-', '').replace('@', '').lower()
return re.split(r"[ ,:.]+", phrase) return re.split(r"[ ,:.]+", phrase)
def __process_word(self, word): def __process_words(self, words):
print word, self.__get_strong_and_uncomplete_ranks(word) for word in words:
yield WordEntry(self.db, word)
def find(self, text): def find(self, text):
words = self.__split_phrase(text) words = self.__split_phrase(text)
for word in words: word_entries = self.__process_words(words)
self.__process_word(word) for word_entry in word_entries:
print word_entry, word_entry.get_type()
# result = self.client.Query(text) # result = self.client.Query(text)
# print json.dumps(result) # print json.dumps(result)
# logging.info("12") # logging.info("12")

73
aore/fias/word.py Normal file
View File

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
import re
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 малое. TODO Хз, бывает ли. Не обрабатываем.
# xx - Куча полных и куча малых. Не обрабатываем.
match_types = dict(
MT_MANY_SUGG=['0000'],
MT_SOME_SUGG=['10..', 'x0..'],
MT_LAST_STAR=['10..', 'x...'],
MT_AS_IS=['.1..', '...1', '...x'],
MT_ADD_SOCR=['..10', '..x0']
)
def __init__(self, db, word):
self.db = db
self.word = word
self.ranks = self.__get_word_entity()
for x, y in self.match_types.iteritems():
self.__dict__[x] = False
for z in y:
self.__dict__[x] = self.__dict__[x] or re.search(z, self.ranks) is not None
def __get_word_entity(self):
word_len = len(self.word)
sql_qry = "SELECT COUNT(*) FROM \"AOTRIG\" WHERE word LIKE '{}%' AND LENGTH(word) > {} " \
"UNION ALL SELECT COUNT(*) FROM \"AOTRIG\" WHERE word='{}' " \
"UNION ALL SELECT COUNT(*) FROM \"SOCRBASE\" WHERE socrname ILIKE '{}'" \
"UNION ALL SELECT COUNT(*) FROM \"SOCRBASE\" WHERE scname ILIKE '{}'".format(
self.word, word_len, self.word, self.word, self.word)
result = self.db.get_rows(sql_qry)
outmask = ""
for ra in result:
if ra[0] > 1:
outmask += 'x'
else:
outmask += str(ra[0])
return outmask
def get_type(self):
return ", ".join([x for x in self.match_types if self.__dict__[x]])
def __str__(self):
return str(self.word)

View File

@ -36,7 +36,7 @@ class SphinxHelper:
out_fname = self.__create_main_config(config_filename) out_fname = self.__create_main_config(config_filename)
# Indexing both configs # Indexing both configs
run_index_cmd = "{} -c {} --all".format(self.index_binary, out_fname) run_index_cmd = "{} -c {} --all --rotate".format(self.index_binary, out_fname)
logging.info("Indexing main ({})...".format(out_fname)) logging.info("Indexing main ({})...".format(out_fname))
os.system(run_index_cmd) os.system(run_index_cmd)
logging.info("All indexes were created.".format(out_fname)) logging.info("All indexes were created.".format(out_fname))
@ -81,12 +81,15 @@ class SphinxHelper:
if line == '': if line == '':
break break
keyword = line.split(' ')[0] splitting_seq = line.split(' ')
if not keyword: keyword = splitting_seq[0]
freq = splitting_seq[1].rstrip('\n')
if not keyword or not freq:
raise BaseException("Cannot process {}".format(self.files['dict.txt'])) raise BaseException("Cannot process {}".format(self.files['dict.txt']))
nodes.append(keyword) nodes.append(keyword)
nodes.append(trigram(keyword)) nodes.append(trigram(keyword))
nodes.append(freq)
exit_file.write("\t".join(nodes) + "\n") exit_file.write("\t".join(nodes) + "\n")

View File

@ -2,3 +2,4 @@ CREATE INDEX "sphinx_ind_aolevel" ON "ADDROBJ" USING btree ("aolevel");
CREATE INDEX "sphinx_ind_parentguid" ON "ADDROBJ" USING btree ("parentguid"); CREATE INDEX "sphinx_ind_parentguid" ON "ADDROBJ" USING btree ("parentguid");
CREATE INDEX "sphinx_ind_livestatus" ON "ADDROBJ" USING btree ("livestatus"); CREATE INDEX "sphinx_ind_livestatus" ON "ADDROBJ" USING btree ("livestatus");
CREATE INDEX "sphinx_ind_aoguid" ON "ADDROBJ" USING btree ("aoguid"); CREATE INDEX "sphinx_ind_aoguid" ON "ADDROBJ" USING btree ("aoguid");
CREATE INDEX "AOTRIG_word_idx" ON "AOTRIG" USING btree ("word");

View File

@ -27,3 +27,14 @@ CREATE TABLE "SOCRBASE" (
) )
WITH (OIDS =FALSE WITH (OIDS =FALSE
); );
DROP TABLE IF EXISTS "AOTRIG";
CREATE TABLE "AOTRIG" (
"id" SERIAL4 NOT NULL,
"word" VARCHAR(50),
"trigramm" VARCHAR(180),
"frequency" INT4,
CONSTRAINT "word" UNIQUE ("word"),
CONSTRAINT "id_aotrig" PRIMARY KEY ("id")
)
WITH (OIDS =FALSE
);

View File

@ -7,11 +7,12 @@ source {{index_name}}
sql_db = {{db_name}} sql_db = {{db_name}}
sql_port = {{db_port}} sql_port = {{db_port}}
sql_query = SELECT id, trigramm, word, LENGTH(word) AS len FROM "AOTRIG" sql_query = SELECT id, trigramm, word, LENGTH(word) AS len, frequency FROM "AOTRIG"
sql_field_string = trigramm sql_field_string = trigramm
sql_attr_uint = len sql_attr_uint = len
sql_attr_string = word sql_attr_string = word
sql_attr_string = frequency
} }
index {{index_name}} index {{index_name}}

View File

@ -54,7 +54,7 @@ def main():
# 4 Debug purposes.. # 4 Debug purposes..
if options.test: if options.test:
sph = SphinxSearch() sph = SphinxSearch()
sph.find('город Гавно д. пидарская, ул Кощеева') sph.find('гор Горно-алтайск проспект Ленина')
if __name__ == '__main__': if __name__ == '__main__':
main() main()