2012-10-10 26 views
1

好的。所以我有一個Vehicle模型和Schedule模型。車輛has_many :schedules(時間表belongs_to :vehicle)。時間表具有以下結構:通過兩個關聯模型的導軌複雜搜索條件

id | (date)from_date | (date)to_date | (bool)available | (int)vehicle_id 

用戶指定天範圍:from_dateto_date和我需要返回車輛,其具有所有天可用

實施例(利用一種車輛爲了簡化):

[ 
    (id: 1, from_date: "10/10/2012", to_date: "13/10/2012, available: true, vehicle_id: 1), 
    (id: 2, from_date: "14/10/2012", to_date: "15/10/2012", available: true, vehicle_id: 1), 
    (id: 3, from_date: "16/10/2012", to_date: "20/10/2012", available: false, vehicle_id: 1) 
] 

-Test查詢:

1) from_date: "12/10/2012", to_date: "15/10/2012",=>結果:車輛

2)from_date: "11/10/2012", to_date: "17/10/2012",=>結果:[](由於16.10和17.10是可用)

3)from_date: "01/11/2012", to_date: "30/11/2012",=>結果:車輛(如果不是一個單一的日期範圍內的日期在Schedule列表中 - 車輛仍然返回(視爲可用)。所以唯一的例外 - 如果從該範圍內的任何一天碰巧是not_available - 車輛應該被拒絕)

這是如何實現的?對不起,如果它太複雜。

+0

是對象模型的實際名稱?它代表什麼? –

+0

不,不是。爲了簡單起見,我已經命名了它(因爲它實際上並不重要)。在我的具體情況下,它代表Vehicle對象(它有一個租借計劃) – Dmitri

+1

我將它重命名爲Vehicle - Object是一個內置的Ruby類,因此它使它更容易混淆,而不是更少! –

回答

4

這裏有一個簡單的方法與範圍做到這一點:

# schedule.rb 

scope :unavailable, where(available: false) 

def self.between(from_date, to_date) 
    where "from_date <= ? AND to_date >= ?", to_date, from_date 
end 


# vehicle.rb 

def self.available_between(from_date, to_date) 
    id_not_in Schedule.unavailable.between(from_date, to_date).map(&:vehicle_id) 
end 

def self.id_not_in(ids) 
    return all if ids.empty? 
    where "id NOT IN (?)", ids 
end 

然後,你可以調用Vehicle.available_between "2012-10-12", "2012-10-15"

這依賴於兩個意見:

  • 可用無關緊要的時間表。只需找到標記爲不可用的時間表,然後查找所有與這些時間表相關的車輛而不是

  • 可以測試如果兩個日期範圍A和B的重疊通過檢查兩個以下爲真

    • A的開始日期是之前乙
    • 結束日期的結束日期A的是 B的開始日期

NB我用紅寶石1.9 SYNT斧頭在unavailable範圍內,如果你在1。8您需要將available: false更改爲:available => false

+0

哇。非常整潔!我一定會更精確地看看它!謝謝! – Dmitri

+0

嗯。似乎沒有工作。 id NOT IN(NULL) - 所以它總是返回空數組:(){ – Dmitri

+0

那麼,將它分解成範圍的巧妙之處在於你可以單獨測試每個部分,'Schedule.unavailable'是否返回所有不可用的範圍? Schedule.between(「2012-10-12」,「2012-10-15」)'返回10月12日至15日之間的所有日程安排嗎?日期列是否實際存儲爲日期而不僅僅是字符串(我問你是因爲當數據庫通常使用「yyyy-mm-dd」時,寫入「dd/mm/yyyy」)? –

1
# for each vehicle find all the rows where vehicle is available for given dates 
available_schedules = Schedule.where("vehicle_id = ? and from_date <= ? and to_date >= ? and available = ?",vehicle_id,from_date,to_date,true) 

# start by assuming that schedule hasn't been found 
schedule_found = false 

# for each date in you range check all the available schedules if there is one matching your date. 
# If you find a single schedule set schedule_found flag to true and break from loop else loop through all the available schedules. 
# Now if schedule_found is true then make it false (to be used by next date) else return [] as per your requirement. 
(from_date..to_date).each do |dt| 
    available_schedules.each do |schedule| 
      if schedule.from_date <= dt and schedule.to_date >= dt 
        schedule_found = true 
        break 
      end 
    end 

    if schedule_found 
      schedule_found = false 
    else 
      return [] 
    end 
end 

# If your code manages to reach here then it means all the dates in range have an available schedule, so just return Vehicle object 
return Vehicle.find(vehicle_id) 
+0

哇!非常感謝您的幫助!我會看看它是否有效! – Dmitri

0

嘗試尋找不符合的是可用性的範圍之外的條件的預約組..如果你不能找到一個..這是你的贏家

警告:未測試

v = Vehicle.join(:schedules).where('vehicles.id not in (select vehicle_id from schedules where schedules.from_date > :date and schedules.to_date < :date)', :date => date)

+0

謝謝,但似乎不起作用=) – Dmitri