25
Busca textual com Rais, Solr e Sunspot @mauriciojr – h-p://techbot.me/

Busca textual com solr e sunspot no rails

Embed Size (px)

DESCRIPTION

Aprenda a fazer buscas textuais com mais eficiência usando o Solr integrado a sua aplicação Rails através do Sunspot.

Citation preview

Page 1: Busca textual com solr e sunspot no rails

Busca  textual  com  Rais,  Solr  e  Sunspot  

@mauriciojr  –  h-p://techbot.me/  

Page 2: Busca textual com solr e sunspot no rails

Who?  •  Maurício  Linhares  •  @mauriciojr  •  h-p://techbot.me/  •  Developer  da  OfficeDrop.com  •  Professor  na  Faculdade  iDez  •  JUGleader  do  PBJUG  

Page 3: Busca textual com solr e sunspot no rails

“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;  

Page 4: Busca textual com solr e sunspot no rails

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  |  +-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+  

   

Page 5: Busca textual com solr e sunspot no rails

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;  

Page 6: Busca textual com solr e sunspot no rails

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  

Page 7: Busca textual com solr e sunspot no rails

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;  

Page 8: Busca textual com solr e sunspot no rails

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;  

Page 9: Busca textual com solr e sunspot no rails

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    

Page 10: Busca textual com solr e sunspot no rails

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'!

Page 11: Busca textual com solr e sunspot no rails

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  

Page 12: Busca textual com solr e sunspot no rails

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  

Page 13: Busca textual com solr e sunspot no rails

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  

Page 14: Busca textual com solr e sunspot no rails

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]  

Page 15: Busca textual com solr e sunspot no rails

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.  

Page 16: Busca textual com solr e sunspot no rails

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/  

Page 17: Busca textual com solr e sunspot no rails

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>  

Page 18: Busca textual com solr e sunspot no rails

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>  

Page 19: Busca textual com solr e sunspot no rails

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;  

Page 20: Busca textual com solr e sunspot no rails

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>  

Page 21: Busca textual com solr e sunspot no rails

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;  

Page 22: Busca textual com solr e sunspot no rails

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  

Page 23: Busca textual com solr e sunspot no rails

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  )  )  

Page 24: Busca textual com solr e sunspot no rails

Outras  ferramentas  de  busca  textual  •  Sphinx  -­‐  h-p://sphinxsearch.com/  

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

•  Ferret  -­‐  h-ps://github.com/dbalmain/ferret    

Page 25: Busca textual com solr e sunspot no rails

DÚVIDAS?