Un contenedor Ruby simple pero poderoso para RediSearch, un motor de búsqueda basado en Redis.
En primer lugar, es necesario instalar Redis y RediSearch.
Puede descargar Redis desde https://redis.io/download y consultar las instrucciones de instalación aquí. Alternativamente, en macOS o Linux puedes instalar a través de Homebrew.
Para instalar RediSearch, consulte https://oss.redislabs.com/redisearch/Quick_Start.html. Una vez que haya creado RediSearch, si no está utilizando Docker, puede actualizar su archivo redis.conf para cargar siempre el módulo RediSearch con loadmodule /path/to/redisearch.so
. (En macOS, el archivo redis.conf se puede encontrar en /usr/local/etc/redis.conf
)
Una vez que Redis y RediSearch estén en funcionamiento, agregue la siguiente línea a su Gemfile:
gem 'redi_search'
Y luego:
❯ bundle
O instálelo usted mismo:
❯ gem install redi_search
y exigirlo:
require 'redi_search'
Una vez que la gema esté instalada y sea necesaria, deberá configurarla con su configuración de Redis. Si estás en Rails, esto debería ir en un inicializador ( config/initializers/redi_search.rb
).
RediSearch . configure do | config |
config . redis_config = {
host : "127.0.0.1" ,
port : "6379"
}
end
RediSearch gira en torno a un índice de búsqueda, así que comencemos definiendo qué es un índice de búsqueda. Según Swifttype:
Un índice de búsqueda es un conjunto de datos estructurados al que hace referencia un motor de búsqueda cuando busca resultados que sean relevantes para una consulta específica. Los índices son una pieza crítica de cualquier sistema de búsqueda, ya que deben adaptarse al método de recuperación de información específico del algoritmo del motor de búsqueda. De esta manera, el algoritmo y el índice están inextricablemente vinculados entre sí. Índice también se puede utilizar como verbo (indexación), refiriéndose al proceso de recopilación de datos no estructurados de un sitio web en un formato estructurado adaptado al algoritmo del motor de búsqueda.
Una forma de pensar en los índices es considerar la siguiente analogía entre una infraestructura de búsqueda y un sistema de archivo de oficina. Imagine que le entrega a un pasante una pila de miles de hojas de papel (documentos) y le dice que las organice en un archivador (índice) para ayudar a la empresa a encontrar información de manera más eficiente. El pasante primero tendrá que clasificar los papeles y hacerse una idea de toda la información contenida en ellos, luego tendrá que decidir sobre un sistema para ordenarlos en el archivador y finalmente tendrá que decidir cuál es el forma más eficaz de buscar y seleccionar los archivos una vez que están en el archivador. En este ejemplo, el proceso de organización y archivo de los artículos corresponde al proceso de indexación del contenido del sitio web, y el método para buscar en estos archivos organizados y encontrar los más relevantes corresponde al algoritmo de búsqueda.
Esto define los campos y las propiedades de esos campos en el índice. Un esquema es un DSL simple. Cada campo puede ser de cuatro tipos: geográfico, numérico, de etiqueta o de texto y puede tener muchas opciones. Un ejemplo simple de un esquema es:
RediSearch :: Schema . new do
text_field :first_name
text_field :last_name
end
Las opciones admitidas para cada tipo son las siguientes:
Sin opciones: text_field :name
text_field :name, weight: 2
text_field :name, phonetic: 'dm:en'
text_field :name, sortable: true
sortable
, para crear campos cuya actualización usando PARTIAL no provocará una reindexación completa del documento. Si un campo tiene no_index
y no tiene sortable
, el índice simplemente lo ignorará.text_field :name, no_index: true
text_feidl :name, no_stem: true
Sin opciones: numeric_field :price
numeric_field :id, sortable: true
sortable
, para crear campos cuya actualización usando PARTIAL no provocará una reindexación completa del documento. Si un campo tiene no_index
y no tiene sortable
, el índice simplemente lo ignorará.numeric_field :id, no_index: true
Sin opciones: tag_field :tag
tag_field :tag, sortable: true
sortable
, para crear campos cuya actualización usando PARTIAL no provocará una reindexación completa del documento. Si un campo tiene no_index
y no tiene sortable
, el índice simplemente lo ignorará.tag_field :tag, no_index: true
tag_field :tag, separator: ','
Sin opciones: geo_field :place
geo_field :place, sortable: true
sortable
, para crear campos cuya actualización usando PARTIAL no provocará una reindexación completa del documento. Si un campo tiene no_index
y no tiene sortable
, el índice simplemente lo ignorará.geo_field :place, no_index: true
Un Document
es la representación Ruby de un hash de Redis.
Puede recuperar un Document
utilizando métodos de clase .get
.
get(index, document_id)
recupera un único Document
en un Index
para un document_id
determinado. También puede crear una instancia Document
utilizando el método de clase .for_object(index, record, only: [])
. Se necesita una instancia Index
y un objeto Ruby. Ese objeto debe responder a todos los campos especificados en el Schema
del Index
. only
acepta una serie de campos del esquema y limita los campos que se pasan al Document
.
Una vez que tenga una instancia de un Document
, éste responde a todos los campos especificados en el Schema
del Index
como métodos y document_id
. document_id
se antepone automáticamente con los nombres del Index
a menos que ya lo esté para garantizar la unicidad. Anteponemos el nombre Index
porque si tiene dos Document
con la misma identificación en Index
diferentes, no queremos que los Document
se anulen entre sí. También hay un método #document_id_without_index
que elimina el nombre del índice antepuesto.
Finalmente hay un método #del
que eliminará el Document
del Index
.
Para inicializar un Index
, pase el nombre del Index
como una cadena o símbolo y el bloque Schema
.
RediSearch :: Index . new ( name_of_index ) do
text_field :foobar
end
create
false
si el índice ya existe. Acepta algunas opciones:max_text_fields: #{true || false}
add_field
.no_offsets: #{true || false}
no_highlight
.temporary: #{seconds}
seconds
de inactividad. El temporizador de inactividad interno se reinicia cada vez que se busca o se agrega al índice. Debido a que dichos índices son livianos, puede crear miles de índices sin implicaciones negativas para el rendimiento.no_highlight: #{true || false}
no_highlight
también está implícito en no_offsets
.no_fields: #{true || false}
no_frequencies: #{true || false}
drop(keep_docs: false)
Index
de la instancia de Redis y devuelve un valor booleano. Tiene un método de explosión adjunto que generará una excepción en caso de falla. Devolverá false
si el Index
ya se ha eliminado. Toma una opción de palabra clave arg, keep_docs
, que de forma predeterminada eliminará todos los hashes del documento en Redis.exist?
Index
.info
Index
.fields
Index
.add(document)
Document
. Tiene un método de explosión adjunto que generará una excepción en caso de falla.add_multiple(documents)
Document
. Esto proporciona una forma más eficaz de agregar varios documentos al Index
. Acepta las mismas opciones que add
.del(document)
Document
del Index
.document_count
Document
en el Index
add_field(name, type, **options, &block)
Index
.index.add_field(:first_name, :text, phonetic: "dm:en")
reindex(documents, recreate: false)
recreate
es true
el Index
se eliminará y se volverá a crear. La búsqueda se inicia a partir de una instancia RediSearch::Index
con cláusulas que se pueden encadenar. Al realizar la búsqueda, se devuelve una matriz de Document
que tiene métodos de lectura públicos para todos los campos del esquema.
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "10039" , "Gene" , "Volkman" ) )
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "9998" , "Jeannie" , "Ledner" ) )
main ❯ index . search ( "john" )
RediSearch ( 1.1 ms ) FT . SEARCH user_idx `john`
=> [ #<RediSearch::Document:0x00007f862e241b78 first: "Gene", last: "Volkman", document_id: "10039">,
#<RediSearch::Document:0x00007f862e2417b8 first: "Jeannie", last: "Ledner", document_id: "9998">]
Consulta de frase simple : hello AND world
index . search ( "hello" ) . and ( "world" )
Consulta de frase exacta - hello FOLLOWED BY world
index . search ( "hello world" )
Consulta de unión : hello OR world
index . search ( "hello" ) . or ( "world" )
Consulta de negación - hello AND NOT world
index . search ( "hello" ) . and . not ( "world" )
Intersecciones y uniones complejas:
# Intersection of unions
index . search ( index . search ( "hello" ) . or ( "halo" ) ) . and ( index . search ( "world" ) . or ( "werld" ) )
# Negation of union
index . search ( "hello" ) . and . not ( index . search ( "world" ) . or ( "werld" ) )
# Union inside phrase
index . search ( "hello" ) . and ( index . search ( "world" ) . or ( "werld" ) )
Todos los términos admiten algunas opciones que se pueden aplicar.
Términos de prefijo : coincide con todos los términos que comienzan con un prefijo. (Parecido al like term%
en SQL)
index . search ( "hel" , prefix : true )
index . search ( "hello worl" , prefix : true )
index . search ( "hel" , prefix : true ) . and ( "worl" , prefix : true )
index . search ( "hello" ) . and . not ( "worl" , prefix : true )
Términos opcionales : los documentos que contengan los términos opcionales tendrán una clasificación más alta que aquellos que no los contengan.
index . search ( "foo" ) . and ( "bar" , optional : true ) . and ( "baz" , optional : true )
Términos difusos : los partidos se realizan en función de la distancia de Levenshtein (LD). La distancia máxima de Levenshtein admitida es 3.
index . search ( "zuchini" , fuzziness : 1 )
Los términos de búsqueda también se pueden limitar a campos específicos mediante una cláusula where
:
# Simple field specific query
index . search . where ( name : "john" )
# Using where with options
index . search . where ( first : "jon" , fuzziness : 1 )
# Using where with more complex query
index . search . where ( first : index . search ( "bill" ) . or ( "bob" ) )
La búsqueda de campos numéricos requiere un rango:
index . search . where ( number : 0 .. 100 )
# Searching to infinity
index . search . where ( number : 0 .. Float :: INFINITY )
index . search . where ( number : - Float :: INFINITY .. 0 )
slop(level)
in_order
slop
. Nos aseguramos de que los términos de la consulta aparezcan en el mismo orden en el Document
que en la consulta, independientemente de las compensaciones entre ellos.no_content
Document
y no el contenido. Esto es útil si RediSearch se utiliza en un modelo Rails donde los atributos Document
no importan y se están convirtiendo en objetos ActiveRecord
.language(language)
Document
en chino, debe configurarlo en chino para tokenizar correctamente los términos de la consulta. Si se envía un idioma no compatible, el comando devuelve un error.sort_by(field, order: :asc)
:asc
o :desc
limit(num, offset = 0)
num
especificado en el offset
. El límite predeterminado está establecido en 10
.count
Document
encontrados en la consulta de búsqueda.highlight(fields: [], opening_tag: "<b>", closing_tag: "</b>")
fields
son una serie de campos que se resaltarán.verbatim
no_stop_words
with_scores
Document
. Esto se puede utilizar para fusionar resultados de varias instancias. Esto agregará un método score
a las instancias Document
devueltas.return(*fields)
Document
se devuelven.explain
La revisión ortográfica se inicia desde una instancia RediSearch::Index
y proporciona sugerencias para términos de búsqueda mal escritos. Se necesita un argumento distance
opcional que es la distancia máxima de Levenshtein para sugerencias de ortografía. Devuelve una matriz donde cada elemento contiene sugerencias para cada término de búsqueda y una puntuación normalizada basada en sus apariciones en el índice.
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . spellcheck ( "jimy" )
RediSearch ( 1.1 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 1
=> [ #<RediSearch::Spellcheck::Result:0x00007f805591c670
term : "jimy" ,
suggestions :
[ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]>]
main ❯ index . spellcheck ( "jimy" , distance : 2 ) . first . suggestions
RediSearch ( 0.5 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 2
=> [ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]
¡La integración con Rails es súper fácil! Llame redi_search
con el argumento de la palabra clave schema
desde dentro de su modelo. Ex:
class User < ApplicationRecord
redi_search do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
Esto agregará automáticamente los métodos User.search
y User.spellcheck
que se comportan igual que si los llamara en una instancia Index
.
User.reindex(recreate: false, only: [])
también se agrega y se comporta de manera similar a RediSearch::Index#reindex
. Algunas de las diferencias incluyen:
Document
como primer parámetro. El alcance search_import
se llama automáticamente y todos los registros se convierten a Document
s.only
parámetro opcional donde puede especificar un número limitado de campos para actualizar. Útil si modifica el esquema y solo necesita indexar un campo en particular. Mientras define el esquema, opcionalmente puede pasarle un bloque. Si no se pasa ningún bloque, el name
se llamará en el modelo para obtener el valor. Si se pasa un bloque, el valor del campo se obtiene llamando al bloque.
class User < ApplicationRecord
redi_search do
text_field :name do
" #{ first_name } #{ last_name } "
end
end
end
Puede anular el alcance search_import
en el modelo para acelerar las relaciones de carga al indexar o puede usarse para limitar los registros a indexar.
class User < ApplicationRecord
scope :search_import , -> { includes ( :posts ) }
end
Al buscar, de forma predeterminada se devuelve una colección de Document
. Llamar #results
en la consulta de búsqueda ejecutará la búsqueda y luego buscará todos los registros encontrados en la base de datos y devolverá una relación ActiveRecord.
El nombre Index
predeterminado para Index
de modelo es #{model_name.plural}_#{RediSearch.env}
. El método redi_search
toma un argumento index_prefix
opcional que se antepone al nombre del índice:
class User < ApplicationRecord
redi_search index_prefix : 'prefix' do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
User . search_index . name
# => prefix_users_development
Al integrar RediSearch en un modelo, los registros se indexarán automáticamente después de crearlos y actualizarlos y se eliminarán del Index
tras su destrucción.
Hay algunos métodos más convenientes que están disponibles públicamente:
search_document
RediSearch::Document
remove_from_index
Index
add_to_index
Index
search_index
RediSearch::Index
Después de revisar el repositorio, ejecute bin/setup
para instalar las dependencias. Luego, ejecute rake test
para ejecutar las pruebas unitarias y de integración. Para ejecutarlos individualmente, puede ejecutar rake test:unit
o rake test:integration
. También puede ejecutar bin/console
para obtener un mensaje interactivo que le permitirá experimentar.
Para instalar esta joya en su máquina local, ejecute bundle exec rake install
. Para lanzar una nueva versión, ejecute bin/publish (major|minor|patch)
que actualizará el número de versión en version.rb
, creará una etiqueta git para la versión, enviará confirmaciones y etiquetas de git y enviará el archivo .gem
a rubygems. .org y GitHub.
Los informes de errores y las solicitudes de extracción son bienvenidos en GitHub. Este proyecto pretende ser un espacio seguro y acogedor para la colaboración, y se espera que los contribuyentes cumplan con el código de conducta del Pacto de Colaboradores.
La gema está disponible como código abierto según los términos de la licencia MIT.
Se espera que todos los que interactúan en las bases de código, rastreadores de problemas, salas de chat y listas de correo del proyecto RediSearch sigan el código de conducta.