Rails 5.1其中查询有很多模型

时间:2018-03-07 22:35:08

标签: ruby-on-rails where-clause

背景: 我有两个模型:

gin.rb

class Gin < ApplicationRecord
  has_many :gins_botanical
  has_many :botanicals, through: :gins_botanical

botanical.rb

class Botanical < ApplicationRecord
  has_many :gins_botanical
  has_many :gins, through: :gins_botanical

在查看给定植物的展示页面时,我想显示一个包含特定植物的杜松子酒列表。

我正在使用where子句来实现这一目标,但我无法让它发挥作用。

  

PG :: UndefinedTable:错误:缺少表的FROM子句条目   &#34;植物&#34;第1行:SELECT&#34; gins&#34;。* FROM&#34; gins&#34;哪里   (botanicals.name LIKE&#39;%an ...                                              ^:SELECT&#34; gins&#34;。* FROM&#34; gins&#34; WHERE(botanicals.name LIKE&#39;%angelica root%&#39;)

我可以看到"%#{@botanical.name}%"很好,因为它显示了当前植物的价值,在这种情况下,angelica root,但很明显我并没有拉过其他也有的杜松子酒angelica root作为一种植物。

botanicals_controller

  def show
    @similargins = Gin.where("botanicals.name LIKE ?", "%#{@botanical.name}%")
  end

植物/ show.html.erb

<% @similargins.each do |gin| %>
  <%= gin.name%>
<% end %>

3 个答案:

答案 0 :(得分:2)

假设你的关联设置正确(你的关系并不明显),那么我想你可以这样做:

class Gin < ApplicationRecord 
  has_many :gin_botanicals 
  has_many :botanicals, through: :gin_botanicals

  class << self 

    def with_botanicals(botanicals)
      joins(:bontanicals).where(botanicals: {id: botanicals})
    end

  end

end
之前我已经说过了,我会再说一遍。有些人会更喜欢scope到类方法。有些人会更喜欢self.with_botanicalsclass << self。可以说,这些是偏好的问题。

然后,我们假设您有一个@gin变量和一个@botanical变量。您应该可以执行以下操作:

Gin.with_botanicals(@botanical).where.not(@gin)

这应该会为您提供@botanical但不是@gin的所有杜松子酒。您可以通过执行以下操作匹配所有@gin.botanicals

Gin.with_botanicals(@gin.botanicals).where.not(@gin)

如果你想找到与@gin共有任何植物药的所有杜松子酒,那么这个鱼的水壶会略有不同(包括使用{{1} }})并适用于一个新问题。同样,您可以搜索所有含有名称IN LIKE的植物药的杜松子酒,但同样会遇到另一种问题。

答案 1 :(得分:1)

  

在查看给定植物的展示页面时,我想显示一个包含特定植物的杜松子酒列表。

简单。只是做:

@botanical = Botanical.includes(:gins).find(params[:id])
@gins = @botanical.gins

如果您已拥有ID,则无需LIKE查询。而且由于你已经设置了间接关联,你可以用它来获取金币。

如果您真正想要的是获得与给定杜松子酒有共同植物成分的其他杜松子酒,您可以这样做:

class Gin < ApplicationRecord
  has_many :gin_botanicals
  has_many :botanicals, through: :gin_botanicals

  def similiar_gins
    Gin.joins(:botanicals)
     .where(botanicals: { id: self.botanical_ids })
     .where.not(id: self.id)
  end
end

.joins创建一个左内连接 - 所以在连接表中没有匹配的任何行都将被丢弃。

.where(botanicals: { id: self.botanical_ids })创建一个WHERE IN查询,要求联合记录至少有一个植物。

您还可以使用GROUP BY和HAVING:

设置所需的模拟度
class Gin < ApplicationRecord
  has_many :gin_botanicals
  has_many :botanicals, through: :gin_botanicals

  def similiar_gins(common_ingredients: 1)
    Gin.joins(:botanicals)
     .where(botanicals: { id: self.botanical_ids })
     .where.not(id: self.id)
     .group("gins.id")
     .having("COUNT(distinct botanicals.id) >= ?", common_ingredients)
  end
end

假设:

irb(main):039:0> Gin.all.pluck(:id, :name)
   (1.1ms)  SELECT "gins"."id", "gins"."name" FROM "gins"
=> [[1, "Beefeater Gin"], [2, "Bombay Sapphire"], [3, "Mockinghamshire"]]
irb(main):040:0> Botanical.all.pluck(:id, :name)
   (1.1ms)  SELECT "botanicals"."id", "botanicals"."name" FROM "botanicals"
=> [[1, "Almond"], [2, "liquorice"], [3, "Foo"]]
irb(main):041:0> GinBotanical.all.pluck(:gin_id, :botanical_id)
   (0.5ms)  SELECT "gin_botanicals"."gin_id", "gin_botanicals"."botanical_id" FROM "gin_botanicals"
=> [[1, 1], [2, 1], [1, 3], [1, 2], [2, 2]]

有两种常见成分:

irb(main):036:0> Gin.first.similiar_gins(common_ingredients: 2)
  Gin Load (1.2ms)  SELECT  "gins".* FROM "gins" ORDER BY "gins"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (4.0ms)  SELECT "botanicals".id FROM "botanicals" INNER JOIN "gin_botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "gin_botanicals"."gin_id" = $1  [["gin_id", 1]]
  Gin Load (4.3ms)  SELECT "gins".* FROM "gins" INNER JOIN "gin_botanicals" ON "gin_botanicals"."gin_id" = "gins"."id" INNER JOIN "botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "botanicals"."id" IN (1, 2, 3) AND ("gins"."id" != $1) GROUP BY gins.id HAVING (COUNT(distinct botanicals.id) >= 2)  [["id", 1]]
=> #<ActiveRecord::Relation [#<Gin id: 2, name: "Bombay Sapphire", created_at: "2018-03-07 23:44:43", updated_at: "2018-03-07 23:44:43">]>

但是如果我们将它设置为3,我们得到一个空集:

irb(main):037:0> Gin.first.similiar_gins(common_ingredients: 3)
  Gin Load (0.7ms)  SELECT  "gins".* FROM "gins" ORDER BY "gins"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (1.8ms)  SELECT "botanicals".id FROM "botanicals" INNER JOIN "gin_botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "gin_botanicals"."gin_id" = $1  [["gin_id", 1]]
  Gin Load (5.0ms)  SELECT "gins".* FROM "gins" INNER JOIN "gin_botanicals" ON "gin_botanicals"."gin_id" = "gins"."id" INNER JOIN "botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "botanicals"."id" IN (1, 2, 3) AND ("gins"."id" != $1) GROUP BY gins.id HAVING (COUNT(distinct botanicals.id) >= 3)  [["id", 1]]
=> #<ActiveRecord::Relation []>

答案 2 :(得分:0)

最终这是一个非常简单的修复。有人昨晚发布了这个答案,但很快就将其删除了。

我需要一个简单的连接:

 DECLARE @SESSION_START_DATE datetime
 SET @SESSION_START_DATE = DATEADD(ms, 1520503133980 % 1000,DATEADD(ss, 1520503133980/1000, CONVERT(DATETIME2(3),'19700101')))
 INSERT INTO EC_USER_SESSION(SESSION_START_DATE) VALUES(@SESSION_START_DATE);

所以现在,当在植物'当归根'的展示页面上,我有一个每个循环,向我展示任何其他在他们的植物中列出'当归根'的杜松子酒。