[英]Weighted + ordered tag search using Postgres
在對數萬個音頻文件進行 AI 文件分析后,我最終在 Postgres 數據庫中得到了這種數據結構:
id | name | tag_1 | tag_2 | tag_3 | tag_4 | tag_5
1 | first song | rock | pop | 80s | female singer | classic rock
2 | second song | pop | rock | jazz | electronic | new wave
3 | third song | rock | funk | rnb | 80s | rnb
標簽位置非常重要:越“靠左”,它在歌曲中就越突出。 標簽的數量也是有限的(50 個標簽),AI 總是為每首歌曲返回其中的 5 個標簽,沒有預期的 null 個值。
另一方面,這是我必須查詢的:
{"rock" => 15, "pop" => 10, "soul" => 3}
鍵是標簽名稱,值是任意權重。 條目數可以是 1 到 50 之間的隨機數。根據示例數據集,在這種情況下它應該返回 [1, 3, 2]
如果使用原始連接字符串可以更容易地實現,我也願意進行數據重組,但是......使用 Postgres(tsvectors?)是否可行,或者我真的必須為此使用 Elasticsearch 之類的東西嗎?
經過大量的試驗和錯誤,這就是我最終得到的結果,只使用 Postgres:
id | bpm | tag_1 | tag_2 | tag_3 | tag_4 | tag_5
1 | 114 | 1 | 2 | 3 | 4 | 5
2 | 102 | 2 | 1 | 6 | 7 | 8
3 | 110 | 1 | 9 | 10 | 3 | 12
requests = [
"bpm BETWEEN 110 AND 124
AND tag_1 = 1
AND tag_2 = 2
AND tag_3 = 3
AND tag_4 = 4
AND tag_5 = 5",
"bpm BETWEEN 110 AND 124
AND tag_1 = 1
AND tag_2 = 2
AND tag_3 = 3
AND tag_4 = 4
AND tag_5 IN (1, 3, 5)",
"bpm BETWEEN 110 AND 124
AND tag_1 = 1
AND tag_2 = 2
AND tag_3 = 3
AND tag_4 IN (1, 3, 5),
AND tag_5 IN (1, 3, 5)",
....
]
# Ruby / ActiveRecord example
track_ids = []
requests.each do |request|
track_ids += Track.where([
"(#{request})
AND tracks.id NOT IN ?", track_ids
]).pluck(:id)
break if track_ids.length > 200
end
... 完成了,我所有的歌曲都按相似度排序,最接近的匹配在頂部,更多的在底部。 他們得到的越近似,因為一切都是關於整數的,所以它非常快(在 100K 行數據集上足夠快)。 output 看起來像純粹的魔法:加分。 它仍然很容易被整個團隊調整和維護。
我知道這很粗糙,所以我願意接受任何更有效的方法來做同樣的事情,即使堆棧中需要其他東西(ES?),但到目前為止:這是一個簡單的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.