搜索多个表并将结果添加到数组

时间:2015-09-03 10:28:38

标签: ruby-on-rails activerecord

作为一项练习,我获得了一个项目,包括三张桌子预订,房间和主人。模型如下:

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

数据库架构如下所示:

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

我需要实现一个搜索功能,该功能可以在搜索表单指定的日期之间返回所有可用房间的主机。要查找可用于所需日期的主机,我需要与预订表交叉引用。

我浏览了the ruby guides并查看了this question,但我无法实现我所阅读的内容。

在rails控制台中,我一直在玩各种查询,试图获得我需要的结果。

如果我允许@bookings = Booking.includes(:room).all@hosts = []以及start_date,则end_dateguests是从搜索返回的参数,然后运行以下查询:

@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)) }

生成一个空的@hosts。然后我尝试了

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

但是那也回来了并且空洞的反应所以我试图通过找到所请求日期的所有预订,过滤掉没有任何空间的房间然后聚集所有主机离开的房间。我得到了:

@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 }

然而,这仍然是空房间。我已经在这一天工作了一天以上,所以如果有人能指出我正确的方向来解决这个问题我会很感激!

调试ATTEMPTS

遵循denis-bu的建议我已经更新了我的第一个查询阅读(暂时保留房间容量):

@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)) }

我在rails控制台中运行它,然后当我尝试@hosts.count时它返回0 - 这意味着查询不会获取任何结果。我期待看到一系列Host条记录。

2 个答案:

答案 0 :(得分:1)

使用我的Where Exists gem和PostgreSQL:

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

没有宝石:

# 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
})")

答案 1 :(得分:0)

关于您尝试实现的目标的详细程度不够,但我猜测问题与您的情况有关:start_date < ? AND end_date > ? - 检查查询日期预订间隔,您应该检查预订是否与您的查询日期重叠:((b.start_date > START_DATE and b.start_date < END_DATE) OR (b.end_date > START_DATE and b.end_date < END)

但是,我说这不是你应该做的全部。您还应该找到没有预订查询日期的房间,然后将这些集合结合起来。