GithubHelp home page GithubHelp logo

Models with Associations about retire HOT 8 CLOSED

karmi avatar karmi commented on September 15, 2024
Models with Associations

from retire.

Comments (8)

karmi avatar karmi commented on September 15, 2024

Hi, there's no elegant solution for this problem with associations, in the current version.

The wrapper is set to the model, and obviously, the model inicialization fails with NoMethodError (or other) error when being passed the hash, which was returned from ElasticSearch.

First, I have anticipated the issue. As you can see in commit 5d89dd1, Tire was initializing the collection from the records retrieved from the database, originally (via MyModel.find [list of IDs]). This, however, had many issues. The performance was unnecesarilly dumped by roundtrips to the database. The sort order was not preserved (a huge bug). So I decided against it.

Second, the problem can be immediately solved. Let's simplify your situation (because that's really a M to N relation, with videos and categories).

The Article model has many Comment associations:

class Article < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks

  index_name 'articles-with-comments'

  has_many :comments
end

The Comment is simple:

class Comment < ActiveRecord::Base
  belongs_to :article
end

Now, obviously, when you have the Article indexed as:

def to_indexed_json
  { :id      => id,
    :title   => title,
    :content => content,
    :comments => comments.map { |c| { :id => id, :author => c.author, :content => c.content }  },
  }.to_json
end

it would break on Article#comments= being given wrong type of objects on initialization.

You can temporarily, and in a quite ugly way, solve it like this:

class Article < ActiveRecord::Base
  # ...
  alias :original_comments= :comments=
  def comments=(comments)
    # Are the objects returned from ElasticSearch?
    if comments.all? { |c| c.is_a? Hash }
      comment_ids = comments.map { |c| c['id'] }
    else
      send :original_comments=, comments
    end
  end
end

So, when we are initializing the comments from ElasticSearch, set the ID (an retrieve the records from database), otherwise, perform the original method.

Of course, this could be isolated into completely different model, like this:

class SearchedArticle < Article
  index_name 'articles-with-comments'

  def comments=(comments)
    comment_ids = comments.map { |c| c['id'] }
  end
end

You'll then search via this fake model: SearchedArticle.search 'love'. Of course, this is an ugly, and temporary way of dealing with the issue. The underlying issue is, of course, blocking for advanced production usage.

When I have discussed this with another Tire users, the real, and elegant, solution seems to me like this:

  • Do not wrap the results in the real model class, but wrap them in Tire::Results::Item, as usual
  • Add a proxy object to every result, let's say object, which would point, via the ID, to the underlying record (and model instance)

In this way, we would keep the awesome performance of ElasticSearch, for most cases. Where you'd need or like to get the original model instance, you'd just write result.object.comments.first.my_complicated_method, instead of result.comments.my_complicated_method.

Thank you for the report, and I'll definitely have a look at this issue.

from retire.

russ avatar russ commented on September 15, 2024

Cool. I'll give that a try. Thanks for the detailed response.

from retire.

karmi avatar karmi commented on September 15, 2024

@russ, any luck with the proposed solution?

from retire.

russ avatar russ commented on September 15, 2024

The solution did work. But for the time being I went back to sunspot. I will definitely give Tire another try in the future.

from retire.

karmi avatar karmi commented on September 15, 2024

Understood. Thanks for the report!

from retire.

karmi avatar karmi commented on September 15, 2024

Hi @russ, I've tried to solve this issue with ActiveRecord associations -- see the closing commit.

Could you test it against your use-case, if you still have the code handy? The only thing you need is to define Git as endpoint in the gemfile:

gem "tire", :git => "git://github.com/karmi/tire.git", :branch => "activerecord"

from retire.

aaronchi avatar aaronchi commented on September 15, 2024

This looks very cool :D

from retire.

karmi avatar karmi commented on September 15, 2024

@aaronchi: It looks, but let's see how it works in real world :)

from retire.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.