Pensando Sphinx e Atos_as_taggable_on plugin
-
21-09-2019 - |
Pergunta
Instalei a Esfinge e pensando na Esfinge para Ruby on Rails 2.3.2.
Quando pesquisar sem condições, a pesquisa funciona bem. Agora, o que eu gostaria de fazer é filtrar por tags, então, enquanto estou usando o plugin Acts_as_taggable_on, meu modelo de anúncio se parece com o seguinte:
class Announcement < ActiveRecord::Base
acts_as_taggable_on :tags,:category
define_index do
indexes title, :as => :title, :sortable => true
indexes description, :as => :description, :sortable => true
indexes tags.name, :as => :tags
indexes category.name, :as => :category
has category(:id), :as => :category_ids
has tags(:id), :as => :tag_ids
end
Por alguma razão, quando eu executar o seguinte comando, trará apenas um anúncio, que não tem nada a ver com o que eu espero. Eu tenho muitos anúncios, então esperava muitos resultados.
Announcement.search params[:announcement][:search].to_s, :with => {:tag_ids => 1}, :page => params[:page], :per_page => 10
Eu acho que algo está errado e não está pesquisando corretamente.
Alguém pode dar uma idéia do que está acontecendo?
Obrigado, Brian
Solução
Pensar Sphinx depende de associações no modelo. Em situações comuns, você só precisa colocar a definição de índice abaixo de suas associações.
Com Atos_as_taggable_on plug-in você não tem associações relacionadas a tags no arquivo de modelo e quando você escreve
indexes tags.name ,: as =>: tags
TS interpreta como:
CAST(`announcements`.`name` AS CHAR) AS `tags`
(Veja SQL_QUERY em desenvolvimento.sphinx.conf, no meu caso). Suponho que você tenha o nome de atributo no anúncio do modelo e não se depare com o erro de reconstrução.
Mas esperamos:
CAST(GROUP_CONCAT(DISTINCT IFNULL(`tags`.`name`, '0') SEPARATOR ' ') AS CHAR) AS `tags`
e:
LEFT OUTER JOIN `taggings` ON (`announcements`.`id` = `taggings`.`taggable_id`)
LEFT OUTER JOIN `tags` ON (`tags`.`id` = `taggings`.`tag_id`) AND taggings.taggable_type = 'Announcement'
Para fazer as coisas funcionarem, basta adicionar associações relacionadas a tags em seu modelo antes de reconstruir o índice:
class Announcement < ActiveRecord::Base
acts_as_taggable_on :tags,:category
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging",
:conditions => "taggings.taggable_type = 'Announcement'"
#for context-dependent tags:
has_many :category_tags, :through => :taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag",
:conditions => "taggings.context = 'categories'"
Dentro define_index método:
indexes category_tags(:name), :as => :tags
has category_tags(:id), :as => :tag_ids, :facet => true
No controlador:
@announcement_facets = Announcement.facets params[:search], :with => {:tag_ids => [...]}
@announcements = @announcement_facets.for.paginate( :page => params[:page], :per_page => 10 )
Outras dicas
Eu descobri que simplesmente definir o índice assim:
Class Thing < ActiveRecord::Base
acts_as_taggable
define_index do
..other indexing...
indexes taggings.tag.name, :as => :tags
end
end
funcionou bem.
Uma possibilidade é que você precise declarar o tipo para tag_ids como: multi porque o TS pode ficar confuso (acabei de descobrir isso aqui http://groups.google.com/group/thinking-sphinx/browse_thread/thread/9bd4572398f35712/14d4c1503f5959a9?lnk=gst&q=yanowitz#14d4c1503f5959a9).
Mas por que não usar os nomes de tags para pesquisar? Por exemplo,
Announcement.search params[:announcement][:search].to_s, :conditions => {:tags => "my_tag"}, :page => params[:page], :per_page => 10
Ou, se você precisar procurar várias tags:
Announcement.search( "#{params[:announcement][:search].to_s} (@tags my_tag | @tags your_tag)", :page => params[:page], :per_page => 10 )
(Como de lado, você pode querer higienizar/remover caracteres de controle de esfinge da consulta fornecida pelo usuário antes de usá-la).
Para a depuração, eu entrava em console e retirava sua consulta o máximo possível (eliminaria argumentos de paginação, até a consulta (basta fazer ""), etc.).