【问题标题】:ActiveRecord 4.2 includes and ordering by countActiveRecord 4.2 包括按计数排序
【发布时间】:2016-02-16 07:43:30
【问题描述】:

我正在搜索我正在从事的项目。它旨在能够搜索文章正文并生成作者列表,按匹配文章的数量排序,并且仅包括相关文章,而不是所有文章。

我目前有以下查询:

Author.includes(:articles).where('articles.body ilike ?', '%foo%').references(:articles)

在这种情况下使用包含使得所有相关文章(不是所有文章)都被预加载,这正是我想要的。但是,当涉及到按收录文章数量排序时,我不知道如何进行。

我应该注意我想在 ActiveRecord 中执行此操作,因为分页将在查询之后应用。不是在 Ruby 解决方案之后。

我应该注意我使用的是 PostgreSQL 9.3。

编辑:使用原始 SQL

这似乎像这样单独工作:

Author.includes(:articles).where('articles.body ilike ?', '%foo%').references(:articles).select('authors.*, (SELECT COUNT(0) FROM articles WHERE articles.author_id = authors.id) AS article_count').order('article_count DESC')

这很好用。但是,如果我添加 .limit(1) 它会中断。

PG::UndefinedColumn: ERROR: column "article_count" does not exist

知道为什么添加限制会破坏它吗?查询似乎也很不一样

SELECT DISTINCT "authors"."id", article_count AS alias_0 FROM "authors" LEFT OUTER JOIN "articles" ON "articles"."author_id" = "authors"."id" WHERE (articles.body ilike '%microsoft%') ORDER BY article_count DESC LIMIT 1

【问题讨论】:

    标签: ruby-on-rails ruby postgresql activerecord


    【解决方案1】:

    我认为没有现成的解决方案。您必须编写原始 sql 来执行此操作,但您可以将其与现有的 ActiveRecord 查询结合使用。

    Author
      .includes(:articles)
      .select('authors.*, (SELECT COUNT(0) FROM articles WHERE articles.author_id = authors.id) AS article_count')
      .order('article_count DESC')
    

    所以这里唯一要解释的是select 部分。第一部分 authors.* 选择 authors 表下的所有字段,这是默认设置。由于我们还想计算文章的数量,我们创建了一个子查询并将其结果作为作者的伪列之一传递(我们称之为article_count)。最后一部分是使用article_count 调用order

    此解决方案假定您必须根据您的设置进行微调。

    • 按照惯例,rails 中的Author 映射到作者表。如果它是 STI(继承自 User 类并使用 users 表),则需要将 authors 更改为 users
    • articles.author_id 假设外键是 author_id(本质上,一篇文章只由一个作者撰写)。更改为任何外键。

    因此,您将拥有一组按作者撰写的文章数量排序的作者。

    【讨论】:

    • 我用更多信息更新了这个问题。您的建议本身就可以正常工作,但是当我尝试添加限制条款时,它会崩溃,说没有名为 article_count 的列。
    • 我实际上也不知道当你添加limit 时发生了什么,但我做了一些检查,结果发现添加limit 将导致2 次sql 调用。第一个是引发错误的查询,rails 这样做是为了获取作者的 id(这里没有使用 select 部分,但 order 部分是,因此出现错误)。下一个 sql 查询用于实际加载使用 select 部分的作者。所以我想没有 ActiveRecord 方法可以做你想做的事。
    • 看来你是对的,我使用两个单独的查询来代替它。无论如何,我将来会改进我的搜索后端,所以这不是一个大问题。感谢您的帮助。
    • 只是想补充一点,以防将来有人通过谷歌到达这里,我设法在一个查询中做到这一点,如本答案中所述,然后使用Kaminari 使用@987654322 进行分页@ 它提供。我觉得这比做 2 次查询更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多