Ruby on rails 仅检索具有多个请求的唯一记录

Ruby on rails 仅检索具有多个请求的唯一记录,ruby-on-rails,ruby,sorting,activerecord,filtering,Ruby On Rails,Ruby,Sorting,Activerecord,Filtering,我有一个“重旋转”过滤器,我正在工作。基本上,它根据特定的参数从我们的数据库中获取曲目(包括收听次数、员工选择、购买次数等) 向过滤器控制器动作发出xhr请求。在那里我有一面旗子来检查它是否是“重旋转”。我可能会将此移到模型(因为此控制器正在变胖)。。。无论如何,我如何确保(以有效的方式)不让它拉取相同的记录?我考虑过偏移量,但是我必须跟踪每个查询的偏移量。或者存储每个查询要比较的track.id?有什么想法吗?我很难想出一个优雅的方法来做这件事 也许应该注意的是,通过Javascript设置了

我有一个“重旋转”过滤器,我正在工作。基本上,它根据特定的参数从我们的数据库中获取曲目(包括收听次数、员工选择、购买次数等)

向过滤器控制器动作发出xhr请求。在那里我有一面旗子来检查它是否是“重旋转”。我可能会将此移到模型(因为此控制器正在变胖)。。。无论如何,我如何确保(以有效的方式)不让它拉取相同的记录?我考虑过偏移量,但是我必须跟踪每个查询的偏移量。或者存储每个查询要比较的track.id?有什么想法吗?我很难想出一个优雅的方法来做这件事

也许应该注意的是,通过Javascript设置了14的限制,当用户点击“查看更多”进行分页时,它会发送另一个请求来过滤U曲目

感谢您的帮助!谢谢

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
#这将返回给调用者
最佳结果