On Monday, June 15, 2015 at 11:27:01 AM UTC-7, Andrew Burleson wrote:
>
> I've got two models like this:
>
> class Order < Sequel::Model
>   has_many :order_states
> end
>
>
> class OrderState < Sequel::Model
>
>   set_dataset dataset.order(:created_at).reverse
>   many_to_one :order
> end
>
>
> An Order can be `active` or `inactive`, users can toggle this. I've added 
> an after save hook that creates a new order state if the state was just 
> changed, like this:
>
> def after_save
>   super
>   add_order_state(state: state) if order_states.last.try(:state) != state
> end
>
> This all works great in terms of recording the state changes on the order 
> over time.
>

If you can, I'd recommend using a database trigger for this, it's a more 
robust approach.
 

> I need to write a dataset_module method `active_at(time)` that can tell me 
> if the service order was active at a particular time. The part that is 
> tripping me up is, I need to query against the joined `order_states` table, 
> then for each `order_state` check the last order state prior to the given 
> time and see then select the order if that order_state was active.
>
> If I was looking at a single record I might do something like:
>
> order.order_states.order(:created_at).reverse.where{ created_at < time }.
> last.state == "active"
>
> I haven't been able to think of how to write that as a dataset query 
> method though. How would you do that?
>

This is more of an SQL question than a Sequel question, unless you know the 
SQL for the query you want and just aren't sure how to express it in Sequel 
syntax.  If that's the case, it is best to post the SQL you want to use.

Without actually testing it, I'm guessing something like:

Order.def_dataset_method(:active_at) do |time|
  ds = OrderState.where(OrderState.
    from(:order_states___os).
    select(:id).
    where{created_at < time}.
    where(:os__order_id=>:order_states__order_id).
    reverse(:created_at).
    limit(1)=>:id)
  where(['active', :id]=>ds.select(:state, :order_id))
end

Which will be compiled into something like:

SELECT * FROM orders
WHERE (('active', id) IN (
  SELECT state, order_id IN order_states
  WHERE (
    SELECT id FROM order_states AS os
    WHERE created_at < ? AND os.order_id = order_states.order_id
    ORDER BY created_at DESC
    LIMIT 1
  ) = id)
)

Basically, you are looking for orders where the last entry before the given 
time in the order_states table had a state of active.

I wouldn't expect this query to perform well on a large dataset. To 
optimize it, you'd want to denormalize and add a inactive_at column to the 
orders table, containing the timestamp when the order was no longer active 
(also an active_at column if active is not the initial state, for when the 
order became active).  With an index on that column, it would be fairly 
fast to filter by whether the order was active at a given time.

Thanks,
Jeremy

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sequel-talk.
For more options, visit https://groups.google.com/d/optout.

Reply via email to