您当然可以通过过滤掉与您当前用户匹配或有当前用户投票的文章来避免在第三次查询中出现重复文章。
假设你正在做Article.where(article_type: 'public')你可以做
q = Article.where(article_type: 'public')
# Filter out articles owned by the current user
q = q.where.not(user_id: current_user.id)
# Filter out articles voted on by the current user
# This left joins the votes table to include the single row that will have
# recorded the vote for the current user on the article in question
# if the id of that left joined table is null then we know there
# is no vote on the article for the current user and that it won't have
# been included in the second step
q = q.joins("left join votes on article_id = articles.id and user_id = #{User.sanitize(current_user.id)}"
q = q.where(votes: { id: nil })
articles = q # or q.limit(10) or somesuch
或者,正如您所说,更理想的情况是您希望执行一个包含您的订购要求的查询。我不确定这会有多有效,因此您可能需要尝试一下。使用单个查询将更容易对文章进行分页。 (您可能还需要考虑复杂的搜索顺序是否对您的用户来说实际上是清楚的,以及您是否最好使用 3 种不同的搜索 - 但这是另一个问题。
因此,要做到这一点,您需要一个稍微复杂一点的查询,并具有更复杂的排序。我正在使用CASE 根据您的条件设置订单:
q = Article.where("user_id = ? or article_type = 'public'", current_user.id)
# As in the query above, join in the vote row for the current user on the article
q = q.joins("left join votes on article_id = articles.id and user_id = #{User.sanitize(current_user.id)}"
q = q.where(votes: { id: nil })
# First order by whether the user is the current user or not
q = q.order("case when articles.user_id = #{User.sanitize(current_user.id)} then 1 else 2 end")
# Next order by whether the user has voted or not
q = q.order("case when votes.id is not null then 1 else 2 end")
articles = q # or q.limit(100) or q.page(1) or somesuch
order 子句中的 case 语句将条件转换为 1 或 2。因此,例如在第一个 order 子句中。如果文章的用户 id 与当前用户匹配,则将使用 1 进行排序,否则将使用 2。因此,用户的文章将在文章列表中显示得更高。下一个排序标准是用户是否对文章进行了投票。所以它最终会变成order by 1, 1。
这应该可以满足您的需求(尽管我尚未对其进行测试,因此您可能需要在这里或那里进行调整)。