在Rails中做Something.find(array_of_ids)
时,生成的数组的顺序不取决于顺序array_of_ids
。ActiveRecord.find(array_of_ids),保留顺序
有什么办法可以查找和保存订单吗?
自动柜员机手动排序基于ID的顺序的记录,但这是一种跛脚。
UPD:如果可以使用:order
param和某种SQL子句指定顺序,那么该怎么做?
在Rails中做Something.find(array_of_ids)
时,生成的数组的顺序不取决于顺序array_of_ids
。ActiveRecord.find(array_of_ids),保留顺序
有什么办法可以查找和保存订单吗?
自动柜员机手动排序基于ID的顺序的记录,但这是一种跛脚。
UPD:如果可以使用:order
param和某种SQL子句指定顺序,那么该怎么做?
答案是MySQL只
有MySQL中的函数调用FIELD()
这里是你如何能在.find()使用它:
>> ids = [100, 1, 6]
=> [100, 1, 6]
>> WordDocument.find(ids).collect(&:id)
=> [1, 6, 100]
>> WordDocument.find(ids, :order => "field(id, #{ids.join(',')})")
=> [100, 1, 6]
谢谢!现在开始进行基准测试。 – 2009-11-06 12:35:22
你碰巧知道在Postgres中相当于'FIELDS()'吗? – 2012-06-26 06:27:24
我写了一个plpgsql函数在postgres中执行此操作 - http://omarqureshi.net/articles/2010-6-10-find-in-set-for-postgresql – 2012-06-29 16:58:50
find((order =>'...')中有一个订单子句,它在获取记录时执行此操作。 你也可以从这里获得帮助。
好的。问题已更新。 – 2009-11-05 14:01:07
引擎盖下,find
与IDS的阵列将产生具有WHERE id IN...
子句,这应该是比通过IDS循环更有效的一个SELECT
。
因此,请求满足一次到数据库,但SELECT
s没有ORDER BY
子句是未排序的。 ActiveRecord的明白这一点,所以我们扩大find
如下:
Something.find(array_of_ids, :order => 'id')
如果IDS的数组中的顺序是任意的,显著(也就是说你想行的顺序返回不论ID匹配的序列的排列包含在其中),那么我认为你会是最好的服务器,通过代码后处理结果 - 你可以建立一个:order
条款,但这将是非常复杂,并不意图透露。
请注意,选项散列已被弃用。 (第二个参数,在这个例子中':order => id') – ocodo 2012-07-25 02:26:11
在SQL这将适用于所有情况不幸的是没有可能的,你要么需要编写Ruby的每个记录或阶单的发现,虽然有可能是一个方法,使工作中使用的专有技术:
第一个例子:
sorted = arr.inject([]){|res, val| res << Model.find(val)}
非常低效
第二个例子:
unsorted = Model.find(arr)
sorted = arr.inject([]){|res, val| res << unsorted.detect {|u| u.id == val}}
是的,第二个例子就是我现在正在做的事情。 – 2009-11-06 12:34:18
虽然效率不高,但我同意这种解决方法是不依赖于数据库的,并且如果行数很少,则可以接受。 – 2012-06-26 06:26:49
'map'而不是'reduce'(inject)会更具可读性。 – 2013-02-27 06:30:17
奇怪的是,没有有人建议这样的事情:
index = Something.find(array_of_ids).group_by(&:id)
array_of_ids.map { |i| index[i].first }
效率如它得到除了让SQL后端去做。
编辑:为了提高我自己的答案,你也可以做这样的:
Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
#index_by
和#slice
是非常方便的添加在ActiveSupport数组和哈希分别。
所以你的编辑似乎工作,但它让我紧张的哈哈键顺序不能保证是吗?所以当你调用slice并将hash返回到“重新排序”时,它实际上取决于hash键返回值的顺序,即键被添加。这感觉像取决于可能改变的实现细节。 – Jon 2013-09-03 17:45:44
@Jon,在Ruby 1.9和其他所有试图遵循它的实现中都保证了顺序。对于1.8,Rails(ActiveSupport)修补了Hash类,使其具有相同的行为方式,因此如果您使用Rails,则应该没问题。 – Gunchars 2013-09-03 21:39:01
感谢您的澄清,只是在文档中发现。 – Jon 2013-09-04 04:46:36
@Gunchars的答案很棒,但它在Rails 2.3中并不适用,因为Hash类没有排序。简单的解决方法是延长枚举类index_by
使用OrderedHash类:
module Enumerable
def index_by_with_ordered_hash
inject(ActiveSupport::OrderedHash.new) do |accum, elem|
accum[yield(elem)] = elem
accum
end
end
alias_method_chain :index_by, :ordered_hash
end
现在@Gunchars'的做法将工作
Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
奖金
module ActiveRecord
class Base
def self.find_with_relevance(array_of_ids)
array_of_ids = Array(array_of_ids) unless array_of_ids.is_a?(Array)
self.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
end
end
end
然后
Something.find_with_relevance(array_of_ids)
由于his answer中陈述的Mike Woodhouse,这是因为在引擎盖下,Rails使用SQL查询与WHERE id IN... clause
来检索一个查询中的所有记录。这比单独检索每个ID更快,但是如您注意到的,它不会保留您正在检索的记录的顺序。
为了解决这个问题,您可以根据查找记录时使用的原始ID列表在应用程序级别对记录进行排序。
基于很多优秀的答案Sort an array according to the elements of another array,我建议以下解决方案:
Something.find(array_of_ids).sort_by{|thing| array_of_ids.index thing.id}
或者,如果你需要的东西更快一点(但可以说稍差可读的),你可以这样做:
Something.find(array_of_ids).index_by(&:id).values_at(*array_of_ids)
第二个解决方案(使用index_by)似乎失败了,产生了所有零结果。 – 2016-12-13 20:36:39
正如我回答here,我刚刚发布了一个宝石(order_as_specified),它允许你做本地SQL排序是这样的:
Something.find(array_of_ids).order_as_specified(id: array_of_ids)
就我所能测试的那样,它在所有的RDBMS中都能正常工作,并且它返回一个可链接的ActiveRecord关系。
伙计,你真棒。谢谢! – swrobel 2016-01-21 23:18:39
这似乎为的PostgreSQL(source)工作 - 并返回一个ActiveRecord关系
class Something < ActiveRecrd::Base
scope :for_ids_with_order, ->(ids) {
order = sanitize_sql_array(
["position((',' || id::text || ',') in ?)", ids.join(',') + ',']
)
where(:id => ids).order(order)
}
end
# usage:
Something.for_ids_with_order([1, 3, 2])
可以扩展为其它列,以及,例如对于name
列,请使用position(name::text in ?)
...
你是我这周的英雄。谢谢! – ntdb 2016-02-29 23:42:37
请注意,这只适用于微不足道的情况,您最终会遇到一种情况,即您的Id包含在列表中的其他ID中(例如,它会在11中找到1)。解决方法之一是将逗号添加到位置检查中,然后向连接添加最终逗号,如下所示: order = sanitize_sql_array( [“position(','|| clients.id :: text | |','in?)“,ids.join(',')+','] ) – IrishDubGuy 2016-05-18 14:29:51
好点,@IrishDubGuy!我会根据您的建议更新我的答案。谢谢! – gingerlime 2016-05-19 19:59:49
可能的重复[Clean方式按指定顺序查找ActiveRecord对象](http://stackoverflow.com/questions/801824/clean-way-to-find -active-order-objects-by-id-in-order-specified) – 2013-03-22 06:56:01