Нечеткая строка Поиск с Whoosh в Python
Я создал большую базу банков в MongoDB. Я могу легко взять эту информацию и создать с ней индексы в whoosh. Например, я хотел бы иметь возможность сопоставлять названия банков "Eagle Bank" и "Trust Co of Missouri" и "Eagle Bank and Trust Company of Missouri". Следующий код работает с простым нечетким, но не может достичь совпадения с приведенным выше:
from whoosh.index import create_in
from whoosh.fields import *
schema = Schema(name=TEXT(stored=True))
ix = create_in("indexdir", schema)
writer = ix.writer()
test_items = [u"Eagle Bank and Trust Company of Missouri"]
writer.add_document(name=item)
writer.commit()
from whoosh.qparser import QueryParser
from whoosh.query import FuzzyTerm
with ix.searcher() as s:
qp = QueryParser("name", schema=ix.schema, termclass=FuzzyTerm)
q = qp.parse(u"Eagle Bank & Trust Co of Missouri")
results = s.search(q)
print results
дает мне:
<Top 0 Results for And([FuzzyTerm('name', u'eagle', boost=1.000000, minsimilarity=0.500000, prefixlength=1), FuzzyTerm('name', u'bank', boost=1.000000, minsimilarity=0.500000, prefixlength=1), FuzzyTerm('name', u'trust', boost=1.000000, minsimilarity=0.500000, prefixlength=1), FuzzyTerm('name', u'co', boost=1.000000, minsimilarity=0.500000, prefixlength=1), FuzzyTerm('name', u'missouri', boost=1.000000, minsimilarity=0.500000, prefixlength=1)]) runtime=0.00166392326355>
Можно ли добиться того, что я хочу с Whoosh? Если нет, то какие другие решения на основе python у меня есть?
Ответы
Ответ 1
Вы можете сопоставить Co
с Company
с помощью нечеткого поиска в Whoosh, но вы не должны, потому что разница между Co
и Company
большой. Co
похож на Company
, поскольку Be
похож на Beast
и ny
на Company
. Вы можете себе представить, насколько плохи и насколько велики будут результаты поиска.
Однако, если вы хотите совместить Compan
или compani
или Companee
с Company
, вы можете сделать это с помощью персонализированного класса FuzzyTerm
со значением по умолчанию maxdist
равным 2 или более:
maxdist - максимальное расстояние редактирования от данного текста.
class MyFuzzyTerm(FuzzyTerm):
def __init__(self, fieldname, text, boost=1.0, maxdist=2, prefixlength=1, constantscore=True):
super(MyFuzzyTerm, self).__init__(fieldname, text, boost, maxdist, prefixlength, constantscore)
Тогда:
qp = QueryParser("name", schema=ix.schema, termclass=MyFuzzyTerm)
Вы можете сопоставить Co
с Company
, установив maxdist
в 5
, но это, как я сказал, дает плохие результаты поиска. Я предлагаю сохранить maxdist
от 1
до 3
.
Если вы ищете соответствие языковым вариациям, лучше использовать whoosh.query.Variations
.
Примечание: более старые версии Whoosh имеют minsimilarity
вместо maxdist
.
Ответ 2
Для справки в будущем, и должен быть лучший способ сделать это как-то, но вот мой снимок.
# -*- coding: utf-8 -*-
import whoosh
from whoosh.index import create_in
from whoosh.fields import *
from whoosh.query import *
from whoosh.qparser import QueryParser
schema = Schema(name=TEXT(stored=True))
idx = create_in("C:\\idx_name\\", schema, "idx_name")
writer = idx.writer()
writer.add_document(name=u"This is craaazy shit")
writer.add_document(name=u"This is craaazy beer")
writer.add_document(name=u"Raphaël rocks")
writer.add_document(name=u"Rockies are mountains")
writer.commit()
s = idx.searcher()
print "Fields: ", list(s.lexicon("name"))
qp = QueryParser("name", schema=schema, termclass=FuzzyTerm)
for i in range(1,40):
res = s.search(FuzzyTerm("name", "just rocks", maxdist=i, prefixlength=0))
if len(res) > 0:
for r in res:
print "Potential match ( %s ): [ %s ]" % ( i, r["name"] )
break
else:
print "Pass: %s" % i
s.close()
Ответ 3
Возможно, некоторые из этих вещей могут помочь (строка, соответствующая открытому источнику парней):
https://github.com/seatgeek/fuzzywuzzy
Ответ 4
Вы можете использовать эту функцию ниже, чтобы fuzz найти набор слов по фразе:
def FuzzySearch(text, phrase):
"""Check if word in phrase is contained in text"""
phrases = phrase.split(" ")
for x in range(len(phrases)):
if phrases[x] in text:
print("Match! Found " + phrases[x] + " in text")
else:
continue