【问题标题】:Retrieving only unique records with multiple requests仅检索具有多个请求的唯一记录
【发布时间】:2015-04-04 10:52:31
【问题描述】:

我正在处理这个“heavy_rotation”过滤器。基本上,它会根据某些参数(例如,listen_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

【问题讨论】:

    标签: ruby-on-rails ruby sorting activerecord filtering


    【解决方案1】:

    我认为寻求“优雅”的解决方案会产生许多不同的意见,因此我将提供一种方法和我的推理。在我的设计决策中,我觉得在这种情况下,通过过滤返回的记录对象而不是试图将查询限制为仅产生唯一结果来强制查询交集的唯一性是最佳且优雅的。另一方面,对于获取分页的连续结果,我将存储每个查询的偏移量,并将其用作使用实例变量或会话的下一个查询的起点,具体取决于数据需要如何持久化。

    这是我重构后的代码版本的gist,其中包含已实现的解决方案和 cmets 解释我为什么选择使用某些逻辑或数据结构的原因:https://gist.github.com/femmestem/2b539abe92e9813c02da

    #filter_tracks 持有一个哈希映射@tracks_offset,其他方法可以访问和更新它;每个查询方法都负责将自己的偏移键添加到@tracks_offset

    #filter_tracks 还保存了已出现在结果中的曲目的曲目 ID 集合。

    如果您需要持久性,请使用 @tracks_offset@track_ids 会话/cookies 而不是实例变量。逻辑应该是一样的。如果您使用会话来存储结果中的偏移量和 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_pickstop_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 清理记录对象。我的验证器通过其祖先方法#filter_tracks 中的@track_ids 集合检查记录对象是否存在重复项。然后它返回调用者指定的记录数。

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 2020-02-10
      • 2023-03-04
      • 1970-01-01
      相关资源
      最近更新 更多