SlideShare uma empresa Scribd logo
Busca	
  textual	
  com	
  
Rais,	
  Solr	
  e	
  Sunspot	
  

  @mauriciojr	
  –	
  h-p://techbot.me/	
  
Who?	
  
•  Maurício	
  Linhares	
  
   •    @mauriciojr	
  
   •    h-p://techbot.me/	
  
   •    Developer	
  da	
  OfficeDrop.com	
  
   •    Professor	
  na	
  Faculdade	
  iDez	
  
   •    JUGleader	
  do	
  PBJUG	
  
“LIKE”	
  considered	
  evil	
  
•  Consultas	
  que	
  usam	
  LIKE	
  só	
  são	
  eficientes	
  se	
  a	
  coluna	
  esPver	
  
   indexada	
  e	
  for	
  uma	
  busca	
  de	
  prefixo:	
  
   •  “josé%”	
  
   •  “maria%”	
  


•  Alguns	
  bancos	
  tem	
  um	
  limite	
  de	
  caracteres	
  que	
  podem	
  ser	
  
   indexado	
  em	
  campos	
  textuais;	
  

•  Bancos	
  de	
  dados	
  relacionais	
  normalmente	
  não	
  são	
  capazes	
  de	
  
   fazer	
  análise	
  para	
  tornar	
  os	
  dados	
  buscáveis	
  mais	
  fáceis	
  de	
  
   serem	
  encontrados;	
  
EXPLAINing	
  	
  
mysql>	
  select	
  *	
  from	
  products	
  where	
  name	
  like	
  "%galacPca%";	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  
|	
  id	
  |	
  name	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  |	
  price	
  |	
  descripPon	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  |	
  category_id	
  |	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  
|	
  	
  2	
  |	
  Ba-lestar	
  GalacPca:	
  The	
  Complete	
  Series	
  |	
  39.90	
  |	
  All	
  four	
  seasons	
  in	
  a	
  single	
  pack	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  |	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  2	
  |	
  
|	
  	
  3	
  |	
  Ba-lestar	
  GalacPca:	
  The	
  Boardgame	
  	
  	
  	
  	
  	
  	
  |	
  59.90	
  |	
  A	
  game	
  of	
  strife,	
  space	
  fights	
  and	
  intrige	
  |	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  3	
  |	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  
2	
  rows	
  in	
  set	
  (0.00	
  sec)	
  

	
  
	
  
mysql>	
  explain	
  select	
  *	
  from	
  products	
  where	
  name	
  like	
  "%galacPca%";	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  
|	
  id	
  |	
  select_type	
  |	
  table	
  	
  	
  	
  |	
  type	
  |	
  possible_keys	
  |	
  key	
  	
  |	
  key_len	
  |	
  ref	
  	
  |	
  rows	
  |	
  Extra	
  	
  	
  	
  	
  	
  	
  |	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  
|	
  	
  1	
  |	
  SIMPLE	
  	
  	
  	
  	
  	
  |	
  products	
  |	
  ALL	
  	
  |	
  NULL	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  |	
  NULL	
  |	
  NULL	
  	
  	
  	
  |	
  NULL	
  |	
  	
  	
  12	
  |	
  Using	
  where	
  |	
  
+-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+	
  

	
  
	
  
Entram	
  as	
  ferramentas	
  de	
  
busca	
  textual	
  
•  Bancos	
  de	
  dados	
  não	
  servem,	
  surgem	
  as	
  ferramentas	
  de	
  
   busca	
  puramente	
  textual;	
  

•  Lucene,	
  escrita	
  em	
  Java,	
  torna-­‐se	
  a	
  ferramenta	
  open	
  source	
  
   mais	
  comum	
  pra	
  solucionar	
  esse	
  Ppo	
  de	
  problema;	
  

•  Surge	
  o	
  Solr,	
  um	
  servidor	
  web	
  com	
  interface	
  semi-­‐REST	
  para	
  
   que	
  outras	
  linguagens	
  possam	
  também	
  usar	
  o	
  Lucene	
  pra	
  
   busca	
  textual;	
  
Diferenças?	
  
•  Stemming	
  –	
  redução	
  das	
  palavras	
  para	
  o	
  seu	
  radical:	
  
   •  Cat	
  –	
  catlike,	
  ca-y,	
  catwoman,	
  caright	
  


•  Remoção	
  de	
  palavras	
  comuns:	
  
   •  e,	
  ou,	
  de,	
  aqui,	
  ali,	
  se,	
  a,	
  o,	
  	
  


•  Subdivisão	
  de	
  palavras:	
  
   •  PowerShot	
  DX3100	
  –	
  power,	
  shot,	
  dx,	
  3100	
  


•  Sinônimos	
  
   •  Casa	
  –	
  lar,	
  apartamento,	
  domicílio,	
  residência	
  
Lucene,	
  Solr	
  e	
  Rails	
  
•  Vários	
  plugins	
  disponíveis,	
  mas	
  só	
  o	
  Sunspot	
  (
   h-p://outowime.github.com/sunspot/	
  )	
  é	
  realmente	
  manPdo;	
  

•  Existe	
  um	
  port	
  do	
  Lucene	
  para	
  Ruby,	
  o	
  Ferret,	
  mas	
  está	
  sem	
  
   desenvolvimento	
  já	
  a	
  muito	
  tempo	
  e	
  é	
  instável	
  quando	
  várias	
  
   aplicações	
  usam	
  o	
  mesmo	
  índice;	
  

•  É	
  possível	
  usar	
  Lucene	
  diretamente	
  se	
  você	
  esPver	
  usando	
  
   JRuby;	
  
sunspot	
  e	
  sunspot_rails	
  
•  Gems	
  para	
  integrar	
  as	
  buscas	
  com	
  Solr	
  na	
  sua	
  aplicação,	
  
   contém	
  uma	
  instalação	
  do	
  Solr	
  como	
  servidor	
  web	
  pronto	
  pra	
  
   ser	
  uPlizado;	
  

•  Integram-­‐se	
  em	
  objetos	
  AcPveRecord,	
  mas	
  também	
  é	
  possível	
  
   usar	
  modelos	
  não	
  AcPveRecord;	
  

•  Projeto	
  em	
  movimento	
  constante	
  e	
  já	
  com	
  vários	
  plugins	
  pra	
  
   se	
  integrar	
  com	
  outros	
  bancos	
  de	
  dados,	
  como	
  MongoDB;	
  
Setup	
  –	
  conIig/sunspot.yml	
  
development:	
  
	
  	
  solr:	
  
	
  	
  	
  	
  hostname:	
  localhost	
  
	
  	
  	
  	
  port:	
  8980	
  
	
  	
  	
  	
  log_level:	
  DEBUG	
  
	
  	
  auto_commit_awer_request:	
  true	
  
	
  	
  	
  
test:	
  
	
  	
  solr:	
  
	
  	
  	
  	
  hostname:	
  localhost	
  
	
  	
  	
  	
  port:	
  8981	
  
	
  	
  	
  	
  log_level:	
  OFF	
  
	
  
producPon:	
  
	
  	
  solr:	
  
	
  	
  	
  	
  hostname:	
  localhost	
  
	
  	
  	
  	
  port:	
  8982	
  
	
  	
  	
  	
  log_level:	
  WARNING	
  
	
  	
  auto_commit_awer_request:	
  true	
  	
  
Setup	
  –	
  Parte	
  2	
  
•  Pegue	
  o	
  código	
  fonte	
  do	
  Sunspot	
  no	
  GitHub	
  -­‐	
  
   h-ps://github.com/outowime/sunspot	
  	
  

•  Copie	
  a	
  pasta	
  “sunspot/solr-­‐1.3/solr”	
  pra	
  dentro	
  do	
  seu	
  
   projeto	
  Rails	
  

•  Adicione	
  o	
  Sunspot	
  no	
  seu	
  Gemfile:	
  
	
  
        !gem 'sunspot',                                                 '1.2.1'!
        !gem 'sunspot_rails',                                           '1.2.1'!
Integrando	
  o	
  sunspot	
  em	
  um	
  
model	
  
class	
  Product	
  <	
  AcPveRecord::Base	
  
	
  	
  	
  
	
  	
  belongs_to	
  :category	
  
	
  	
  	
  
	
  	
  validates_presence_of	
  :name,	
  :descripPon,	
  :category_id,	
  :price	
  
	
  	
  validates_uniqueness_of	
  :name,	
  :allow_blank	
  =>	
  true	
  
	
  	
  	
  
	
  	
  searchable	
  :auto_index	
  =>	
  true,	
  :auto_remove	
  =>	
  true	
  do	
  
	
  	
  	
  	
  text	
  :name,	
  :boost	
  =>	
  2.0	
  
	
  	
  	
  	
  text	
  :descripPon	
  
	
  	
  	
  	
  float	
  :price	
  
	
  	
  	
  	
  integer	
  :category_id,	
  :references	
  =>	
  ::Category	
  
	
  	
  end	
  
	
  	
  	
  
	
  	
  def	
  to_s	
  
	
  	
  	
  	
  self.name	
  
	
  	
  end	
  
	
  	
  	
  
end	
  
E	
  no	
  controller	
  
class	
  ProductsController	
  <	
  ApplicaPonController	
  
	
  	
  
	
  	
  def	
  index	
  
	
  	
  	
  	
  @products	
  =	
  if	
  params[:q].blank?	
  
	
  	
  	
  	
  	
  	
  Product.all	
  :order	
  =>	
  'name	
  ASC'	
  
	
  	
  	
  	
  else	
  
	
  	
  	
  	
  	
  	
  Product.solr_search	
  do	
  |s|	
  
	
  	
  	
  	
  	
  	
  	
  	
  s.keywords	
  params[:q]	
  
	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  end	
  
	
  	
  end	
  
	
  	
  
end	
  
Mas	
  antes	
  de	
  continuar,	
  um	
  
pequeno	
  monkey-­‐patch	
  
::Sunspot::Search::StandardSearch.class_eval	
  do	
  
	
  	
  
	
  	
  include	
  Enumerable	
  
	
  	
  
	
  	
  delegate(	
  
	
  	
  	
  	
  :current_page,	
  
	
  	
  	
  	
  :per_page,	
  
	
  	
  	
  	
  :total_entries,	
  
	
  	
  	
  	
  :total_pages,	
  
	
  	
  	
  	
  :offset,	
  
	
  	
  	
  	
  :previous_page,	
  
	
  	
  	
  	
  :next_page,	
  
	
  	
  	
  	
  :out_of_bounds?,	
  
	
  	
  	
  	
  :each,	
  
	
  	
  	
  	
  :in_groups_of,	
  
	
  	
  	
  	
  :blank?,	
  
	
  	
  	
  	
  :[],	
  
	
  	
  	
  	
  :to	
  =>	
  :results)	
  
	
  	
  
end	
  
Na	
  sua	
  view	
  -­‐	
  1	
  
 %h1	
  Products	
  
 	
  
 %p=	
  link_to	
  'New	
  product',	
  new_product_path	
  
 	
  	
  	
  
 %h2	
  Search	
  products	
  
 	
  
 -­‐	
  form_tag	
  products_path,	
  :method	
  =>	
  :get	
  do	
  |t|	
  
 	
  	
  %p	
  
 	
  	
  	
  	
  =	
  text_field_tag	
  :q,	
  params[:q]	
  
 	
  	
  	
  	
  =	
  submit_tag	
  'Go!'	
  
 	
  	
  	
  	
  =	
  hidden_field_tag	
  :category_id,	
  params[:category_id]	
  
Na	
  sua	
  view	
  -­‐	
  2	
  
 =	
  will_paginate	
  @products	
  
 	
  	
  %table	
  
 	
  	
  	
  	
  %thead	
  
 	
  	
  	
  	
  	
  	
  %tr	
  
 	
  	
  	
  	
  	
  	
  	
  	
  %th	
  Name	
  
 	
  	
  	
  	
  	
  	
  	
  	
  %th	
  Category	
  
 	
  	
  	
  	
  	
  	
  	
  	
  %th	
  Price	
  
 	
  	
  	
  	
  %tbody	
  
 	
  	
  	
  	
  	
  	
  -­‐	
  for	
  product	
  in	
  @products	
  
 	
  	
  	
  	
  	
  	
  	
  	
  %tr	
  
 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  %td=	
  product	
  
 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  %td=	
  product.category	
  
 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  %td=	
  product.price	
  
 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  %td=	
  link_to	
  'Edit',	
  edit_product_path(	
  product	
  )	
  
 	
  	
  =	
  will_paginate	
  @products	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
 -­‐	
  else	
  
 	
  	
  %p	
  There	
  are	
  no	
  products	
  available.	
  
Análise	
  de	
  dados	
  

•  Inicie	
  o	
  Solr	
  no	
  seu	
  projeto:	
  
    •  rake	
  sunspot:solr:run	
  



•  Faça	
  a	
  indexação	
  de	
  alguns	
  dos	
  seus	
  dados:	
  
    •  Product.solr_reindex	
  


•  Abra	
  a	
  administração	
  do	
  Solr:	
  
    •  h-p://localhost:8980/solr/admin/	
  
Snippet	
  -­‐	
  solr/conf/
schema.xml	
  
	
  
<fieldtype	
  class="solr.TextField"	
  posiPonIncrementGap="100"	
  
name="text">	
  
	
  	
  	
  	
  	
  	
  <analyzer>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <tokenizer	
  class="solr.StandardTokenizerFactory"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <filter	
  class="solr.StandardFilterFactory"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <filter	
  class="solr.LowerCaseFilterFactory"/>	
  
	
  	
  	
  	
  	
  	
  </analyzer>	
  
</fieldtype>	
  
Adicionando	
  mais	
  Iiltros	
  
<fieldtype	
  class="solr.TextField"	
  posiPonIncrementGap="100"	
  
name="text">	
  
	
  	
  <analyzer>	
  
	
  	
  	
  	
  <tokenizer	
  class="solr.StandardTokenizerFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StandardFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.LowerCaseFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StopFilterFactory"	
  words="stopwords.txt"	
  
ignoreCase="true"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.ISOLaPn1AccentFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.TrimFilterFactory"	
  />	
  
	
  	
  </analyzer>	
  
</fieldtype>	
  
Buscas	
  com	
  match	
  parcial	
  
•  O	
  Lucene	
  normalmente	
  só	
  retorna	
  um	
  match	
  em	
  uma	
  palavra	
  
   se	
  ela	
  for	
  um	
  match	
  total	
  em	
  um	
  token,	
  ele	
  não	
  faz	
  matches	
  
   parciais	
  diretamente;	
  

•  Há	
  um	
  operador	
  pra	
  permiPr	
  o	
  match	
  parcial	
  de	
  palavras,	
  “*”,	
  
   mas	
  esse	
  operador	
  só	
  é	
  indicado	
  para	
  buscas	
  simples	
  em	
  
   índices	
  pequenos;	
  

•  Se	
  você	
  tem	
  um	
  índice	
  grande	
  e	
  precisa	
  de	
  performance	
  nas	
  
   suas	
  buscas,	
  precisa	
  usar	
  um	
  filtro	
  que	
  gere	
  pedaços	
  da	
  
   palavra	
  como	
  tokens	
  para	
  serem	
  buscados;	
  
Adicionando	
  o	
  ngram	
  Iilter	
  
<fieldtype	
  class="solr.TextField"	
  posiPonIncrementGap="100"	
  name="text">	
  
	
  	
  <analyzer	
  type="index">	
  
	
  	
  	
  	
  <tokenizer	
  class="solr.StandardTokenizerFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StandardFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.LowerCaseFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StopFilterFactory"	
  words="stopwords.txt"	
  ignoreCase="true"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.ISOLaPn1AccentFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.TrimFilterFactory"	
  />	
  
	
  	
  	
  	
  <filter	
  class="solr.EdgeNGramFilterFactory"	
  
	
  	
  	
  	
  	
  	
  minGramSize="3"	
  
	
  	
  	
  	
  	
  	
  maxGramSize="30"/>	
  
	
  	
  </analyzer>	
  
	
  	
  <analyzer	
  type="query">	
  
	
  	
  	
  	
  <tokenizer	
  class="solr.StandardTokenizerFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StandardFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.LowerCaseFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.StopFilterFactory"	
  words="stopwords.txt"	
  ignoreCase="true"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.ISOLaPn1AccentFilterFactory"/>	
  
	
  	
  	
  	
  <filter	
  class="solr.TrimFilterFactory"	
  />	
  
	
  	
  </analyzer>	
  
</fieldtype>	
  
Facets	
  
•  Facets	
  são	
  uma	
  forma	
  de	
  agrupar	
  os	
  resultados	
  com	
  base	
  em	
  
   um	
  dos	
  campos	
  do	
  seu	
  objeto	
  indexado;	
  

•  Você	
  poderia	
  retornar	
  os	
  produtos	
  do	
  resultado	
  da	
  busca	
  e	
  
   mostrar	
  para	
  o	
  usuário	
  quantos	
  produtos	
  em	
  cada	
  categoria	
  
   foram	
  retornados,	
  assim	
  o	
  usuário	
  poderia	
  filtrar	
  também	
  por	
  
   categoria;	
  
Adicionando	
  facets	
  na	
  busca	
  
	
  	
  	
  	
  	
  	
  result	
  =	
  Product.solr_search	
  do	
  |s|	
  
	
  	
  	
  	
  	
  	
  	
  	
  s.keywords	
  params[:q]	
  
	
  	
  	
  	
  	
  	
  	
  	
  unless	
  params[:category_id].blank?	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  s.with(	
  :category_id	
  ).equal_to(	
  params[:category_id].to_i	
  )	
  
	
  	
  	
  	
  	
  	
  	
  	
  else	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  s.facet	
  :category_id	
  
	
  	
  	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  	
  	
  	
  	
  s.paginate	
  :per_page	
  =>	
  3,	
  :page	
  =>	
  @page	
  
	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  if	
  result.facet(	
  :category_id	
  )	
  
	
  	
  	
  	
  	
  	
  	
  	
  @facet_rows	
  =	
  result.facet(:category_id).rows	
  	
  	
  
	
  	
  	
  	
  	
  	
  end	
  
Na	
  sua	
  view	
  
	
  
-­‐	
  unless	
  @facet_rows.blank?	
  
	
  	
  %h3	
  Filters	
  
	
  	
  -­‐	
  %ul	
  
	
  	
  	
  	
  -­‐	
  @facet_rows.each	
  do	
  |facet|	
  
	
  	
  	
  	
  	
  	
  %li=	
  link_to(	
  "#{facet.instance}	
  (#{facet.count})",	
  
products_path(	
  :q	
  =>	
  params[:q],	
  :category_id	
  =>	
  
facet.instance	
  )	
  )	
  
Outras	
  ferramentas	
  de	
  busca	
  
textual	
  
•  Sphinx	
  -­‐	
  h-p://sphinxsearch.com/	
  



•  ElasPcSearch	
  -­‐	
  h-p://www.elasPcsearch.org/	
  



•  Ferret	
  -­‐	
  h-ps://github.com/dbalmain/ferret	
  	
  
DÚVIDAS?	
  

Mais conteúdo relacionado

PDF
Construindo Soluções Científicas com Big Data & MapReduce
PDF
Computação Científica com Python, Numpy e Scipy
PDF
HTML, CSS & JS: olhando pra frente
PDF
How to use Elasticsearch Analyzers by EmergiNet
KEY
Como Ruby on Rails pode o tornar um programador pior
PPTX
Mercado de TI
PPTX
Unindo Ruby e Java através de uma arquitetura orientada a serviços na OfficeDrop
PPTX
Mixing Ruby and Java in a Service Oriented Architecture at OfficeDrop
Construindo Soluções Científicas com Big Data & MapReduce
Computação Científica com Python, Numpy e Scipy
HTML, CSS & JS: olhando pra frente
How to use Elasticsearch Analyzers by EmergiNet
Como Ruby on Rails pode o tornar um programador pior
Mercado de TI
Unindo Ruby e Java através de uma arquitetura orientada a serviços na OfficeDrop
Mixing Ruby and Java in a Service Oriented Architecture at OfficeDrop

Mais de Maurício Linhares (20)

PDF
Aprendendo ruby
PDF
Curso java 07 - exceções
PDF
Curso java 08 - mais sobre coleções
PDF
Curso java 06 - mais construtores, interfaces e polimorfismo
PDF
Curso java 05 - herança, classes e métodos abstratos
PDF
Curso java 04 - ap is e bibliotecas
PPTX
Curso java 01 - molhando os pés com java
PDF
Curso java 02 - variáveis
PDF
Curso java 03 - métodos e parâmetros
PDF
Extreme programming
PDF
Feature Driven Development
PDF
Migrando pra Scala
PPTX
Outsourcing e trabalho remoto para a nuvem
PDF
Mercado hoje
PDF
Análise de sistemas oo 1
PDF
Revisão html e java script
PPTX
Aulas de Java Avançado 2- Faculdade iDez 2010
PPTX
Introdução ao desenvolvimento web - 2 - iDez 2010
PPTX
Aulas de Java Avançado 1 - Faculdade iDez 2010
PDF
Projeto e desenvolvimento de sistemas de informação 4 - computação em rede
Aprendendo ruby
Curso java 07 - exceções
Curso java 08 - mais sobre coleções
Curso java 06 - mais construtores, interfaces e polimorfismo
Curso java 05 - herança, classes e métodos abstratos
Curso java 04 - ap is e bibliotecas
Curso java 01 - molhando os pés com java
Curso java 02 - variáveis
Curso java 03 - métodos e parâmetros
Extreme programming
Feature Driven Development
Migrando pra Scala
Outsourcing e trabalho remoto para a nuvem
Mercado hoje
Análise de sistemas oo 1
Revisão html e java script
Aulas de Java Avançado 2- Faculdade iDez 2010
Introdução ao desenvolvimento web - 2 - iDez 2010
Aulas de Java Avançado 1 - Faculdade iDez 2010
Projeto e desenvolvimento de sistemas de informação 4 - computação em rede
Anúncio

Último (16)

PPTX
Programação - Linguagem C - Variáveis, Palavras Reservadas, tipos de dados, c...
PDF
Custos e liquidação no SAP Transportation Management, TM130 Col18
PDF
Fundamentos de gerenciamento de ordens e planejamento no SAP TransportationMa...
PDF
Processos na gestão de transportes, TM100 Col18
PDF
20250805_ServiceNow e a Arquitetura Orientada a Serviços (SOA) A Base para Ap...
PDF
Mergulho profundo técnico para gestão de transportes no SAP S/4HANA, S4TM6 Col14
PDF
Otimizador de planejamento e execução no SAP Transportation Management, TM120...
PPTX
Gestao-de-Bugs-em-Software-Introducao.pptxxxxxxxx
PDF
Custos e faturamento no SAP S/4HANA Transportation Management, S4TM3 Col26
PPTX
Como-se-implementa-um-softwareeeeeeeeeeeeeeeeeeeeeeeee.pptx
PDF
COBITxITIL-Entenda as diferença em uso governança TI
PPTX
Informática Aplicada Informática Aplicada Plano de Ensino - estudo de caso NR...
PPTX
Arquitetura de computadores - Memórias Secundárias
PDF
Gestão de transportes básica no SAP S/4HANA, S4611 Col20
PDF
Termos utilizados na designação de relação entre pessoa e uma obra.pdf
PDF
Fullfilment AI - Forum ecommerce 2025 // Distrito e Total Express
Programação - Linguagem C - Variáveis, Palavras Reservadas, tipos de dados, c...
Custos e liquidação no SAP Transportation Management, TM130 Col18
Fundamentos de gerenciamento de ordens e planejamento no SAP TransportationMa...
Processos na gestão de transportes, TM100 Col18
20250805_ServiceNow e a Arquitetura Orientada a Serviços (SOA) A Base para Ap...
Mergulho profundo técnico para gestão de transportes no SAP S/4HANA, S4TM6 Col14
Otimizador de planejamento e execução no SAP Transportation Management, TM120...
Gestao-de-Bugs-em-Software-Introducao.pptxxxxxxxx
Custos e faturamento no SAP S/4HANA Transportation Management, S4TM3 Col26
Como-se-implementa-um-softwareeeeeeeeeeeeeeeeeeeeeeeee.pptx
COBITxITIL-Entenda as diferença em uso governança TI
Informática Aplicada Informática Aplicada Plano de Ensino - estudo de caso NR...
Arquitetura de computadores - Memórias Secundárias
Gestão de transportes básica no SAP S/4HANA, S4611 Col20
Termos utilizados na designação de relação entre pessoa e uma obra.pdf
Fullfilment AI - Forum ecommerce 2025 // Distrito e Total Express
Anúncio

Busca textual com solr e sunspot no rails

  • 1. Busca  textual  com   Rais,  Solr  e  Sunspot   @mauriciojr  –  h-p://techbot.me/  
  • 2. Who?   •  Maurício  Linhares   •  @mauriciojr   •  h-p://techbot.me/   •  Developer  da  OfficeDrop.com   •  Professor  na  Faculdade  iDez   •  JUGleader  do  PBJUG  
  • 3. “LIKE”  considered  evil   •  Consultas  que  usam  LIKE  só  são  eficientes  se  a  coluna  esPver   indexada  e  for  uma  busca  de  prefixo:   •  “josé%”   •  “maria%”   •  Alguns  bancos  tem  um  limite  de  caracteres  que  podem  ser   indexado  em  campos  textuais;   •  Bancos  de  dados  relacionais  normalmente  não  são  capazes  de   fazer  análise  para  tornar  os  dados  buscáveis  mais  fáceis  de   serem  encontrados;  
  • 4. EXPLAINing     mysql>  select  *  from  products  where  name  like  "%galacPca%";   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |  id  |  name                                                                            |  price  |  descripPon                                                                |  category_id  |   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |    2  |  Ba-lestar  GalacPca:  The  Complete  Series  |  39.90  |  All  four  seasons  in  a  single  pack                    |                      2  |   |    3  |  Ba-lestar  GalacPca:  The  Boardgame              |  59.90  |  A  game  of  strife,  space  fights  and  intrige  |                      3  |   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   2  rows  in  set  (0.00  sec)       mysql>  explain  select  *  from  products  where  name  like  "%galacPca%";   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |  id  |  select_type  |  table        |  type  |  possible_keys  |  key    |  key_len  |  ref    |  rows  |  Extra              |   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |    1  |  SIMPLE            |  products  |  ALL    |  NULL                    |  NULL  |  NULL        |  NULL  |      12  |  Using  where  |   +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+      
  • 5. Entram  as  ferramentas  de   busca  textual   •  Bancos  de  dados  não  servem,  surgem  as  ferramentas  de   busca  puramente  textual;   •  Lucene,  escrita  em  Java,  torna-­‐se  a  ferramenta  open  source   mais  comum  pra  solucionar  esse  Ppo  de  problema;   •  Surge  o  Solr,  um  servidor  web  com  interface  semi-­‐REST  para   que  outras  linguagens  possam  também  usar  o  Lucene  pra   busca  textual;  
  • 6. Diferenças?   •  Stemming  –  redução  das  palavras  para  o  seu  radical:   •  Cat  –  catlike,  ca-y,  catwoman,  caright   •  Remoção  de  palavras  comuns:   •  e,  ou,  de,  aqui,  ali,  se,  a,  o,     •  Subdivisão  de  palavras:   •  PowerShot  DX3100  –  power,  shot,  dx,  3100   •  Sinônimos   •  Casa  –  lar,  apartamento,  domicílio,  residência  
  • 7. Lucene,  Solr  e  Rails   •  Vários  plugins  disponíveis,  mas  só  o  Sunspot  ( h-p://outowime.github.com/sunspot/  )  é  realmente  manPdo;   •  Existe  um  port  do  Lucene  para  Ruby,  o  Ferret,  mas  está  sem   desenvolvimento  já  a  muito  tempo  e  é  instável  quando  várias   aplicações  usam  o  mesmo  índice;   •  É  possível  usar  Lucene  diretamente  se  você  esPver  usando   JRuby;  
  • 8. sunspot  e  sunspot_rails   •  Gems  para  integrar  as  buscas  com  Solr  na  sua  aplicação,   contém  uma  instalação  do  Solr  como  servidor  web  pronto  pra   ser  uPlizado;   •  Integram-­‐se  em  objetos  AcPveRecord,  mas  também  é  possível   usar  modelos  não  AcPveRecord;   •  Projeto  em  movimento  constante  e  já  com  vários  plugins  pra   se  integrar  com  outros  bancos  de  dados,  como  MongoDB;  
  • 9. Setup  –  conIig/sunspot.yml   development:      solr:          hostname:  localhost          port:  8980          log_level:  DEBUG      auto_commit_awer_request:  true         test:      solr:          hostname:  localhost          port:  8981          log_level:  OFF     producPon:      solr:          hostname:  localhost          port:  8982          log_level:  WARNING      auto_commit_awer_request:  true    
  • 10. Setup  –  Parte  2   •  Pegue  o  código  fonte  do  Sunspot  no  GitHub  -­‐   h-ps://github.com/outowime/sunspot     •  Copie  a  pasta  “sunspot/solr-­‐1.3/solr”  pra  dentro  do  seu   projeto  Rails   •  Adicione  o  Sunspot  no  seu  Gemfile:     !gem 'sunspot', '1.2.1'! !gem 'sunspot_rails', '1.2.1'!
  • 11. Integrando  o  sunspot  em  um   model   class  Product  <  AcPveRecord::Base            belongs_to  :category            validates_presence_of  :name,  :descripPon,  :category_id,  :price      validates_uniqueness_of  :name,  :allow_blank  =>  true            searchable  :auto_index  =>  true,  :auto_remove  =>  true  do          text  :name,  :boost  =>  2.0          text  :descripPon          float  :price          integer  :category_id,  :references  =>  ::Category      end            def  to_s          self.name      end         end  
  • 12. E  no  controller   class  ProductsController  <  ApplicaPonController          def  index          @products  =  if  params[:q].blank?              Product.all  :order  =>  'name  ASC'          else              Product.solr_search  do  |s|                  s.keywords  params[:q]              end          end      end       end  
  • 13. Mas  antes  de  continuar,  um   pequeno  monkey-­‐patch   ::Sunspot::Search::StandardSearch.class_eval  do          include  Enumerable          delegate(          :current_page,          :per_page,          :total_entries,          :total_pages,          :offset,          :previous_page,          :next_page,          :out_of_bounds?,          :each,          :in_groups_of,          :blank?,          :[],          :to  =>  :results)       end  
  • 14. Na  sua  view  -­‐  1   %h1  Products     %p=  link_to  'New  product',  new_product_path         %h2  Search  products     -­‐  form_tag  products_path,  :method  =>  :get  do  |t|      %p          =  text_field_tag  :q,  params[:q]          =  submit_tag  'Go!'          =  hidden_field_tag  :category_id,  params[:category_id]  
  • 15. Na  sua  view  -­‐  2   =  will_paginate  @products      %table          %thead              %tr                  %th  Name                  %th  Category                  %th  Price          %tbody              -­‐  for  product  in  @products                  %tr                      %td=  product                      %td=  product.category                      %td=  product.price                      %td=  link_to  'Edit',  edit_product_path(  product  )      =  will_paginate  @products                       -­‐  else      %p  There  are  no  products  available.  
  • 16. Análise  de  dados   •  Inicie  o  Solr  no  seu  projeto:   •  rake  sunspot:solr:run   •  Faça  a  indexação  de  alguns  dos  seus  dados:   •  Product.solr_reindex   •  Abra  a  administração  do  Solr:   •  h-p://localhost:8980/solr/admin/  
  • 17. Snippet  -­‐  solr/conf/ schema.xml     <fieldtype  class="solr.TextField"  posiPonIncrementGap="100"   name="text">              <analyzer>                  <tokenizer  class="solr.StandardTokenizerFactory"/>                  <filter  class="solr.StandardFilterFactory"/>                  <filter  class="solr.LowerCaseFilterFactory"/>              </analyzer>   </fieldtype>  
  • 18. Adicionando  mais  Iiltros   <fieldtype  class="solr.TextField"  posiPonIncrementGap="100"   name="text">      <analyzer>          <tokenizer  class="solr.StandardTokenizerFactory"/>          <filter  class="solr.StandardFilterFactory"/>          <filter  class="solr.LowerCaseFilterFactory"/>          <filter  class="solr.StopFilterFactory"  words="stopwords.txt"   ignoreCase="true"/>          <filter  class="solr.ISOLaPn1AccentFilterFactory"/>          <filter  class="solr.TrimFilterFactory"  />      </analyzer>   </fieldtype>  
  • 19. Buscas  com  match  parcial   •  O  Lucene  normalmente  só  retorna  um  match  em  uma  palavra   se  ela  for  um  match  total  em  um  token,  ele  não  faz  matches   parciais  diretamente;   •  Há  um  operador  pra  permiPr  o  match  parcial  de  palavras,  “*”,   mas  esse  operador  só  é  indicado  para  buscas  simples  em   índices  pequenos;   •  Se  você  tem  um  índice  grande  e  precisa  de  performance  nas   suas  buscas,  precisa  usar  um  filtro  que  gere  pedaços  da   palavra  como  tokens  para  serem  buscados;  
  • 20. Adicionando  o  ngram  Iilter   <fieldtype  class="solr.TextField"  posiPonIncrementGap="100"  name="text">      <analyzer  type="index">          <tokenizer  class="solr.StandardTokenizerFactory"/>          <filter  class="solr.StandardFilterFactory"/>          <filter  class="solr.LowerCaseFilterFactory"/>          <filter  class="solr.StopFilterFactory"  words="stopwords.txt"  ignoreCase="true"/>          <filter  class="solr.ISOLaPn1AccentFilterFactory"/>          <filter  class="solr.TrimFilterFactory"  />          <filter  class="solr.EdgeNGramFilterFactory"              minGramSize="3"              maxGramSize="30"/>      </analyzer>      <analyzer  type="query">          <tokenizer  class="solr.StandardTokenizerFactory"/>          <filter  class="solr.StandardFilterFactory"/>          <filter  class="solr.LowerCaseFilterFactory"/>          <filter  class="solr.StopFilterFactory"  words="stopwords.txt"  ignoreCase="true"/>          <filter  class="solr.ISOLaPn1AccentFilterFactory"/>          <filter  class="solr.TrimFilterFactory"  />      </analyzer>   </fieldtype>  
  • 21. Facets   •  Facets  são  uma  forma  de  agrupar  os  resultados  com  base  em   um  dos  campos  do  seu  objeto  indexado;   •  Você  poderia  retornar  os  produtos  do  resultado  da  busca  e   mostrar  para  o  usuário  quantos  produtos  em  cada  categoria   foram  retornados,  assim  o  usuário  poderia  filtrar  também  por   categoria;  
  • 22. Adicionando  facets  na  busca              result  =  Product.solr_search  do  |s|                  s.keywords  params[:q]                  unless  params[:category_id].blank?                      s.with(  :category_id  ).equal_to(  params[:category_id].to_i  )                  else                      s.facet  :category_id                  end                  s.paginate  :per_page  =>  3,  :page  =>  @page              end                            if  result.facet(  :category_id  )                  @facet_rows  =  result.facet(:category_id).rows                  end  
  • 23. Na  sua  view     -­‐  unless  @facet_rows.blank?      %h3  Filters      -­‐  %ul          -­‐  @facet_rows.each  do  |facet|              %li=  link_to(  "#{facet.instance}  (#{facet.count})",   products_path(  :q  =>  params[:q],  :category_id  =>   facet.instance  )  )  
  • 24. Outras  ferramentas  de  busca   textual   •  Sphinx  -­‐  h-p://sphinxsearch.com/   •  ElasPcSearch  -­‐  h-p://www.elasPcsearch.org/   •  Ferret  -­‐  h-ps://github.com/dbalmain/ferret