Un wrapper Ruby simple mais puissant autour de RediSearch, un moteur de recherche au-dessus de Redis.
Tout d'abord, Redis et RediSearch doivent être installés.
Vous pouvez télécharger Redis depuis https://redis.io/download et consulter les instructions d'installation ici. Alternativement, sur macOS ou Linux, vous pouvez installer via Homebrew.
Pour installer RediSearch, consultez https://oss.redislabs.com/redisearch/Quick_Start.html. Une fois RediSearch construit, si vous n'utilisez pas Docker, vous pouvez mettre à jour votre fichier redis.conf pour toujours charger le module RediSearch avec loadmodule /path/to/redisearch.so
. (Sur macOS, le fichier redis.conf se trouve dans /usr/local/etc/redis.conf
)
Une fois Redis et RediSearch opérationnels, ajoutez la ligne suivante à votre Gemfile :
gem 'redi_search'
Et puis:
❯ bundle
Ou installez-le vous-même :
❯ gem install redi_search
et l'exiger :
require 'redi_search'
Une fois la gemme installée et requise, vous devrez la configurer avec votre configuration Redis. Si vous êtes sur Rails, cela devrait aller dans un initialiseur ( config/initializers/redi_search.rb
).
RediSearch . configure do | config |
config . redis_config = {
host : "127.0.0.1" ,
port : "6379"
}
end
RediSearch s'articule autour d'un index de recherche, commençons donc par définir ce qu'est un index de recherche. Selon Swifttype :
Un index de recherche est un ensemble de données structurées auquel un moteur de recherche se réfère lorsqu'il recherche des résultats pertinents pour une requête spécifique. Les index constituent un élément essentiel de tout système de recherche, car ils doivent être adaptés à la méthode de récupération d'informations spécifique de l'algorithme du moteur de recherche. De cette manière, l’algorithme et l’index sont inextricablement liés l’un à l’autre. Index peut également être utilisé comme verbe (indexation), faisant référence au processus de collecte de données de sites Web non structurées dans un format structuré adapté à l'algorithme du moteur de recherche.
Une façon d’envisager les indices consiste à considérer l’analogie suivante entre une infrastructure de recherche et un système de classement bureautique. Imaginez que vous remettez à un stagiaire une pile de milliers de morceaux de papier (documents) et que vous lui dites d'organiser ces morceaux de papier dans un classeur (index) pour aider l'entreprise à trouver des informations plus efficacement. Le stagiaire devra d'abord trier les papiers et avoir une idée de toutes les informations qu'ils contiennent, puis il devra décider d'un système pour les classer dans le classeur, puis enfin il devra décider quel est le Le moyen le plus efficace pour rechercher et sélectionner les fichiers une fois qu'ils sont dans l'armoire. Dans cet exemple, le processus d'organisation et de classement des articles correspond au processus d'indexation du contenu du site Web, et la méthode de recherche dans ces fichiers organisés et de recherche de ceux qui sont les plus pertinents correspond à l'algorithme de recherche.
Ceci définit les champs et les propriétés de ces champs dans l'index. Un schéma est un simple DSL. Chaque champ peut être de quatre types : géographique, numérique, balise ou texte et peut comporter de nombreuses options. Un exemple simple de schéma est :
RediSearch :: Schema . new do
text_field :first_name
text_field :last_name
end
Les options prises en charge pour chaque type sont les suivantes :
Sans options : text_field :name
text_field :name, weight: 2
text_field :name, phonetic: 'dm:en'
text_field :name, sortable: true
sortable
, pour créer des champs dont la mise à jour à l'aide de PARTIAL n'entraînera pas une réindexation complète du document. Si un champ a no_index
et n'a pas sortable
, il sera simplement ignoré par l'index.text_field :name, no_index: true
text_feidl :name, no_stem: true
Sans options : numeric_field :price
numeric_field :id, sortable: true
sortable
, pour créer des champs dont la mise à jour à l'aide de PARTIAL n'entraînera pas une réindexation complète du document. Si un champ a no_index
et n'a pas sortable
, il sera simplement ignoré par l'index.numeric_field :id, no_index: true
Sans option : tag_field :tag
tag_field :tag, sortable: true
sortable
, pour créer des champs dont la mise à jour à l'aide de PARTIAL n'entraînera pas une réindexation complète du document. Si un champ a no_index
et n'a pas sortable
, il sera simplement ignoré par l'index.tag_field :tag, no_index: true
tag_field :tag, separator: ','
Sans option : geo_field :place
geo_field :place, sortable: true
sortable
, pour créer des champs dont la mise à jour à l'aide de PARTIAL n'entraînera pas une réindexation complète du document. Si un champ a no_index
et n'a pas sortable
, il sera simplement ignoré par l'index.geo_field :place, no_index: true
Un Document
est la représentation Ruby d'un hachage Redis.
Vous pouvez récupérer un Document
à l'aide des méthodes de classe .get
.
get(index, document_id)
récupère un seul Document
dans un Index
pour un document_id
donné. Vous pouvez également créer une instance Document
à l'aide de la méthode de classe .for_object(index, record, only: [])
. Il faut une instance Index
et un objet Ruby. Cet objet doit répondre à tous les champs spécifiés dans le Schema
de Index
. only
tableau de champs du schéma et limite les champs transmis au Document
.
Une fois que vous disposez d'une instance d'un Document
, elle répond à tous les champs spécifiés dans le Schema
de Index
en tant que méthodes et document_id
. document_id
est automatiquement ajouté au début des noms de Index
sauf si c'est déjà le cas pour garantir l'unicité. Nous ajoutons le nom Index
car si vous avez deux Document
avec le même identifiant dans différents Index
, nous ne voulons pas que les Document
se remplacent. Il existe également une méthode #document_id_without_index
qui supprime le nom d'index ajouté.
Enfin, il existe une méthode #del
qui supprimera le Document
de l' Index
.
Pour initialiser un Index
, transmettez le nom de l' Index
sous forme de chaîne ou de symbole et le bloc Schema
.
RediSearch :: Index . new ( name_of_index ) do
text_field :foobar
end
create
false
si l'index existe déjà. Accepte quelques options :max_text_fields: #{true || false}
add_field
.no_offsets: #{true || false}
no_highlight
.temporary: #{seconds}
seconds
secondes d'inactivité. Le minuteur d'inactivité interne est réinitialisé chaque fois que l'index est recherché ou ajouté. Ces index étant légers, vous pouvez en créer des milliers sans conséquences négatives sur les performances.no_highlight: #{true || false}
no_highlight
est également impliqué par no_offsets
.no_fields: #{true || false}
no_frequencies: #{true || false}
drop(keep_docs: false)
Index
de l'instance Redis et renvoie un booléen. Est accompagné d'une méthode bang qui déclenchera une exception en cas d'échec. Renvoie false
si l' Index
a déjà été supprimé. Prend un mot-clé d'option arg, keep_docs
, qui supprimera par défaut tous les hachages de documents dans Redis.exist?
Index
.info
Index
.fields
Index
.add(document)
Document
. Est accompagné d'une méthode bang qui déclenchera une exception en cas d'échec.add_multiple(documents)
Document
. Cela fournit un moyen plus performant d’ajouter plusieurs documents à l’ Index
. Accepte les mêmes options que add
.del(document)
Document
de l' Index
.document_count
Document
s dans l' Index
add_field(name, type, **options, &block)
Index
.index.add_field(:first_name, :text, phonetic: "dm:en")
reindex(documents, recreate: false)
recreate
est true
l' Index
sera supprimé et recréé La recherche est lancée à partir d'une instance RediSearch::Index
avec des clauses qui peuvent être enchaînées. Lors de la recherche, un tableau de Document
s est renvoyé avec des méthodes de lecture publiques pour tous les champs du schéma.
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">]
Requête d'expression simple - hello AND world
index . search ( "hello" ) . and ( "world" )
Requête d'expression exacte - hello FOLLOWED BY world
index . search ( "hello world" )
Requête syndicale - hello OR world
index . search ( "hello" ) . or ( "world" )
Requête de négation - hello AND NOT world
index . search ( "hello" ) . and . not ( "world" )
Intersections et unions complexes :
# 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" ) )
Tous les termes prennent en charge quelques options qui peuvent être appliquées.
Termes préfixes : faites correspondre tous les termes commençant par un préfixe. (Semblable à like term%
dans 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 )
Termes facultatifs : les documents contenant les termes facultatifs seront mieux classés que ceux qui n'en contiennent pas.
index . search ( "foo" ) . and ( "bar" , optional : true ) . and ( "baz" , optional : true )
Termes flous : les matchs sont effectués sur la base de la distance de Levenshtein (LD). La distance Levenshtein maximale prise en charge est de 3.
index . search ( "zuchini" , fuzziness : 1 )
Les termes de recherche peuvent également être étendus à des champs spécifiques à l'aide d'une clause 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 recherche de champs numériques prend une plage :
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
. Nous veillons à ce que les termes de la requête apparaissent dans le même ordre dans le Document
que dans la requête, quels que soient les décalages entre eux.no_content
Document
et non le contenu. Ceci est utile si RediSearch est utilisé sur un modèle Rails où les attributs Document
n'ont pas d'importance et où il est converti en objets ActiveRecord
.language(language)
Document
en chinois, cela doit être défini sur chinois afin de symboliser correctement les termes de la requête. Si une langue non prise en charge est envoyée, la commande renvoie une erreur.sort_by(field, order: :asc)
:asc
ou :desc
limit(num, offset = 0)
num
spécifié au offset
. La limite par défaut est fixée à 10
.count
Document
trouvés dans la requête de recherchehighlight(fields: [], opening_tag: "<b>", closing_tag: "</b>")
fields
sont un tableau de champs à mettre en évidence.verbatim
no_stop_words
with_scores
Document
. Cela peut être utilisé pour fusionner les résultats de plusieurs instances. Cela ajoutera une méthode score
aux instances Document
renvoyées.return(*fields)
Document
qui sont renvoyés.explain
La vérification orthographique est lancée à partir d'une instance RediSearch::Index
et fournit des suggestions pour les termes de recherche mal orthographiés. Il prend un argument distance
facultatif qui est la distance maximale de Levenshtein pour les suggestions orthographiques. Il renvoie un tableau dans lequel chaque élément contient des suggestions pour chaque terme de recherche et un score normalisé basé sur ses occurrences dans l'index.
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">]
L'intégration avec Rails est super simple ! Appelez redi_search
avec l'argument de mot-clé schema
depuis l'intérieur de votre modèle. Ex:
class User < ApplicationRecord
redi_search do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
Cela ajoutera automatiquement les méthodes User.search
et User.spellcheck
qui se comportent de la même manière que si vous les appeliez sur une instance Index
.
User.reindex(recreate: false, only: [])
est également ajouté et se comporte de la même manière que RediSearch::Index#reindex
. Certaines des différences incluent :
Document
s comme premier paramètre. La portée search_import
est automatiquement appelée et tous les enregistrements sont convertis en Document
s.only
dans lequel vous pouvez spécifier un nombre limité de champs à mettre à jour. Utile si vous modifiez le schéma et que vous devez uniquement indexer un champ particulier. Lors de la définition du schéma, vous pouvez éventuellement lui transmettre un bloc. Si aucun bloc n'est passé, le name
sera appelé sur le modèle pour obtenir la valeur. Si un bloc est transmis, la valeur du champ est obtenue en appelant le bloc.
class User < ApplicationRecord
redi_search do
text_field :name do
" #{ first_name } #{ last_name } "
end
end
end
Vous pouvez remplacer la portée search_import
sur le modèle pour charger rapidement les relations lors de l'indexation ou l'utiliser pour limiter les enregistrements à indexer.
class User < ApplicationRecord
scope :search_import , -> { includes ( :posts ) }
end
Lors de la recherche, par défaut, une collection de Document
s est renvoyée. L'appel de #results
sur la requête de recherche exécutera la recherche, puis recherchera tous les enregistrements trouvés dans la base de données et renverra une relation ActiveRecord.
Le nom Index
par défaut pour les modèles Index
s est #{model_name.plural}_#{RediSearch.env}
. La méthode redi_search
prend un argument index_prefix
facultatif qui est ajouté au nom de l'index :
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
Lors de l'intégration de RediSearch dans un modèle, les enregistrements seront automatiquement indexés après création et mise à jour et seront supprimés de l' Index
lors de leur destruction.
Il existe quelques méthodes plus pratiques qui sont accessibles au public :
search_document
RediSearch::Document
remove_from_index
Index
add_to_index
Index
search_index
RediSearch::Index
Après avoir extrait le dépôt, exécutez bin/setup
pour installer les dépendances. Ensuite, exécutez rake test
pour exécuter les tests unitaires et d’intégration. Pour les exécuter individuellement, vous pouvez exécuter rake test:unit
ou rake test:integration
. Vous pouvez également exécuter bin/console
pour une invite interactive qui vous permettra d'expérimenter.
Pour installer cette gemme sur votre machine locale, exécutez bundle exec rake install
. Pour publier une nouvelle version, exécutez bin/publish (major|minor|patch)
qui mettra à jour le numéro de version dans version.rb
, créera une balise git pour la version, poussera les commits et balises git et poussera le fichier .gem
vers rubygems. .org et GitHub.
Les rapports de bogues et les demandes d'extraction sont les bienvenus sur GitHub. Ce projet est destiné à être un espace de collaboration sûr et accueillant, et les contributeurs doivent adhérer au code de conduite Contributor Covenant.
La gemme est disponible en open source selon les termes de la licence MIT.
Toute personne interagissant dans les bases de code, les outils de suivi des problèmes, les salons de discussion et les listes de diffusion du projet RediSearch doit suivre le code de conduite.