Rails:根据关联或序列化属性创建范围或有序查询?

时间:2015-04-13 04:51:57

标签: ruby-on-rails activerecord

我希望将我的故事索引与“已浏览的故事”(标记为用户查看的故事)进行升序排序,将它们放在用户的Feed中的最后一个,而“未查看”的故事首先出现。

我已经跟踪用户是否看过一个名为View的模型的故事。 客户端上的滚动事件发送ajax请求,该请求使用story_id和User_id创建视图:

class ViewsController < ApplicationController
  def create
    @user = current_user
    @story = Story.find(params[:story_id])
    @view = @user.views.find_or_create_by!(story_id: @story.id)
    render json: {viewed: true, id: @story.id}
  end
end

模型如下关联:

class View < ActiveRecord::Base
  belongs_to :user
  belongs_to :story
end

class User < ActiveRecord::Base
  has_many :views
  has_many :favorites
  has_many :stories, through: :favorites
end

class Story < ActiveRecord::Base
  serialize :viewed_by_users, Array
  has_many :views
  has_many :favorites
  has_many :users, through: :favorites
end

正如您所看到的,我已经在使用连接通过表来收藏,因此@ user.stories或@ story.users反映了收藏夹而不是视图。 如果我想检查一个故事是否已被用户查看,我可以这样做:

current_user.views.exists?(story: @story)
#Returns true or false when examining an instance of story.

我还为了方便而序列化了一个关于故事的故事,其中填充了用户ID,表示哪些用户查看过故事。 我可能想稍后取消我可以开始工作的属性。与此同时,我可以这样检查:

stories_not_viewed = Story.all.select { |s| s.viewed_by_users.include?(current_user) }
#returns an array of stories that have been viewed by current_user.

现在我正在努力在我的模型中创建一个范围,或者在我的控制器中创建一个.order查询,它将用户查看过的故事放在索引的尾端(升序)。

我有一个这样的故事控制器(有分页):

class StoriesController < ApplicationController
  def index
    page = params[:page].try(:to_i) || 1
    story_index = page - 1
    @stories = Story.includes(:users).limit(15).offset(story_index * 15)
    render json: @stories
  end
end

我无法弄清楚如何编写一个考虑我的视图关联的默认范围。看来我必须将current_user传递给模型,编写一些逻辑来查找用户id是否在每个故事的View关联中,如果是,则最后对这些故事进行排序(升序)。我在这里尝试了很多不同的课程方法但是对于我的生活,我无法弄清楚这一点。任何与此类似的例子都会受到赞赏。

另一种选择是如果我稍微改变我的设计,我可以让@stories索引简单地返回未查看的故事并为查看的故事创建不同的端点,让用户使用显示“show”的按钮切换前端视图查看故事“或”显示未审查的故事“。

我也无法做到这一点。我需要它来处理我的活动记录查询而不是数组,所以我可以保持我的分页。 我想我需要这样的事情:

@stories = Story.where.not viewed_by_users.include?(current_user) #...etc 

显然这不是正确的查询代码,这是我正在努力学习并试图了解更多信息。它显然需要像:

where("viewed_by_users != ?", current_user) 

...除了current_user是数组中的众多用户之一,因此在这种情况下不会完全正常工作。

我一直在努力解决所有这些问题但是没有任何工作要做。在这一点上,我很乐意让任何一个解决方案启动并运行。我知道这有点令人沮丧。感谢您提供任何帮助,提示或建议。

1 个答案:

答案 0 :(得分:1)

好的,这是一个关于如何运作的简单查询

select stories.*,
  IF (views.id is null, 0, 1) as viewed
from stories
left join views
  on(views.story_id = stories.id and views.user_id = 1)
where 1
group by stories.id
order by viewed, stories.id

因此,这基本上选择所有故事,使用视图进行连接并将视图限制为user_id,如果视图记录存在设置为已查看,否则设置为未查看,则通过为查看的记录提供较小的索引来对记录进行排序,然后按故事ID排序(例如,您可以将其更改为story.created_at)

现在将其转换为有效的记录查询

Story.select('*', 'IF (views.id is null, 0, 1) as viewed')
  .join('left join views on(
       views.story_id = stories.id and views.user_id = ?
     )', user.id)
  .group(:id)
  .order(viewed: :asc)
  .order(id: :asc)

并将其转换为范围或方法

def as_viewed_by(user)
  # same query here 
end

这是sql fiddle link你可以检查,3个故事和3个用户
用户1有1个视图,
用户2有2个视图,
用户3有3个视图,
更改@user_id并查看输出的不同顺序

试试并告诉我它是怎么回事。