ruby-on-railsrubyruby-on-rails-4activerecordsqueel

Using scopes for search queries


I'm working on improving my search functionality in my app by using models.

This is what I've been using so far:

searchController#index:

def index
    # Enable display of results page on direct access with no params
    return false if params[:s].nil?

    # Venues near search location using Lon/Lat and Geocoder
    @venues = Venue.near eval(params[:s]["geo"])

    # Search variables
    near_by_venues = @venues.to_a.map(&:id)
    s_activity = params[:s]["activity"].to_i
    s_start_time = (params[:s]["start_time"]).to_datetime
    s_end_time = (params[:s]["end_time"]).to_datetime

    # Filter availabilities based on search criteria
    @search = Availability.includes{facility.activities}
            .where{
              (facility.venue_id.in near_by_venues)
            }
            .where{
              (activities.id == s_activity) &
              (booking_id == nil) &
              (start_time >= s_start_time ) &
              (end_time <= s_end_time )
            }
            .order{start_time.desc}
            .references(:all)
end

This has worked well for me so far but I started thinking other use cases for search, like optional parameters or default parameters if for some reason a location wasn't provided or start/end time wasn't specified. This lead me to articles regarding scoped search where everything is handled at the model level instead of the controller.

I created these scopes in my Availability model:

class Availability < ActiveRecord::Base
  # Associations
  belongs_to :facility
  belongs_to :booking

  #  Scopes for search filters
  scope :close_to, -> (locations) { where{facility.venue_id.in locations} }
  scope :activity,  -> (activity) {where {activities.id == activity}}
  scope :start_date, -> (datetime) {where{created_at >= datetime}}
  scope :end_date, -> (datetime) {where{created_at <= datetime}}
  scope :not_booked, -> {where(booking: nil)}
  scope :order, -> {order{start_time.desc}}
end

I'm having trouble putting it all together. I'm not sure how to include the joins I have includes{facility.activities} in the model so when I do something like Availability.activity(5).not_booked it returns correctly.


Solution

  • You can include the activity within availability in the following fashion:

    scope :activity, -> (activity) { includes({facility: :activities}).where{activities.id == activity}.references(:all) }