我有这个" heavy_rotation"过滤我正在努力。基本上它根据某些参数(listens_count,staff_pick,purchase_count,仅举几例)的混合物从我们的数据库中抓取曲目。
对filter_tracks控制器操作发出xhr请求。在那里,我有一个标志,以检查它是否" heavy_rotation"。我可能会把这个移到模型上(因为这个控制器变胖了)...无论如何,我怎样才能确保(以有效的方式)不让它拉相同的记录?我考虑过偏移量,但是我必须跟踪每个查询的偏移量。或者可以存储track.id以进行每个查询的比较?有任何想法吗?我很难想出一个优雅的方法来做这件事。
也许应该注意,通过Javascript设置限制为14,并且当用户点击"查看更多"为了分页,它向filter_tracks发送另一个请求。
任何帮助表示赞赏!谢谢!
def filter_tracks
params[:limit] ||= 50
params[:offset] ||= 0
params[:order] ||= 'heavy_rotation'
# heavy rotation filter flag
heavy_rotation ||= (params[:order] == 'heavy_rotation')
@result_offset = params[:offset]
@tracks = Track.ready.with_artist
params[:order] = "tracks.#{params[:order]}" unless heavy_rotation
if params[:order]
order = params[:order]
order.match(/artist.*/){|m|
params[:order] = params[:order].sub /tracks\./, ''
}
order.match(/title.*/){|m|
params[:order] = params[:order].sub /tracks.(title)(.*)/i, 'LOWER(\1)\2'
}
end
searched = params[:q] && params[:q][:search].present?
@tracks = parse_params(params[:q], @tracks)
@tracks = @tracks.offset(params[:offset])
@result_count = @tracks.count
@tracks = @tracks.order(params[:order], 'tracks.updated_at DESC').limit(params[:limit]) unless heavy_rotation
# structure heavy rotation results
if heavy_rotation
puts "*" * 300
week_ago = Time.now - 7.days
two_weeks_ago = Time.now - 14.days
three_months_ago = Time.now - 3.months
# mix in top licensed tracks within last 3 months
t = Track.top_licensed
tracks_top_licensed = t.where(
"tracks.updated_at >= :top",
top: three_months_ago).limit(5)
# mix top listened to tracks within last two weeks
tracks_top_listens = @tracks.order('tracks.listens_count DESC').where(
"tracks.updated_at >= :top",
top: two_weeks_ago)
.limit(3)
# mix top downloaded tracks within last two weeks
tracks_top_downloaded = @tracks.order("tracks.downloads_count DESC").where(
"tracks.updated_at >= :top",
top: two_weeks_ago)
.limit(2)
# mix in 25% of staff picks added within 3 months
tracks_staff_picks = Track.ready.staff_picks.
includes(:artist).order("tracks.created_at DESC").where(
"tracks.updated_at >= :top",
top: three_months_ago)
.limit(4)
@tracks = tracks_top_licensed + tracks_top_listens + tracks_top_downloaded + tracks_staff_picks
end
render partial: "shared/results"
end
答案 0 :(得分:1)
我认为寻求优雅"解决方案将产生许多不同的意见,所以我将提供一种方法和我的推理。在我的设计决策中,我觉得在这种情况下,通过过滤返回的记录对象而不是试图将查询限制为仅产生唯一结果,在查询交叉点上强制执行唯一性是最佳和优雅的。至于获取分页的连续结果,另一方面,我会存储每个查询的偏移量,并使用实例变量或会话作为下一个查询的起点,具体取决于数据需要如何保留。
这里是我的代码重构版本的gist
,其中包含一个解决方案,并解释了我选择使用某些逻辑或数据结构的原因:https://gist.github.com/femmestem/2b539abe92e9813c02da
#filter_tracks
包含一个哈希映射@tracks_offset
,其他方法可以访问和更新;每个查询方法都有责任将自己的偏移键添加到@tracks_offset
。
#filter_tracks
还为已经出现在搜索结果中的曲目保存了一系列曲目ID。
如果您需要持久性,请创建@tracks_offset
和@track_ids
个会话/ cookie而不是实例变量。逻辑应该是相同的。如果您使用会话来存储结果中的偏移量和ID,请记住在用户完成与此功能的交互时清除它们。
见下文。请注意,我重构了您的#filter_tracks
方法,将责任分为9种不同的方法:#filter_tracks
,#heavy_rotation
,#order_by_params
,#heavy_rotation?
,#validate_and_return_top_results
,和#tracks_top_licensed
... #tracks_top_<whatever>
。这将使我的笔记更容易理解,并且您的代码更易于维护。
def filter_tracks
# Does this need to be so high when JavaScript limits display to 14?
@limit ||= 50
@tracks_offset ||= {}
@tracks_offset[:default] ||= 0
@result_track_ids ||= []
@order ||= params[:order] || 'heavy_rotation'
tracks = Track.ready.with_artist
tracks = parse_params(params[:q], tracks)
@result_count = tracks.count
# Checks for heavy_rotation filter flag
if heavy_rotation? @order
@tracks = heavy_rotation
else
@tracks = order_by_params
end
render partial: "shared/results"
end
所有#heavy_rotation
都会调用各种查询方法。这样可以轻松地添加,修改或删除任何一种查询方法作为条件更改,而不会影响任何其他方法。
def heavy_rotation
week_ago = Time.now - 7.days
two_weeks_ago = Time.now - 14.days
three_months_ago = Time.now - 3.months
tracks_top_licensed(date_range: three_months_ago, max_results: 5) +
tracks_top_listens(date_range: two_weeks_ago, max_results: 3) +
tracks_top_downloaded(date_range: two_weeks_ago, max_results: 2) +
tracks_staff_picks(date_range: three_months_ago, max_results: 4)
end
这里有一个查询方法的样子。它们基本相同,但使用自定义SQL / ORM查询。您注意到我没有将:limit
参数设置为我希望查询方法返回的结果数。如果返回的其中一个记录被另一个查询方法复制,如果staff_picks
和top_downloaded
返回了相同的轨道,则会产生问题。然后我将不得不进行额外的查询以获得另一条记录。这不是一个错误的决定,只是一个我没有决定做的决定。
def tracks_top_licensed(args = {})
args = @default.merge args
max = args[:max_results]
date_range = args[:date_range]
# Adds own offset key to #filter_tracks hash map => @tracks_offset
@tracks_offset[:top_licensed] ||= 0
unfiltered_results = Track.top_licensed
.where("tracks.updated_at >= :date_range", date_range: date_range)
.limit(@limit)
.offset(@tracks_offset[:top_licensed])
top_tracks = validate_and_return_top_results(unfiltered_results, max)
# Add offset of your most recent query to the cumulative offset
# so triggering 'view more'/pagination returns contiguous results
@tracks_offset[:top_licensed] += top_tracks[:offset]
top_tracks[:top_results]
end
在每种查询方法中,我都是通过自定义方法#validate_and_return_top_results
清理记录对象。我的验证器通过记录对象检查其祖先方法@track_ids
中#filter_tracks
集合的重复项。然后它返回其调用者指定的记录数。
def validate_and_return_top_results(collection, max = 1)
top_results = []
i = 0 # offset incrementer
until top_results.count >= max do
# Checks if track has already appeared in the results
unless @result_track_ids.include? collection[i].id
# this will be returned to the caller
top_results << collection[i]
# this is the point of reference to validate your query method results
@result_track_ids << collection[i].id
end
i += 1
end
{ top_results: top_results, offset: i }
end