Search code examples
ruby-on-railsrubysearchmodels

Ruby on Rails search 2 models


Right... I've spent 3 days trying to do this myself to no a vale.

I have 2 models called Film and Screenings. Screenings belongs_to Film, Film has_many Screenings.

The Film has certain attributes(:title, :date_of_release, :description, :genre).

The Screening has the attributes(:start_time, :date_being_screened, :film_id(foreign key of Film)).

What I am trying to do is create a Search against both of these models. I want to do something like this...

@films = Film.advanced_search(params[:genre], params[:title], params[:start_time], params[:date_showing])

And then in the Film model...

def self.advanced_search(genre, title, start_time, date)
    search_string = "%" + title + "%"

    self.find(:all, :conditions => ["title LIKE ? OR genre = ? OR start_time LIKE ? OR date_showing = ?", title, genre, start_time, date], order: 'title')
    end
end 

I don't think this could ever work quite like this, but I'm hoping my explanation is detailed enough for anyone to understand what im TRYING to do?? :-/

Thanks for any help guys


Solution

  • I would extract the search capability into a separate (non-ActiveRecord) class, such as AdvancedSearch as it doesn't neatly fit into either the Film or Screening class.

    Rather than writing a complex SQL query, you could just search the films, then the screenings, and combine the results, for example:

    class AdvancedSearch
      def self.search
        film_matches = Film.advanced_search(...) # return an Array of Film objects
        screening_matches = Screening.advanced_search(...) # return an Array of Screening objects
    
        # combine the results
        results = film_matches + screening_matches.map(&:film)
    
        results.uniq # may be necessary to remove duplicates
      end
    end
    

    Update

    Let's say your advanced search form has two fields - Genre and Location. So when you submit the form, the params sent are:

    { :genre => 'Comedy', :location => 'London' }
    

    Your controller would then something like:

    def advanced_search(params)
      film_matches = Film.advanced_search(:genre => params[:genre])
      screening_matches = Screening.advanced_search(:location => params[:location])
    
      # remaining code as above 
    end
    

    i.e. you're splitting the params, sending each to a different model to run a search, and then combining the results.

    This is essentially an OR match - it would return films that match the genre or are being screened at that specified venue. (If you wanted and AND match you would need to the work out the array intersection).