【问题标题】:Rails merge common attributes on a `has_many: through` model to show unique values based on another attribute of the modelRails 合并 `has_many: through` 模型上的公共属性,以根据模型的另一个属性显示唯一值
【发布时间】:2021-06-01 07:29:50
【问题描述】:

我有以下模型和关系:

class Company < ApplicationRecord
  has_many :jobs, dependent: :delete_all
  has_many :job_technologies, through: :jobs
end

class Job < ApplicationRecord
  has_many :job_technologies
  has_many :technologies, through: :job_technologies
  belongs_to :company
end

class JobTechnology < ApplicationRecord
  belongs_to :job
  belongs_to :technology
end

工作技术具有以下架构:

  create_table "job_technologies", force: :cascade do |t|
    t.bigint "job_id"
    t.bigint "technology_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "title_matches"
    t.integer "description_matches"
    t.index ["job_id"], name: "index_job_technologies_on_job_id"
    t.index ["technology_id"], name: "index_job_technologies_on_technology_id"
  end

因此,当我创建 JobTechnology 时,我会这样做:

JobTechnology.new(
        job: Job.new(title: 'some title'),
        technology: Technology.where(name: 'javascript').first,
        title_matches: 1,
        description_matches: 10
      )

这意味着如果我想看看我的工作有哪些技术可以做:

Job.first.job_technologies
=> [#<JobTechnology:0x00007ffc5b5957e0
  id: 647,
  job_id: 263,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 0,
  description_matches: 2>,
 #<JobTechnology:0x00007ffc5b5953a8
  id: 648,
  job_id: 263,
  technology_id: 4,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 1,
  description_matches: 2>,
 #<JobTechnology:0x00007ffc5b595100

这将始终是独特技术的列表,因为我只为每个 Job 的每种独特技术(例如“javascript”、“ruby”等)创建一个 JobTechnology

现在我想看看一家公司拥有哪些技术。我能做到:

Company.first.job_technologies
=> [#<JobTechnology:0x00007ffc56e3b9d0
  id: 647,
  job_id: 270,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 0,
  description_matches: 2>,
 #<JobTechnology:0x00007ffc56e3b908
  id: 648,
  job_id: 271,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 1,
  description_matches: 2>,
 #<JobTechnology:0x00007ffc56e3b818
  id: 649,
  job_id: 263,
  technology_id: 31,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 0,
  description_matches: 1>,
 #<JobTechnology:0x00007ffc56e3b750
  id: 650,
  job_id: 263,
  technology_id: 32,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 0,
  description_matches: 2>]

问题是Company 可以拥有JobTechnologies,它们是“重复”技术(想象一下Company 有5 个Jobs,它们都有JobTechnology'javascript')。您可以在上面的示例中看到我们有

#<JobTechnology:0x00007ffc56e3b9d0
  id: 647,
  job_id: 270,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 0,
  description_matches: 2>,
 #<JobTechnology:0x00007ffc56e3b908
  id: 648,
  job_id: 271,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 1,
  description_matches: 2>,

我想合并JobTechnology,以便我们只为每种技术计算一个Technology,但我想聚合title_matchesdescription_matches。所以上面的例子会变成

#<JobTechnology:0x00007ffc56e3b9d0
  id: 647,
  job_id: 263,
  technology_id: 1,
  created_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  updated_at: Mon, 31 May 2021 12:56:36 UTC +00:00,
  title_matches: 1,
  description_matches: 4>

我可以将其作为Company 上手动合并这些的方法来执行,但每次搜索时我都需要手动执行此操作。我想知道是否有更便宜的方法来实现这一点。

我对缓存知之甚少,但也许最好的选择是这样做 - 任何关于此的链接/想法/建议都值得赞赏。我只是觉得有一种方法可以通过活动记录查询来做到这一点,而不必每次都手动计算。

我的另一个想法是我是否应该创建一个CompanyTechnology 模型,也许这就是这个逻辑应该存在的地方

【问题讨论】:

    标签: ruby-on-rails ruby postgresql


    【解决方案1】:

    如您在示例中概述的那样获取idjob_idcreated_atupdated_at 没有多大意义。如果通过5个JobTechnology-s聚合结果,应该是哪个id?我想你无论如何都不在乎它。让我们专注于您明确定义的项目,technology_idtitle_matchesdescription_matches

    您要查找的内容在 SQL 中称为 GROUP BY。 rails 中的等价物是.group()。我们希望按technology_id 对结果进行分组,并且对于每个technology_id 聚合(总和)title_matchesdescription_matches

    Company.first.job_technologies.group(:technology_id).pluck(
      :technology_id, 'sum(title_matches)', 'sum(description_matches)'
    )
    

    这将返回一个数组数组,其中每个内部数组都有我们要查找的 3 个元素。

    为了摆脱 sql 文字,我们可以将 Arel 用于同样的事情,只是更安全。

    Company.first.job_technologies.group(:technology_id).pluck(
      :technology_id,
      JobTechnology.arel_table[:title_matches].sum,
      JobTechnology.arel_table[:description_matches].sum
    )
    

    如果您想获得一个活动记录关系而不是数组数组,请使用select 而不是pluck。但是,要小心它,因为它没有 id 列,它的行为可能有点奇怪。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 2015-11-23
      • 1970-01-01
      • 2015-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多