Blog tech

Faciliter les recherches avec Ransack

Rédigé par Nicolas Cavigneaux | 31 mai 2012

La recherche est un besoin récurrent dans les applications en ligne. Nous avons déjà vu dans un autre article comment utiliser le moteur d’indexation de texte Sphinx. Sphinx a pour avantage d’être très rapide et peu coûteux en ressource car il indexe son contenu. Il ne peut, par contre, faire de la recherche que sur le contenu qui a été indexé et pas sur le plus récent. Il est également à noter que l’implémentation d’une recherche avancée dans l’application n’est pas triviale.

Nous allons donc nous pencher cette fois ci sur le gem Ransack qui a pour avantage de faciliter grandement la mise en place d’un moteur de recherche évolué. Évidemment, Ransack s’avérera moins véloce qu’une solution équivalente basée sur du full-text search puisque ces recherches sont faîtes en temps réel via des requêtes SQL.

Ransack se base sur le gem Squeel, du même auteur, qui étend Arel avec un certain nombre de prédicats permettant l’écriture de conditions à la manière Ruby. Je vous conseille d’y jeter un œil pour votre propre usage au niveau model et controller.

Utilisation

Installation

Comme à l’habitude, il vous suffit d’ajouter une ligne à votre Gemfile :

gem "ransack"

Controller

La méthode search est ajoutée aux objets ActiveRecord par Ransack. Cette méthode va permettre de parser les paramètres envoyés par le formulaire pour créer la requête SQL.

Voici un exemple de code controller :

def index
  @query = Product.search(params[:q])
  @products = @query.result(:distinct => true)
end

On passe donc les paramètres de recherche à la méthode search qui retourne un objet intermédiaire. Sur cet objet intermédiaire @query, nous pouvons appeler la méthode result qui va nous retourner un tableau d’objet AR correspondant à notre recherche.

Notez dans l’exemple que l’on passe true à l’option distinct pour s’assurer que nos résultats ne contiennent pas de doublons.

Vue

Dans les vues, Ransack met deux choses à notre disposition, un helper pour générer le tag form ainsi qu’un certain nombre d’attributs virtuels sur notre objet query utilisé pour générer le form. C’est donc notre objet AR étendu par quelques méthodes spécialisées pour la recherche.

Voyons un exemple simple :

<%= search_form_for @query do |f| %>
  <%= f.label :name_cont %>
  <%= f.text_field :name_cont %>

  <%= f.label :brand_start %>
  <%= f.text_field :brand_start %>

  <%= f.label :price_lt %>
  <%= f.text_field :price_lt %>

  <%= f.submit %>
<% end %>

La méthode search_form_for va créer un formulaire utilisant l’action courante et lui passera les paramètres de recherche.

On utilise ici un certain nombre de prédicats qui nous permettent de rechercher sur des attributs de notre modèle mais en précisant directement, le type de filtrage à opérer.

On souhaite donc que le nom contienne ce qui est entré dans le premier champ, que la marque commence par ce qui est entré dans le deuxième champ et que le prix soit inférieur à ce qui est entré dans le troisième champ.

Vous avez fini, Ransack se charge de tout le reste, vous n’avez plus qu’à afficher vos résultats comme vous le feriez après n’importe quel requête AR.

Les prédicats disponibles

Comme dit précédemment, Ransack se base sur Squeel et réutilise ses prédicats pour faciliter la mise en place des formulaires de recherche.

Voici la liste des extensions disponibles sur chacun de vos attributs de modèles :

  • eq : la valeur de l’attribut est l’expression
  • not_eq : la valeur de l’attribut n’est pas l’expression
  • matches : la valeur de l’attribut match l’expression
  • does_not_match : la valeur de l’attribut ne match pas l’expression
  • lt : la valeur de l’attribut est inférieure à l’expression
  • lteq : la valeur de l’attribut est inférieure ou égale à l’expression
  • gt : la valeur de l’attribut est supérieur à l’expression
  • gteq : la valeur de l’attribut est supérieur ou égale à l’expression
  • in : la valeur de l’attribut est incluse dans l’expression − not_in : la valeur de l’attribut n’est pas incluse dans l’expression

et de leur dérivés :

  • ’_cont’ : la valeur de l’attribut contient l’expression
  • ’_not_cont’ : la valeur de l’attribut ne contient pas l’expression
  • ’_start’ : la valeur de l’attribut débute par l’expression
  • ’_not_start’ : la valeur de l’attribut ne débute pas par l’expression
  • ’_end’ : la valeur de l’attribut finie par l’expression
  • ’_not_end’ : la valeur de l’attribut ne finie pas par l’expression
  • ’_true’ : la valeur de l’attribut est true
  • ’_false’ : la valeur de l’attribut est false
  • ’_present’ : la valeur de l’attribut est définie
  • ’_blank’ : la valeur de l’attribut est vide
  • ’_null’ : la valeur de l’attribut est nil
  • ’_not_null’ : la valeur de l’attribut n’est pas nil

Ces chaines sont donc à post-fixer au nom de l’attribut concerné.

J’espère donc que cet article vous facilitera la mise en place de vos formulaires de recherche et vous évitera d’avoir à ré-inventer la roue.

L’équipe Synbioz.

Libres d’être ensemble.