ActiveRecord OR查询与SQL中的两个范围,其中一个范围有一个连接

问题描述:

我想编写一个查询,结合两个范围与“OR”查询(使用Rails 4,虽然问题仍然存在与rails 5)。ActiveRecord OR查询与SQL中的两个范围,其中一个范围有一个连接

型号1

scope :association_has_email, -> { joins(:model2).where.not(model2s:{email:nil}) } 
scope :description_has_email, -> { where("description ~* ?", email_regex) } 

(其中email_regex是一个正则表达式挑选出一个邮件)。

这给了我们SQL LIKE:

SELECT \"model1s\".* FROM \"model1s\" WHERE (description ~* '[[:<:]][A-Z0-9._%+-][email protected][A-Z0-9.-]+.[A-Z]{2,4}[[:>:]]') 
SELECT \"model1s\".* FROM \"model1s\" INNER JOIN \"model2s\" ON \"model2s\".\"id\" = \"model1s\".\"model2_id\" WHERE (\"model2s\".\"email\" IS NOT NULL) 

创建范围,挑选出这些机会,其中任一电子邮件是在联想或在文本嵌入的电子邮件。

如何编写一个“OR”查询,其中一方需要连接而另一方不需要?

+0

请参阅http://*.com/questions/31096009/activerecord-or-query-hash-notation –

+1

使用where-or gem(这是辉煌的......)你会得到一个“ArgumentError:传递给#or的关系必须在结构上兼容“ 但是,如果您向范围传递连接,则会出现问题,如果email_in_description根本不包含model2,则将其排除。 但是,我认为我有一个解决方案。可以通过包括获得一个外部连接... – Carpela

+0

我总是建议人们使用原始SQL,其中ActiveRecord DSL无法成为一个好工具。 – MurifoX

确定。 对于导轨4,在那里,或宝石提供一种用于产生或查询,在导轨5的修复,但是如果传递的范围你上面结束了一个

ArgumentError: Relation passed to #or must be structurally compatible 

可以使用以下:

scope :email_somewhere, -> { association_has_email.or(Model1.joins(:model2).description_has_email } 

SELECT \"model1s\".* FROM \"model1s\" INNER JOIN \"model2s\" ON \"model2s\".\"id\" = \"model1s\".\"model2_id\" WHERE ((\"model2s\".\"email\" IS NOT NULL) OR (description ~* '[[:<:]][A-Z0-9._%+-][email protected][A-Z0-9.-]+.[A-Z]{2,4}[[:>:]]')) 

工程,但它会排除任何在描述中有电子邮件但没有model2的东西,因为它使用内部连接。

但是通过使用includes,您可以获得所需的结果。

scope :association_has_email, -> { includes(:model2).where.not(model2s:{email:nil}) } 
scope :description_has_email, -> { where("description ~* ?", email_regex) } 

意味着你可以使用

scope :email_somewhere, -> { association_has_email.or(Model1.includes(:model2).description_has_email } 

的区别是SQL拉所有的属性,从模型1,然后将左外连接的MODEL2查询。

SELECT [...all the model1 attributes...] LEFT OUTER JOIN \"model2s\" ON \"model2s\".\"id\" = \"model1s\".\"model2_id\" WHERE ((\"model2s\".\"email\" IS NOT NULL) OR (description ~* '[[:<:]][A-Z0-9._%+-][email protected][A-Z0-9.-]+.[A-Z]{2,4}[[:>:]]')) 

刺激,如果你真的需要使用内部连接的第一个,但我的作品。

您可以创建一个更大的空间像以下:

scope :check_email, -> { 
    if email is embedded 
    association_has_email 
    else 
    description_has_email 
    end 
} 
+0

什么是“嵌入”在这里? – Carpela

+0

这只是为了检查你要搜索的邮件地址 –

+0

有点困惑。我们要做的是查明我们的某个记录是否有任何类型的电子邮件。我们正在设法确定我们是否有直接沟通的手段。即我们并不知道我们是否有电子邮件,这是范围试图过滤的内容) – Carpela