Unable to order by translated column in Rails using Mobility

戏子无情 提交于 2019-12-24 13:28:40

问题


This question is based on an issue posted to the Mobility GitHub project.

Context

  • Rails: 5.0.6
  • Mobility: 0.4.2 (with table backend)

I'm working with an articles table that supports multiple article types (e.g., blog post, case study, knowledge base article). This table includes a column to track the number of times an article is viewed—an integer column that increments every time a show action is called for an article.

In implementing translations for these articles, I want to track the number of views for each translation individually, not for the main article object. In an effort to achieve this, I included the views property as one of the translated properties on my object:

class Article < ApplicationRecord
  include Taggable
  include PgSearch

  translates :title, type: :string
  translates :subtitle, type: :text
  translates :body, type: :text
  translates :views, type: :integer

  multisearchable :against => [:title, :subtitle, :body]

  has_and_belongs_to_many :products

  attachment :hero_image, content_type: %w(image/jpeg image/png image/gif)

  validates :title, :body, :posted_on, presence: true

  scope :current, -> { where 'posted_on < ?', Date.tomorrow }
  scope :news_articles, -> { where type: ['BlogPost', 'CaseStudy'] }

  def log_view(by = 1)
    self.views ||= 0
    self.views += by
    self.save(touch: false)
  end

  def to_param
    "#{id} #{title}".parameterize
  end

  def published?
    posted_on < Date.tomorrow
  end
end

Expected Behavior

In my controller, I want to list the top ten most viewed articles, which I get with this query:

@top_articles = Article.current.news_articles.order(views: :desc, posted_on: :desc).limit(10)

I expect to receive an array of articles, as I did before implementing Mobility.

Actual Behavior

What I get instead is #<Article::ActiveRecord_Relation:0x233686c>. If I then try to convert that to an array with @top_articles.to_a, I get this error:

  Article Load (0.7ms)  SELECT  "articles".* FROM "articles" WHERE (posted_on < '2018-02-11') AND "articles"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"."views" DESC, "articles"."posted_on" DESC LIMIT $1  [["LIMIT", 10]]
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column articles.views does not exist
LINE 1: ...les"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"...
                                                             ^
: SELECT  "articles".* FROM "articles" WHERE (posted_on < '2018-02-11') AND "articles"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"."views" DESC, "articles"."posted_on" DESC LIMIT $1
from /usr/local/bundle/gems/activerecord-5.0.6/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'

Changing the query to include i18n:

@top_articles = Article.i18n.current.news_articles.order(views: :desc, posted_on: :desc).limit(10)

… returns #<#<Mobility::Backends::ActiveRecord::Table::QueryMethods:0x00000000050a86d8>:0x286c3e0>, and when I try to convert that to an array, I get the same thing:

  Article Load (0.7ms)  SELECT  "articles".* FROM "articles" WHERE (posted_on < '2018-02-11') AND "articles"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"."views" DESC, "articles"."posted_on" DESC LIMIT $1  [["LIMIT", 10]]
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column articles.views does not exist
LINE 1: ...les"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"...
                                                             ^
: SELECT  "articles".* FROM "articles" WHERE (posted_on < '2018-02-11') AND "articles"."type" IN ('BlogPost', 'CaseStudy') ORDER BY "articles"."views" DESC, "articles"."posted_on" DESC LIMIT $1
from /usr/local/bundle/gems/activerecord-5.0.6/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'

It turns out that while Mobility supports the use of translated fields in the where clause, it does not currently support them in the order clause of an Active Record query.

Workaround attempts

1. Reference the translation table in the order clause

Based on feedback from the gem author, I tried the query:

Article.i18n.current.news_articles.order('article_translations.views desc', 'articles.posted_on desc')

… which returns a #<#<Mobility::Backends::ActiveRecord::Table::QueryMethods>> object, and to_a returns this error:

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "article_translations"
LINE 1: ...les"."type" IN ('BlogPost', 'CaseStudy') ORDER BY article_tr...
                                                             ^
: SELECT  "articles".* FROM "articles" WHERE (posted_on < '2018-02-12') AND "articles"."type" IN ('BlogPost', 'CaseStudy') ORDER BY article_translations.views desc, articles.posted_on desc LIMIT $1
from /usr/local/bundle/gems/activerecord-5.0.6/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'

2. Add a joins or includes clause for the translation table

Article.i18n.joins(:article_translations).order('article_translations.views desc', 'articles.posted_on desc').limit(10)

This query, again, returns a #<#<Mobility::Backends::ActiveRecord::Table::QueryMethods>> object, and to_a results in:

ActiveRecord::ConfigurationError: Can't join 'Article' to association named 'article_translations'; perhaps you misspelled it?
from /usr/local/bundle/gems/activerecord-5.0.6/lib/active_record/associations/join_dependency.rb:231:in `find_reflection'

3. Add has_many :article_translations to model

Adding a relation to the model throws back this error:

uninitialized constant Article::ArticleTranslation

So…

What should I try next?


回答1:


UPDATE

Ordering by translated attributes is now supported as of version 0.8.0/

Just do this:

Article.i18n.current.news_articles.
  order(:views => :desc, :'articles.posted_on' => :desc)

and Mobility will handle everything (you don't need to join the translation table, etc.)

ORIGINAL ANSWER

You were right that you need to join the translations table, but the association is named translations, not article_translations.

In any case, there is a method join_translations that joins the translation table, so this should work:

Article.i18n.
  current.
  news_articles.
  join_translations.
  order('article_translations.views desc', 'articles.posted_on desc')


来源:https://stackoverflow.com/questions/48728763/unable-to-order-by-translated-column-in-rails-using-mobility

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!