Rails 3 Active Record relation order: Use hash instead of string

半世苍凉 提交于 2019-12-06 04:17:35

问题


To sort a relation in Rails 3, we have to do this:

User.where(:activated => true).order('id ASC')

But I think this:

User.where(:activated => true).order(:id => :asc)

would make better sense because the way the field name be escaped should depend on the adapter (SqlLite vs Mysql vs PostgreSQL), right?

Is there something similar to that?


回答1:


As far as I know there's no option for this syntax built into ActiveRecord, but it shouldn't be hard for you to add one. I found the order method defined in lib/active_record/relation/query_methods.rb. Theoretically, you should be able to do something like this:

module ActiveRecord
  module QueryMethods
    def order(*args)
      args.map! do |arg|
        if arg.is_a? Hash
          # Format a string out of the hash that matches the original AR style
          stringed_arg
        else
          arg
        end
      end

      super
    end
  end
end



回答2:


I think the key problem is: ActiveRecord API is not aware of ordering semantic. It just accepts a string and bypasses to the underlying database. Fortunately, Sqlite, MySQL and PostgreSQL has no difference in order syntax.

I don't think ActiveRecord can do this abstraction well, and it doesn't need to do it. It works well with relation databases, but is hard to integrate with NoSQL, eg. MongoDB.

DataMapper, another famous Ruby ORM, did better abstraction. Take a look at its query syntax:

@zoos_by_tiger_count = Zoo.all(:order => [ :tiger_count.desc ])

The API is aware of the ordering semantic. By default, DataMapper will generate SQL order statement:

https://github.com/datamapper/dm-do-adapter/blob/master/lib/dm-do-adapter/adapter.rb#L626-634

def order_statement(order, qualify)
  statements = order.map do |direction|
    statement = property_to_column_name(direction.target, qualify)
    statement << ' DESC' if direction.operator == :desc
    statement
  end

  statements.join(', ')
end

However, it's possible to override at DB adapter layer:

https://github.com/solnic/dm-mongo-adapter/blob/master/lib/dm-mongo-adapter/query.rb#L260-264

def sort_statement(conditions)
  conditions.inject([]) do |sort_arr, condition|
    sort_arr << [condition.target.field, condition.operator == :asc ? 'ascending' : 'descending']
  end
end

TL;DR:

  • You don't need worry about syntax problem if you are only using SqlLite, Mysql and PostgreSQL.
  • For better abstraction, you can try DataMapper.



回答3:


For this particular case, you could drop the 'ASC' bit as ordering in all database is implicitly ascending

Foo.order(:bar)

I am aware that this doesn't cover the case where you'd want to do order by bar desc but actually for order by this doesn't matter much unless you are using functions for the order by clause in which case maybe something like squeel would help



来源:https://stackoverflow.com/questions/8138008/rails-3-active-record-relation-order-use-hash-instead-of-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!