ruby on rails 根據(jù)特定字段對(duì)查詢結(jié)果做自定義排序

前幾天在工作中遇到要根據(jù)多個(gè)國(guó)家的code查詢出對(duì)應(yīng)的國(guó)家并將結(jié)果按code的順序來(lái)排序的需求,但我們?cè)趓ails中做查詢時(shí)通常得到的結(jié)果都是有順序的(下面都會(huì)選擇id來(lái)做自定義排序)。

Role.where(id: [2, 1, 5]).map(&:id) #=> [1, 2, 5]

這里就和我上面所講的需求不一致了,我們希望查詢結(jié)果的順序是[2, 1, 5]。想了一下沒(méi)找到什么優(yōu)雅的解決方式就在dash和某中文網(wǎng)站(可能是這個(gè)需求用中文不太好描述。。)搜索沒(méi)結(jié)果后,轉(zhuǎn)而在google上搜了下,挑了靠前的五到六個(gè)網(wǎng)頁(yè)看了下后,發(fā)現(xiàn)方法基本都是那幾種,這里就做一個(gè)小整合。

一.case...when相關(guān)方法或同類原理

這是看到的最多的方法,有很多變種。這里先做一個(gè)記錄。

  • case...when的思路原型是這樣:
case :b
when :a then 1
when :b then 2
when :c then 3
end 
#=> 2

這里第一種是應(yīng)用sql語(yǔ)句的一種寫法和order by連用,在查詢完后做不規(guī)則排序。sql語(yǔ)句原型是

SELECT * FROM roles WHERE id IN (1, 2, 5)
  ORDER BY CASE id
  WHEN 2 THEN 0
  WHEN 1 THEN 1
  WHEN 5 THEN 2
  ELSE 3 END; 
#=> 2, 1, 5

下面的方法就是將該段sql語(yǔ)句用ruby表示出來(lái)

def find_ordered(ids)
  order_clause = "CASE id "
  ids.each_with_index do |id, index|
     order_clause << sanitize_sql_array(["WHEN ? THEN ? ", id, index])
  end
  order_clause << sanitize_sql_array(["ELSE ? END", ids.length])
  where(id: ids).order(order_clause)
end
Role.find_ordered([2, 1, 5]).map(&:id) 
#=> [2, 1, 5]
  • 第二種是同樣的思想,先查詢?nèi)缓髮⒉樵兘Y(jié)果的id與要求的id順序做比較。
ids = [2, 1, 5]
records = Role.find(ids)
result = ids.collect {|id| records.detect {|x| x.id == id}}.map(&:id)   
#=> [2, 1, 5]

也可以將id存為key,所在的那條記錄存為value(這個(gè)也有多種方法,下面寫較簡(jiǎn)單的兩種),再進(jìn)行比較。

Role.find(ids).index_by(&:id).slice(*ids).values.map(&:id)
#=> [2, 1, 5]
ids = [2, 1, 5]
records = Role.find(ids).group_by(&:id)
result = ids.map {|id| records[id].first}.map(&:id)
#=> [2, 1, 5]

二.mysql的特殊方法

  • mysql有個(gè)特性是可以按字段排序ORDER BY FIELD
    具體語(yǔ)法是:
SELECT id FROM roles WHERE id IN (1, 2, 5)
ORDER BY FIELD(id, 2, 1, 5);
#=> 2, 1, 5

用rails轉(zhuǎn)化后就是

Role.where(id: ids).order("FIELD(id, #{ids.join(',')})").map(&:id)
#=> [2, 1, 5]

三.postgresql的特殊方法

  • 那在pg中就無(wú)法使用mysql的field特性了,但是pg也有自己的方式來(lái)自定義排序。
    position(substring in string)可以返回指定子字符串的位置
    eg. position('om' in 'Thomas') #=> 3
ids = [2, 1, 5]
Role.where(id: ids).order("position(id::text in '#{ids.join(',')}')").map(&:id)
#=> [2, 1, 5]

ps: 通過(guò)這樣的方式做自定義排序也存在問(wèn)題,如果ids = [12, 2, 1, 5], 那么結(jié)果就會(huì)出現(xiàn)

User.where(id: ids).order("position(id::text in '#{ids.join(',')}')").map(&:id)
#=> [1, 12, 2, 5]
  • 在postgresql 9.4版本開(kāi)始,我們可以利用WITH ORDINALITY來(lái)設(shè)置返回值。那這里我們配合unnest(將一個(gè)數(shù)組擴(kuò)展為多行記錄)和JOIN使用,可以通過(guò)先新建行記錄確定排序順序然后通過(guò)表連接查詢出對(duì)應(yīng)順序的記錄
ids = [12, 2, 1, 5]
User.joins("JOIN unnest(array#{ids}) WITH ORDINALITY t(id, ord) USING (id) ORDER BY t.ord").map(&:id)
#=> [12, 2, 1, 5]

四.gem包:order_as_specified

  • 通過(guò)order_as_specified也可以根據(jù)字段自定義排序
    github地址
    簡(jiǎn)單介紹:在要做查詢的model中添加extend OrderAsSpecified,
Role.where(id: [2, 1, 5]).order_as_specified(id: [2, 1, 5]).map(&:id) #=> [2, 1, 5]

也可以在此基礎(chǔ)上嵌套排序,具體可以直接看該gem包。

五.如果你沒(méi)很多數(shù)據(jù)要查那最直接的方法。。

[2, 1, 5].map{|id| Role.find(id)}.map(&:id) #=> [2, 1, 5]

***
### 總結(jié)
從以上幾種方法里可以看出,當(dāng)你想要根據(jù)特定順序查詢數(shù)據(jù)時(shí),除了通過(guò)ruby的方法來(lái)進(jìn)行排序外,還可以通過(guò)各個(gè)數(shù)據(jù)庫(kù)的特性來(lái)完成排序,當(dāng)然還需要根據(jù)實(shí)際情況再做決定,我這里因?yàn)槔^承關(guān)系只能在pg的方法的基礎(chǔ)上再做修改了。。

scope :get_and_order_supplier_cal_popular_country, -> (codes) {
sql = sanitize_sql_array(
["JOIN unnest(array[?]) WITH ORDINALITY t(code, ord) USING (code)
WHERE type = 'Country' ORDER BY t.ord", codes] )
joins(sql)
}

最后,可能有些特殊情況沒(méi)有考慮進(jìn)去,歡迎討論。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,626評(píng)論 18 399
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,995評(píng)論 0 11
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽(yáng)光小鎮(zhèn)少爺閱讀 3,951評(píng)論 0 8
  • 落葉惹秋風(fēng) 秋雨吹臉龐 九月秋夜涼 晨曦在他鄉(xiāng)
    晨曦山雞閱讀 222評(píng)論 2 0

友情鏈接更多精彩內(nèi)容