Запрос Rails 4, ограниченный условием from, имеет много сквозных атрибутов

#ruby-on-rails #postgresql #has-many-through

#ruby-on-rails #postgresql #имеет много сквозных

Вопрос:

Вопрос в следующем: «Учитывая компанию, как я должен построить запрос, который возвращает все события, созданные всеми сотрудниками в течение их соответствующих периодов занятости

Пример:

 @company = Company.create

# Welcome John
@john = User.create
@event_a = @john.events.create date: Date.new(2013,6,1)
@event_b = @john.events.create date: Date.new(2014,1,1)
@company.employments.create user:@john, since: Date.new(2013,12,20)

# Welcome Jack
@jack = User.create
@event_c = @jack.events.create date: Date.new(2012,1,1)
@event_d = @jack.events.create date: Date.new(2013,1,1)
@company.employments.create user:@jack, 
    since: Date.new(2011,12,20), till:  Date.new(2012,12,31)

@company.events
=> [@event_b, @event_c]
# @event_a is not returned because it was created prior to John's hiring
# @event_d is not returned because it was created after Jack's departure
  

Я придумал решение, но я хотел бы знать, есть ли какие-либо способы его улучшить.

 class Event
  belongs_to :user

  # attributes
  # date: datetime
  # …
end

class Employment
  belongs_to :user
  belongs_to :event

  # attributes
  # since: date
  # till: date
  # …
end

class Company
  has_many :employments

  def events
    Event.where employments.map do |e|
      if e.since amp;amp; e.till
        "(user_id = '#{e.user_id}' AND date BETWEEN '#{e.since}' AND '#{e.till}')"
      elsif !e.since.nil?
        "(user_id = '#{e.user_id}' AND date > '#{e.since}')"
      else
        "(user_id = '#{e.user_id}')"
      end
    end.join(' OR ')
  end
end
  

Вы видите какой-либо другой способ сделать это?

Ответ №1:

События.объединения (user: :employements).где (company_id: id) Я полагаю, вы могли бы использовать что-то вроде этого:

 def events
    Event.where employments.map do |e|
        user=User.find(e.user_id)
        since = e.since
        till  = e.till || Date.parse('3100-12-31') # a day in the distant future if nil
        user.events.where('(date is null) or (date > ? and date < ?)', since, till)
    end
end
  

2-я попытка, после использования соответствующих объединений для подключения модели событий и занятости, вы можете использовать последнее where предложение для реализации вашего фильтра.

Полный метод, после того как вы добавили правильный набор объединений и фильтров (спасибо), выглядит следующим образом.

 def events
    Events.joins(user: :employments).where(company_id: id). # <-This comes from the author, not me.
           where('employments.since is null OR 
                 (employments.since < events.date AND employments.till is null) OR
                 (employments.since < events.date AND employments.till > events.date)')

end
  

Комментарии:

1. Это возвращает события для данного пользователя. Я хочу, чтобы события для всех пользователей компании, пока они были сотрудниками.

2. Вы должны поместить это внутри блока карты… Я поставил для вас букву «е»… Позвольте мне перестроить ответ

3. О, извините, вам нужно что-то другое… Если это все равно, скажите мне, что я могу удалить свой ответ.

4. Спасибо, что нашли время для ответа, я ценю ваши усилия. Я вижу 4 проблемы с вашим ответом. a) это не работает 🙂 map вернет массив ActiveRecord_AssociationRelation , но он может работать с незначительными изменениями: ‘Событие. найти … с тех пор, пока). pluck(:id)’ б) он загружает все в память, в) он запрашивает базу данных несколько раз (в зависимости от количества пользователей), чего можно было бы избежать, используя employments.includes(:user) и используя e.user вместо user=User.find , г) яне люблю полагаться на случайную дату, однако маловероятно, что кодовая база проживет тысячу лет 🙂

5. @user229688, я добавил 2-ю альтернативу с двумя SQL-запросами, загружающими только события.*… Взгляните на это..

Ответ №2:

как я могу запросить все события, созданные всеми сотрудниками во время их работы в данной компании

Возможно, вы захотите использовать an ActiveRecord Association Extension , который в основном добавляет новый метод к вашей ассоциации, позволяя вам определять фильтрацию или некоторые другие спецификации:

 #app/models/company.rb
Class Company < ActiveRecord::Base
   has_many :employments
   has_many :events do
       def by_date(since, till)
           ... logic here
       end
   end
end
  

Это позволит вам вызвать:

 #app/controllers/events_controller.rb
Class EventsController < ApplicationController
    def action
        company = Company.find params[:id]
        @events = company.events.by_date("01-01-2014", "04-01-2014")
    end
end
  

Если вы дадите мне знать в комментариях, я попробую заполнить метод для вас

Комментарии:

1. Привет, спасибо за ваш ответ. Это не совсем то, что я пытаюсь сделать. Даты начала / окончания варьируются от одного сотрудника к другому, я добавлю пример.