如何通过列使rails查询唯一

时间:2017-10-11 12:54:03

标签: ruby-on-rails postgresql

我的Company模型有很多DisclosuresDisclosure包含名为titlepdfpdf_sha256的列。

class Company < ActiveRecord::Base
  has_many :disclosures
end

class Disclosure < ActiveRecord::Base
  belongs_to :company
end

我想通过pdf_sha256使其独一无二,如果pdf_sha256nil则应将其视为唯一。

如果是Array,我会这样写。

companies_with_sha256 = company.disclosures.where.not(pdf_sha256: nil).group_by(&:pdf_sha256).map do |key,values|
  values.max_by{|v| v.title.length}
end
companies_without_sha256 = company.disclosures.where(pdf_sha256: nil)
companies = companies_with_sha256 + companeis_without_sha256

如何使用Rails查询获得相同的结果?

6 个答案:

答案 0 :(得分:1)

假设您使用的是Rails 5,您可以链接.or命令来合并您的查询。

pdf_sha256_unique_disclosures = company.disclosures.where(pdf_sha256: nil).or(company.disclosures.where.not(pdf_sha256: nil))

然后你可以继续你的group_by逻辑。

但是,在上面的示例中,我并不完全确定目标是什么,但我很想知道如何使用生成的companies变量。

如果您想拥有包含nil的唯一pdf_sha256密钥的哈希值,以及由此产生的独特披露文档,您可以尝试以下方法:

sorted_disclosures = company.disclosures.group_by(&:pdf_sha256).each_with_object({}) do |entries, hash|
  hash[entries[0]] = entries[1].max_by{|v| v.title.length}
end

这应该给你一个类似于group_by的结果散列结构,其中你的密钥都是你唯一的pdf_sha256,并且该值将是与pdf_sha256匹配的最长的命名公开。

答案 1 :(得分:0)

Model.select(:rating)

这是一个Model对象数组。不是简单的评级。从uniq的角度来看,它们完全不同。你可以用这个:

Model.select(:rating).map(&:rating).uniq

或此(最有效)

Model.uniq.pluck(:rating)

Model.distinct.pluck(:rating)

更新

显然,从rails 5.0.0.1开始,它仅适用于“顶级”查询,如上所述。不适用于集合代理(例如“has_many”关系)。

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']

在这种情况下,在查询后进行重复数据删除

user.addresses.pluck(:city).uniq # => ['Moscow']

答案 2 :(得分:0)

为什么不:

ids = Disclosure.select(:id, :pdf_sha256).distinct.map(&:id)
Disclosure.find(ids)

id sill是不同的,因为它是主键,所以你所要做的就是映射id并按id找到Disclosures。

答案 3 :(得分:0)

您可以使用uniq方法

来实现此目的
ComboBox

这将以cloumn&#34; pdf_sha256&#34;

返回您的披露记录uniq

希望这对你有所帮助!干杯

答案 4 :(得分:0)

如果您需要具有不同pdf_sha256的关系,而您不需要明确的条件,则可以使用group -

scope :unique_pdf_sha256, -> { where.not(pdf_sha256: nil).group(:pdf_sha256) }
scope :nil_pdf_sha256,    -> { where(pdf_sha256: nil) }

您可以使用or,但传递给它的关系必须在结构上兼容。因此,即使您在这两个范围中获得相同类型的关系,也不能将其与or一起使用。

修改:为了使其在结构上相互兼容,您可以看到@AlexSantos的answer

答案 5 :(得分:0)

可以在一个查询中执行此操作,首先为每个不同的id获取不同的pdf_sha256作为子查询,然后在查询中通过传递子查询获取该组ID中的元素如下:

def unique_disclosures_by_pdf_sha256(company)
  subquery = company.disclosures.select('MIN(id) as id').group(:pdf_sha256)
  company.disclosures.where(id: subquery)
    .or(company.disclosures.where(pdf_sha256: nil))
end

关于这一点的好处是ActiveRecord是延迟加载的,因此第一个subquery将不会运行并将合并到第二个主查询以在数据库中创建单个查询。然后,它将按disclosures以及pdf_sha256设置为pdf_sha256的所有nil检索所有SELECT "disclosures".* FROM "disclosures" WHERE ( "disclosures"."company_id" = $1 AND "disclosures"."id" IN ( SELECT MAX(id) as id FROM "disclosures" WHERE "disclosures"."company_id" = $2 GROUP BY "disclosures"."pdf_sha256" ) OR "disclosures"."company_id" = $3 AND "disclosures"."pdf_sha256" IS NULL ) 唯一身份。

如果你好奇,给定公司,结果查询将是:

id

这个解决方案的好处是返回的值是一个ActiveRecord查询,所以在你真正需要之前它不会被加载。您还可以使用它来保持链接查询。例如,您只能选择unique_disclosures_by_pdf_sha256(company).select(:id).limit(10).each { |d| puts d } 而不是整个模型,并限制数据库返回的结果数量:

if