python - fields - phrase search elasticsearch
Búsqueda de texto borroso en python (1)
Me pregunto si hay alguna biblioteca de Python que pueda realizar búsquedas de texto difuso. Por ejemplo:
- Tengo tres palabras clave "carta" , "sello" y "correo" .
- Me gustaría tener una función para verificar si esas tres palabras están dentro del mismo párrafo (o ciertas distancias, una página).
- Además, esas palabras tienen que mantener el mismo orden. Está bien que aparezcan otras palabras entre esas tres palabras.
He intentado fuzzywuzzy
que no resolvió mi problema. Otra biblioteca Whoosh
parece poderosa, pero no encontré la función adecuada ...
{1} Puedes hacer esto en Whoosh 2.7
. Tiene búsqueda difusa agregando el complemento whoosh.qparser.FuzzyTermPlugin
:
whoosh.qparser.FuzzyTermPlugin
te permite buscar términos "confusos", es decir, términos que no tienen que coincidir exactamente. El término difuso coincidirá con cualquier término similar dentro de un cierto número de "ediciones" (inserciones, eliminaciones y / o transposiciones de caracteres; esto se denomina "distancia de edición Damerau-Levenshtein").
Para agregar el plugin fuzzy:
parser = qparser.QueryParser("fieldname", my_index.schema)
parser.add_plugin(qparser.FuzzyTermPlugin())
Una vez que agrega el plugin fuzzy al analizador, puede especificar un término difuso agregando un ~
seguido de una distancia de edición máxima opcional. Si no especifica una distancia de edición, el valor predeterminado es 1.
Por ejemplo, la siguiente consulta de término "difusa":
letter~
letter~2
letter~2/3
{2} Para mantener las palabras en orden, use Query whoosh.query.Phrase
pero debe reemplazar el plugin Phrase
por whoosh.qparser.SequencePlugin
que le permite usar términos difusos dentro de una frase:
"letter~ stamp~ mail~"
Para reemplazar el complemento de frase predeterminado con el complemento de secuencia:
parser = qparser.QueryParser("fieldname", my_index.schema)
parser.remove_plugin_class(qparser.PhrasePlugin)
parser.add_plugin(qparser.SequencePlugin())
{3} Para permitir palabras entre, inicialice el slop
en su consulta de Frase a un número mayor:
whoosh.query.Phrase(fieldname, words, slop=1, boost=1.0, char_ranges=None)
slop - la cantidad de palabras permitidas entre cada "palabra" en la frase; el valor predeterminado de 1 significa que la frase debe coincidir exactamente.
También puede definir slop en Query como este:
"letter~ stamp~ mail~"~10
{4} Solución general:
{4.a} Indexador sería como:
from whoosh.index import create_in
from whoosh.fields import *
schema = Schema(title=TEXT(stored=True), content=TEXT)
ix = create_in("indexdir", schema)
writer = ix.writer()
writer.add_document(title=u"First document", content=u"This is the first document we''ve added!")
writer.add_document(title=u"Second document", content=u"The second one is even more interesting!")
writer.add_document(title=u"Third document", content=u"letter first, stamp second, mail third")
writer.add_document(title=u"Fourth document", content=u"stamp first, mail third")
writer.add_document(title=u"Fivth document", content=u"letter first, mail third")
writer.add_document(title=u"Sixth document", content=u"letters first, stamps second, mial third wrong")
writer.add_document(title=u"Seventh document", content=u"stamp first, letters second, mail third")
writer.commit()
{4.b} Searcher sería como:
from whoosh.qparser import QueryParser, FuzzyTermPlugin, PhrasePlugin, SequencePlugin
with ix.searcher() as searcher:
parser = QueryParser(u"content", ix.schema)
parser.add_plugin(FuzzyTermPlugin())
parser.remove_plugin_class(PhrasePlugin)
parser.add_plugin(SequencePlugin())
query = parser.parse(u"/"letter~2 stamp~2 mail~2/"~10")
results = searcher.search(query)
print "nb of results =", len(results)
for r in results:
print r
Eso da el resultado:
nb of results = 2
<Hit {''title'': u''Sixth document''}>
<Hit {''title'': u''Third document''}>
{5} Si desea establecer la búsqueda difusa como predeterminada sin utilizar la word~n
sintaxis word~n
en cada palabra de la consulta, puede inicializar QueryParser
siguiente manera:
from whoosh.query import FuzzyTerm
parser = QueryParser(u"content", ix.schema, termclass = FuzzyTerm)
Ahora puede usar la consulta "letter stamp mail"~10
pero tenga en cuenta que FuzzyTerm
tiene la distancia de edición predeterminada maxdist = 1
. Personaliza la clase si quieres mayor distancia de edición:
class MyFuzzyTerm(FuzzyTerm):
def __init__(self, fieldname, text, boost=1.0, maxdist=2, prefixlength=1, constantscore=True):
super(D, self).__init__(fieldname, text, boost, maxdist, prefixlength, constantscore)
# super().__init__() for Python 3 I think
Referencias