0

的Rails 4.1.2活動記錄 - 鏈查詢與OR

數據庫:PostgreSQL的

對於我的疑問之一,我使用來自兩個textacular寶石和活動記錄的方法。我如何可以鏈接下面的一些查詢用「OR」而不是一個「和」:

people = People.where(status: status_approved).fuzzy_search(first_name: "Test").where("last_name LIKE ?", "Test") 

我想鏈中的最後兩個範圍(fuzzy_search和之後的where)用「OR」在一起而不是「AND」。所以我想檢索所有被批准的人(其名字與「Test」或其姓氏包含「Test」的名字相似)。我一直在爲此奮鬥了一段時間,所以任何幫助將不勝感激!

+0

[以下信息可能與您的問題有關](http://stackoverflow.com/q/31096009/3444240) – potashin

回答

0

幾天後,我想出瞭解決方案!下面是我所做的:

這是我用或想鏈一起查詢:

people = People.where(status: status_approved).fuzzy_search(first_name: "Test").where("last_name LIKE ?", "Test") 

至於晃藩建議,當你在控制檯看,這將產生以下SQL:

SELECT "people".*, COALESCE(similarity("people"."first_name", 'test'), 0) AS "rank69146689305952314" 
FROM "people" 
WHERE "people"."status" = 1 AND (("people"."first_name" % 'Test')) AND (last_name LIKE 'Test') ORDER BY "rank69146689305952314" DESC 

然後,我挖掘到了這個文本寶石,並發現了排名是如何產生的。我在textacular.rb文件中找到它,然後使用它製作了SQL查詢。我也換成了「AND」所連接的最後兩個條件與「OR」:

# Generate a random number for the ordering 
rank = rand(100000000000000000).to_s 

# Create the SQL query 
sql_query = "SELECT people.*, COALESCE(similarity(people.first_name, :query), 0)" + 
      " AS rank#{rank} FROM people" + 
      " WHERE (people.status = :status AND" + 
      " ((people.first_name % :query) OR (last_name LIKE :query_like)))" + 
      " ORDER BY rank#{rank} DESC" 

我指的表和字段,因爲這是給我的錯誤信息時,拿出所有引號中的SQL查詢當我把它們留在那裏,即使我用單引號。

然後,我使用find_by_sql方法來檢索數組中的對象標識People。符號(:status:query:query_like)用於防止SQL注入,所以相應地設置其值:

# Retrieve all the IDs of People who are approved and whose first name and last name match the search query. 
# The IDs are sorted in order of most relevant to the search query. 
people_ids = People.find_by_sql([sql_query, query: "Test", query_like: "%Test%", status: 1]).map(&:id) 

我得到在陣列中的ID而不是People對象,因爲find_by_sql返回Array對象和而不是通常返回的CollectionProxy對象,所以我無法在此陣列上使用ActiveRecord查詢方法,如where。使用這些ID,我們可以執行另一個查詢來得到一個CollectionProxy對象。但是,有一個問題:如果我們只是簡單地運行People.where(id: people_ids),那麼ID的順序將不會被保留,所以我們所做的所有相關性排名都沒有。

幸運的是,有一個很好的名爲order_as_specified的寶石,可以讓我們按ID的特定順序檢索所有People對象。雖然寶石會起作用,但我並沒有使用它,而是編寫了一小段代碼來製作可以保持順序的條件。

order_by = people_ids.map { |id| "people.id='#{id}' DESC" }.join(", ") 

如果我們people_ids陣列是[1, 12, 3],它會創建下列順序聲明:

"people.id='1' DESC, people.id='12' DESC, people.id='3' DESC" 

我從this comment,以這種方式書面命令語句將維持秩序的教訓。

現在,剩下的就是從ActiveRecord中檢索People對象,確保指定順序。

people = People.where(id: people_ids).order(order_by) 

而且做到了!我不擔心刪除任何重複的ID,因爲ActiveRecord在您運行where命令時會自動執行該操作。

據我所知,這段代碼不是非常便於攜帶,如果修改了people表的列中的任何一列,它將需要一些更改,但它完美地工作,似乎根據控制檯只執行一個查詢。

0

我挖成fuzzy_search並認爲這將被轉換爲類似:

SELECT "people".*, COALESCE(similarity("people"."first_name", 'test'), 0) AS "rankxxx" 
FROM "people" 
WHERE (("people"."first_name" % 'abc')) 
ORDER BY "rankxxx" DESC 

說,如果你不關心維持秩序,它只會通過WHERE (("people"."first_name" % 'abc'))

過濾結果知道現在你可以簡單的寫有類似功能的查詢:

People.where(status: status_approved) 
     .where('(first_name % :key) OR (last_name LIKE :key)', key: 'Test') 

如果您要訂購,請註明你會ÿ ou喜歡加入2個條件後的訂單。

+0

謝謝你的回答!這非常有幫助!我寧願在查詢中使用散列條件,因爲它們更整潔,更容易修改,但如果必須,我將使用原始SQL。 'fuzzy_search'使用「rankxxx」字段從最相關到​​最不相關的結果排序。但是,每次運行查詢時,「排名」後的「xxx」部分似乎都是唯一編號(可能是時間戳)。你知道我怎麼會通過「rankxxx」命令結果嗎? – Alexander

+0

我寧願說你不能,爲什麼使用隨機數的寶石是爲了防止任何人稍後使用它。你可以使用與他們相同的選擇(說'COALESCE(相似性...)AS <您的變量>'),但不建議。評估這兩種情況時你會遇到麻煩。如果你關心在這種情況下的訂單,最好的選擇是選擇2次activerecord,然後彙總結果,無論你想要什麼。你知道這不是一個性能損失,更容易閱讀和維護。 –

+0

謝謝你的回覆。我不相信執行兩個查詢會有幫助,因爲我不會得到排名順序。第一個查詢的結果將是唯一按排名排序的結果。我複製了生成的查詢並對其進行了修改,以便它可以與'find_by_sql'方法一起使用。我添加了變量轉義以防止SQL注入。我現在唯一的問題是,用原始SQL,我沒有得到獨特的記錄。我試圖在SELECT語句後添加DISTINCT關鍵字無濟於事。 – Alexander