ActiveRecord OR查询SQL中的两个作用域,其中一个作用域具有连接

时间:2017-02-09 12:50:06

标签: sql ruby-on-rails activerecord

我想写一个查询,它将两个范围与" OR"查询(使用Rails 4虽然问题仍然存在于rails 5中)。

型号1

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

(其中email_regex是挑选电子邮件的正则表达式)。

这为我们提供了如下SQL:

SELECT \"model1s\".* FROM \"model1s\" WHERE (description ~* '[[:<:]][A-Z0-9._%+-]+@[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)

创建一个范围,以选择电子邮件位于关联中的机会或文本中嵌入的电子邮件。

你如何写一个&#34; OR&#34;查询一方需要加入而另一方不需要?

2 个答案:

答案 0 :(得分:0)

您可以再创建一个范围,如下所示:

Caused by: org.springframework.context.NoSuchMessageException: No message found under code 'Pattern.book.name' for locale 'en'.
    at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:84)
    at org.springframework.context.support.AbstractApplicationContext.getMessage(AbstractApplicationContext.java:1264)
    at org.springframework.web.servlet.support.RequestContext.getMessage(RequestContext.java:733)
    at org.springframework.web.servlet.support.BindStatus.initErrorMessages(BindStatus.java:181)
    at org.springframework.web.servlet.support.BindStatus.getErrorMessages(BindStatus.java:277)
    at org.thymeleaf.spring4.processor.attr.SpringErrorsAttrProcessor.processAttribute(SpringErrorsAttrProcessor.java:82)
    at org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
    at org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
    ... 119 more

答案 1 :(得分:0)

确定。 对于Rails 4,where-or gem提供了生成或查询的修复,如rails 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._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}[[:>:]]'))

可以使用,但它会排除描述中包含电子邮件但没有model2的任何内容,因为它使用内部联接。

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

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从model1中提取所有属性,然后为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._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}[[:>:]]'))

如果你真的需要为第一个使用内部联接,那就会激怒,但对我有效。