Ruby on rails 仅检索具有多个请求的唯一记录
我有一个“重旋转”过滤器,我正在工作。基本上,它根据特定的参数从我们的数据库中获取曲目(包括收听次数、员工选择、购买次数等) 向过滤器控制器动作发出xhr请求。在那里我有一面旗子来检查它是否是“重旋转”。我可能会将此移到模型(因为此控制器正在变胖)。。。无论如何,我如何确保(以有效的方式)不让它拉取相同的记录?我考虑过偏移量,但是我必须跟踪每个查询的偏移量。或者存储每个查询要比较的track.id?有什么想法吗?我很难想出一个优雅的方法来做这件事 也许应该注意的是,通过Javascript设置了14的限制,当用户点击“查看更多”进行分页时,它会发送另一个请求来过滤U曲目 感谢您的帮助!谢谢Ruby on rails 仅检索具有多个请求的唯一记录,ruby-on-rails,ruby,sorting,activerecord,filtering,Ruby On Rails,Ruby,Sorting,Activerecord,Filtering,我有一个“重旋转”过滤器,我正在工作。基本上,它根据特定的参数从我们的数据库中获取曲目(包括收听次数、员工选择、购买次数等) 向过滤器控制器动作发出xhr请求。在那里我有一面旗子来检查它是否是“重旋转”。我可能会将此移到模型(因为此控制器正在变胖)。。。无论如何,我如何确保(以有效的方式)不让它拉取相同的记录?我考虑过偏移量,但是我必须跟踪每个查询的偏移量。或者存储每个查询要比较的track.id?有什么想法吗?我很难想出一个优雅的方法来做这件事 也许应该注意的是,通过Javascript设置了
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
我认为寻求一个“优雅”的解决方案将产生许多不同的观点,因此我将提供一种方法和我的推理。在我的设计决策中,我认为在这种情况下,通过过滤返回的记录对象,而不是试图限制查询只产生唯一结果,从而在查询交叉点上强制执行唯一性,这是最佳且优雅的做法。另一方面,对于获取分页的连续结果,我将存储每个查询的偏移量,并使用实例变量或会话将其用作下一个查询的起点,具体取决于数据需要如何持久化
以下是我的代码重构版本的要点
,其中包含已实现的解决方案和注释,解释了我选择使用某些逻辑或数据结构的原因:
#filter_tracks
保存一个散列映射@tracks_offset
,其他方法可以访问和更新该散列映射;每个查询方法都负责将自己的偏移键添加到@tracks\u offset
#filter_曲目
还保存已出现在结果中的曲目的曲目id集合
如果需要持久性,请使用@tracks\u offset
和@track\u id
会话/cookie代替实例变量。逻辑应该是一样的。如果使用会话存储结果中的偏移量和id,请记住在用户完成与此功能交互后清除它们
见下文。注意,我重构了您的#filter_tracks
方法,将职责分为9种不同的方法:#filter_tracks
、#heavy_rotation
、#order_by_params
、#heavy#rotation?
、验证#u并返回#top_结果、以及#tracks#tracks#<代码>跟踪顶部
。这将使我的笔记更容易理解,您的代码更易于维护
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\u picks
和top\u download
返回了相同的曲目。然后我必须进行额外的查询以获得另一条记录。这不是一个错误的决定,只是一个我没有决定做的决定
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_和_return_top_results
清理记录对象。我的验证器通过记录对象检查其祖先方法中的@track\u id
集合中是否存在重复项\filter\u 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
def验证和返回顶部结果(集合,最大值=1)
top_结果=[]
i=0#偏移增量
直到top_results.count>=最大do
#检查轨迹是否已出现在结果中
除非@result\u track\u id.include?集合[i].id
#这将返回给调用者
最佳结果