<commit_before>class Article < ActiveRecord::Base
  include Dateable

  belongs_to :author, class_name: "User"
  belongs_to :editor, class_name: "User"
  has_and_belongs_to_many :tags, before_add: :validates_tag
  has_many :subscriptions, class_name: "ArticleSubscription", counter_cache: true
  has_many :subscribers, through: :subscriptions, class_name: "User", source: :user
  has_many :endorsements, class_name: "ArticleEndorsement", counter_cache: true
  has_many :endorsers, through: :endorsements, class_name: "User", source: :user

  attr_reader :tag_tokens

  before_validation :generate_slug
  after_save :update_subscribers
  after_create :increment_tags_counter
  before_destroy :decrement_tags_counter

  validates :slug, uniqueness: true, presence: true

  FRESHNESS_LIMIT = 7.days
  STALENESS_LIMIT = 6.months

  scope :archived, -> { where("archived_at IS NOT NULL") }
  scope :current, -> { where(archived_at: nil).order("rotted_at DESC") }
  scope :fresh, -> do
    where("updated_at >= ?", FRESHNESS_LIMIT.ago.beginning_of_day).
      where(archived_at: nil).
      where(rotted_at: nil)
  end
  scope :guide, -> { where(guide: true) }
  scope :ordered_current, -> { current.order(updated_at: :desc).limit(20) }
  scope :ordered_fresh, -> { fresh.order(updated_at: :desc).limit(20) }
  scope :rotten, -> { where("rotted_at IS NOT NULL") }
  scope :stale, -> { where("updated_at < ?", STALENESS_LIMIT.ago.beginning_of_day) }

  def self.popular
    includes(:subscribers).sort_by{ |a| a.subscribers.count }.reverse.take(5)
  end

  def self.text_search(query)
    if query.present?
      where("title ILIKE :q OR content ILIKE :q", q: "%#{query}%").order('title ASC')
    else
      order(updated_at: :desc)
    end
  end

  def author?(user)
    self.author == user
  end
<commit_msg>Use counter caches to improve Article.popular<commit_after>class Article < ActiveRecord::Base
  include Dateable

  belongs_to :author, class_name: "User"
  belongs_to :editor, class_name: "User"
  has_and_belongs_to_many :tags, before_add: :validates_tag
  has_many :subscriptions, class_name: "ArticleSubscription", counter_cache: true
  has_many :subscribers, through: :subscriptions, class_name: "User", source: :user
  has_many :endorsements, class_name: "ArticleEndorsement", counter_cache: true
  has_many :endorsers, through: :endorsements, class_name: "User", source: :user

  attr_reader :tag_tokens

  before_validation :generate_slug
  after_save :update_subscribers
  after_create :increment_tags_counter
  before_destroy :decrement_tags_counter

  validates :slug, uniqueness: true, presence: true

  FRESHNESS_LIMIT = 7.days
  STALENESS_LIMIT = 6.months

  scope :archived, -> { where("archived_at IS NOT NULL") }
  scope :current, -> { where(archived_at: nil).order("rotted_at DESC") }
  scope :fresh, -> do
    where("updated_at >= ?", FRESHNESS_LIMIT.ago.beginning_of_day).
      where(archived_at: nil).
      where(rotted_at: nil)
  end
  scope :guide, -> { where(guide: true) }
  scope :ordered_current, -> { current.order(updated_at: :desc).limit(20) }
  scope :ordered_fresh, -> { fresh.order(updated_at: :desc).limit(20) }
  scope :popular, -> { order("endorsements_count DESC, subscriptions_count DESC") }
  scope :rotten, -> { where("rotted_at IS NOT NULL") }
  scope :stale, -> { where("updated_at < ?", STALENESS_LIMIT.ago.beginning_of_day) }

  def self.text_search(query)
    if query.present?
      where("title ILIKE :q OR content ILIKE :q", q: "%#{query}%").order('title ASC')
    else
      order(updated_at: :desc)
    end
  end

  def author?(user)
    self.author == user
  end

{'fraction_non_alphanumeric': 0.1252072968490879, 'fraction_numerical': 0.0035931453841901604, 'mean_word_length': 4.699212598425197, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6398065', 'n_tokens_mistral': 1286, 'n_tokens_neox': 1257, 'n_words': 282}