如何使用 Rails 3 和 Arel 加入子选择(范围)?

发布于 2024-10-08 20:39:44 字数 2092 浏览 8 评论 0原文

我需要将一个表连接到 select/group-by 查询(其中包括相同的表),并且我想使用 Arel 来完成此操作。

我有一个 :phenotypes 表,它们是 has_and_belongs_to_many :genes,它们本身就是 has_and_belongs_to_many :orthogroups。因此,表型和邻位群之间的关系是多对多的。

我有两个范围(在Orthogroup上),它们获取与特定表型相关的所有orthogroup:

  scope :with_phenotype, lambda { |phenotype_id|
    where("observations.phenotype_id = ?", phenotype_id).
      joins("inner join orthologies on (orthologies.orthogroup_id = orthogroups.id) inner join observations on (observations.gene_id = orthologies.gene_id)")
  }

  scope :with_associated_gene_ids_for_phenotype, lambda { |phenotype_id|
    with_phenotype(phenotype_id).
      select("orthogroups.id, array_agg(distinct observations.gene_id) as associated_gene_ids").
      group("orthogroups.id")
  }

因此,执行Orthogroup.with_linked_gene_ids_for_phenotype(48291)应该返回一个orthogroup ID表以及将它们链接到表型的基因。

那东西一切都很好。

问题是我想获取其余的 orthogroups.* 并将其加入到第二个范围的结果中,这样基因列表基本上就像我的 Orthogroup ActiveRecord 上的一个额外字段模型。

粗略地说,是这样的:

SELECT   o1.*, o_genes.associated_gene_ids
FROM     orthogroups o1
INNER JOIN (
  SELECT    o2.id, array_agg(DISTINCT obs.gene_id) AS associated_gene_ids
  FROM orthogroups o2
  INNER JOIN orthologies ortho ON (ortho.orthogroup_id = o2.id)
  INNER JOIN observations obs ON (ortho.gene_id = obs.gene_id)
  WHERE obs.phenotype_id = ? GROUP BY o2.id
) AS o_genes
ON (o1.id = o_genes.id);

现在,该查询似乎可以工作。但我更愿意找到一种方法将 Orthogroup 表直接连接到其自己的范围以获取这些基因。

也许使用 SQL 会更简单,但使用 Arel 似乎应该有一种简单的方法。我发现了几个类似的问题,但似乎没有一个有答案。

我找到的最接近的解决方案是这样的:

def self.orthogroups phenotype_id
  Orthogroup.select("orthogroups.*, o_genes.associated_gene_ids").
    joins(Arel.sql("inner join (" + Orthogroup.with_associated_gene_ids_for_phenotype(phenotype_id).to_sql + ") AS o_genes ON (o_genes.id = orthogroups.id)"))
end

输出的 SQL 在两个上下文中使用表“orthogroups”,这让我很担心;然而,对结果的抽查表明该查询是正确的。

尽管如此,这并不是我所希望的优雅解决方案。是否可以在没有尴尬的“inner join (...)”的情况下做到这一点?

I need to join a table to the a select/group-by query (which includes the same table), and I'd like to do it using Arel.

I have a table of :phenotypes which are has_and_belongs_to_many :genes, which are themselves has_and_belongs_to_many :orthogroups. As a result, the relationship between phenotypes and orthogroups are many-to-many.

I have two scopes (on Orthogroup) which get all orthogroups associated with a specific phenotype:

  scope :with_phenotype, lambda { |phenotype_id|
    where("observations.phenotype_id = ?", phenotype_id).
      joins("inner join orthologies on (orthologies.orthogroup_id = orthogroups.id) inner join observations on (observations.gene_id = orthologies.gene_id)")
  }

  scope :with_associated_gene_ids_for_phenotype, lambda { |phenotype_id|
    with_phenotype(phenotype_id).
      select("orthogroups.id, array_agg(distinct observations.gene_id) as associated_gene_ids").
      group("orthogroups.id")
  }

Thus, doing Orthogroup.with_associated_gene_ids_for_phenotype(48291) should return a table of orthogroup IDs and the genes which link them to the phenotypes.

That stuff all works fine.

The issue is that I'd like to get the rest of orthogroups.* and join it to the results of the second scope, so that the list of genes is basically like an extra field on my Orthogroup ActiveRecord model.

Roughly, something like this:

SELECT   o1.*, o_genes.associated_gene_ids
FROM     orthogroups o1
INNER JOIN (
  SELECT    o2.id, array_agg(DISTINCT obs.gene_id) AS associated_gene_ids
  FROM orthogroups o2
  INNER JOIN orthologies ortho ON (ortho.orthogroup_id = o2.id)
  INNER JOIN observations obs ON (ortho.gene_id = obs.gene_id)
  WHERE obs.phenotype_id = ? GROUP BY o2.id
) AS o_genes
ON (o1.id = o_genes.id);

Now, that query appears to work. But I'd much rather find a way to join the Orthogroup table directly to its own scope to get those genes.

Perhaps it'd be simpler to use SQL, but it seems like there should be an easy way with Arel. I've found several similar questions, but none seem to have answers.

The closest solution I've found is this:

def self.orthogroups phenotype_id
  Orthogroup.select("orthogroups.*, o_genes.associated_gene_ids").
    joins(Arel.sql("inner join (" + Orthogroup.with_associated_gene_ids_for_phenotype(phenotype_id).to_sql + ") AS o_genes ON (o_genes.id = orthogroups.id)"))
end

The outputted SQL uses the table "orthogroups" in two contexts, and this worried me; however, a spot check of results suggests the query is correct.

Still, this is not the elegant solution for which I might have hoped. Is it possible to do this without the awkward "inner join (...)"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

御守 2024-10-15 20:39:44

乍一看您的代码:您是否尝试将方法和局部变量“orthogroups”重命名为不同的名称,因为您有同名的关系?

Just from an initial glance at your code: have you tried renaming the method and the local variable "orthogroups" to something different as you have a relationship by the same name?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文