简体   繁体   中英

Search multiple tables and add results to array

As an exercise I was given a project with three tables Booking, Room and Host. The models are as follows:

class Booking < ActiveRecord::Base
  belongs_to :room
end

class Room < ActiveRecord::Base
  belongs_to :host
  has_many :bookings
end

class Host < ActiveRecord::Base
  has_many :rooms
end

The database schema looks like this:

ActiveRecord::Schema.define(version: 20150305133000) do

  create_table "bookings", force: true do |t|
    t.integer  "room_id",          null: false
    t.date     "start_date",       null: false
    t.date     "end_date",         null: false
    t.integer  "number_of_guests", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "hosts", force: true do |t|
    t.string   "name"
    t.string   "address"
    t.string   "picture_url"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "rooms", force: true do |t|
    t.integer  "host_id",    null: false
    t.integer  "capacity",   null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

I need to implement a search feature that returns all hosts with rooms available between dates specified by the search form. To find hosts that are available for the required dates I need to cross reference with the Bookings table.

I skimmed through the ruby guides and also had a look at this question but I'm having trouble implementing what I've read.

In the rails console I've been playing around with varying queries to try and get the results I need.

If I let @bookings = Booking.includes(:room).all and @hosts = [] and if start_date , end_date and guests are parameters returned from he search then running the following query:

@bookings.each { |b| @hosts.push(b.room.host) if (b.start_date < start_date && b.end_date > end_date && (b.room.capacity - b.number_of_guests >= guests)) }

produces an empty @hosts . I then tried

 Booking.joins(:room).where("start_date < ? AND end_date > ? and capacity-number_of_guests >= ?", start_date, end_date, guests)

But that also returned and empty response so then I tried to do this in baby steps by finding all the bookings for the requested dates, filtering out the rooms that didn't have any space and then gathering together all the hosts of the rooms that were left. I got as far as:

@bookings = Booking.where("start_date = ? AND end_date >= ?",
  Date.parse(params[:start_date]),
  Date.parse(params[:end_date])).paginate(:page => params[:page], :per_page => 10)

@bookings.reject{ |booking| (booking.room.capacity - booking.number_of_guests) == 0 }

However, this is still returning empty rooms. I've been at this for over a day now so if anyone can point me in the right direction towards getting this solved I'd appreciate it!

DEBUGGING ATTEMPTS

Following denis-bu's suggestions I've updated my first query to read (leaving the room capacity alone for the time being):

@bookings.each { |b| @hosts.push(b.room.host) if ((b.start_date > start_date &&
  b.start_date < end_date) || (b.end_date > start_date && b.end_date < end_date)) }

I've run this in the rails console and when I then try @hosts.count it returns 0 - so that implies the query isn't fetching any results. I'd expect to see an array of Host records.

With my Where Exists gem and PostgreSQL:

Room.includes(:host).where_not_exists(:bookings,
 [
   "(?, ?) OVERLAPS (start_date, end_date)",
   params[:start_date],
   params[:end_date]
 ]
)

Without the gem:

# Using PostgreSQL OVERLAPS (http://www.postgresql.org/docs/9.0/static/functions-datetime.html)

overlaps = Booking.where(where("(?, ?) OVERLAPS (start_date, end_date)", start_date, end_date))

# With MySQL or SQLite you should check for boundaries manually

overlaps = Booking.where("? < end_date", start_date).where("? > start_date", end_date)

Room.includes(:host).where("NOT EXISTS (#{
  overlaps.where("bookings.room_id = rooms.id").to_sql
})")

It's not really enough details of what you're trying to achieve, but I'd make a guess that prolem is with your condition: start_date < ? AND end_date > ? start_date < ? AND end_date > ? - checks that query dates within booking interval, instead you should check that booking overlaps with your query dates: ((b.start_date > START_DATE and b.start_date < END_DATE) OR (b.end_date > START_DATE and b.end_date < END) .

BUT, I'd say that is not all you should do. You should also find rooms without bookings for query dates and then union those sets.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM