When Ultrasphinx is used with polymorphic associations…


Lets first consider simple has_many and belongs_to associations as:

class Article < ActiveRecord::Base
  has_many :comments
end
class Comment < ActiveRecord::Base
  belongs_to :article
end

Now if you wish to index the title of associated article with comment for searching, you just need to add ” is_indexed :fields => :body, :include => [{ :association_name => 'article', :field => 'title', :as=> 'article_title'}] ”

So, your comment model will look like:

class Comment < ActiveRecord::Base
  belongs_to :article

  is_indexed :fields => :body,
             :include => [{ :association_name => 'article', :field => 'title', :as=> 'article_title'}]
end

Setup ultrasphinx by issuing:

rake ultrasphinx:bootstrap

Now at rails console try:

>> article = Article.create(:title => "This is the first article's title", :body => "Here comes first article's body.")
=> #<article id: 1, title: "This is the first article's title", tagline: nil, type: nil, body: "Here comes first article's body.", created_at: "2008-08-07 07:47:53", updated_at: "2008-08-07 07:47:53">
>> article.comments
=> []
>> article.comments<<comment.new(:body=>"first comment on first article. which says yahooo !!")
=> [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">]
>> Comment.find :all
=> [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">]
>> search = Ultrasphinx::Search.new(:query => "first article's title", :class_names => ['Comment']).run.results
=> [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">]

Simple, we have article and comment models with has_many belongs_to associations. Comments are indexed with their associated article title. So it can return comments if query matches with article’s titles.

Now, consider a case when we wish to change comment model to make it polymorphic. In that case our models will be look like:

 class Article < ActiveRecord::Base
   has_many :comments, :as => :commentable, :dependent => :destroy
 end
class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

Check at rails if associations are working fine:

>> article = Article.create(:title => "First article title", :body => "here comes first artile's title")
=> #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31">
>> comment = Comment.new(:body => "hello there, I am a comment")
=> #<comment id: nil, body: "hello there, I am a comment", commentable_id: nil, commentable_type: nil, created_at: nil, updated_at: nil>
>> comment.commentable = article
=> #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31">
>> comment.save
=> true
>> comment.reload
=> #<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">
>> article.reload
=> #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31">
>> article.comments
=> [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">]
>> comment = Comment.new(:body => "hi there, this is second comment")
=> #<comment id: nil, body: "hi there, this is second comment", commentable_id: nil, commentable_type: nil, created_at: nil, updated_at: nil>
>> article.comments<< comment
=> [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">]
>> article.comments
=> [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">]
>> comment.commentable
=> #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31">

Now, the point is to index article title with comments. Here we can get associated article using ‘commentable’ i.e. comment.commentable.

So, change ultrasphinx is_indexed code in comment model accordingly:

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true

  is_indexed :fields => :body,
            :include => [{ :association_name => 'commentable', :field => 'title', :as=> 'article_title'}]

end

Note that we have changed associan_name to ‘commentable’.
Next, when we reconfigure ultrasphinx using ” rake ultrasphinx:bootstrap”(since we have changed db schema), it starts throwing errors:

** Invoke ultrasphinx:bootstrap (first_time)
** Invoke ultrasphinx:_environment (first_time)
** Invoke environment (first_time)
** Execute environment
rake aborted!
uninitialized constant Commentable
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/inflector.rb:283:in `constantize'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/core_ext/string/inflections.rb:143:in `constantize'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/associations.rb:19:in `get_association_model'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:128:in `configure'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:124:in `each'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:124:in `configure'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:101:in `each'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:101:in `configure'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/configure.rb:32:in `load_constants'
/Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/autoload.rb:8:in `after_initialize'
/Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:148:in `process'
/Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:93:in `send'
/Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:93:in `run'
/Users/akhilbansal/work/sti/config/environment.rb:13
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `require'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:354:in `new_constants_in'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
/Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/tasks/misc.rake:3
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:546:in `call'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:546:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:541:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:541:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:508:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:518:in `invoke_prerequisites'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `send'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:515:in `invoke_prerequisites'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:507:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:518:in `invoke_prerequisites'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `send'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:515:in `invoke_prerequisites'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:507:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:494:in `invoke'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1931:in `invoke_task'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1948:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1903:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1881:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1948:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1878:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.1/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19

After spending some time on research, I was able to make it work by making some changes in comment model:

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true

is_indexed :fields  => :body,
           :include => [{:class_name => 'Article',  :association_sql => 'left outer join articles on articles.id = comments.commentable_id and comments.commentable_type = "Article"', :field => 'title', :as=> 'article_title'}]
end

Lets check it on rails console:

>> search = Ultrasphinx::Search.new(:query => 'first article', :class_name => "Comment").run.results
=> [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">]

In such cases we need to define class_name and association_sql in is_indexed statement instead of association_name.

Hope it helps…

Sponsors

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts
Leopard sucks while installing/updating gems
When Ultrasphinx is used with STI…
If my code or post helps you then please recommend me at workingwithrails.com by clicking on button below:
Recommend Me

Reader Comments

Be the first to leave a comment!

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.