Нечеткая строка поиска с Whoosh в Python - PullRequest
11 голосов
/ 15 июля 2011

Я создал большую базу данных банков в MongoDB.Я могу легко взять эту информацию и создать индексы в ней.Например, я хотел бы иметь возможность сопоставлять названия банков «Eagle Bank & Trust Co из Миссури» и «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 у меня есть?

Ответы [ 4 ]

7 голосов
/ 28 мая 2015

Вы можете сопоставить Co с Company, используя Fuzzy Search в 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.

3 голосов
/ 20 октября 2011

Для дальнейшего использования, и должен быть лучший способ сделать это как-то, но вот мой выстрел.

# -*- 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()
1 голос
/ 17 июля 2011

Возможно, что-то из этого может помочь (строка соответствует открытому парню из сиденья):

https://github.com/seatgeek/fuzzywuzzy

0 голосов
/ 29 февраля 2016

Вы можете использовать эту функцию ниже, чтобы нечеткий поиск набора слов по фразе:

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
...