【问题标题】:Make an OR with ransack用洗劫做一个 OR
【发布时间】:2012-10-17 18:12:09
【问题描述】:

一个基本问题,但我在project pagewiki 中找不到明确的内容。我有以下代码:

field = "secre"
Position.search( {:description_cont => field, :code_cont => field}).result(:distinct => true).to_sql
 => "SELECT DISTINCT `positions`.* FROM `positions`  WHERE ((`positions`.`description` LIKE '%secre%' AND `positions`.`code` LIKE 0))"

但我的查询应该是这样的:

 => "SELECT DISTINCT `positions`.* FROM `positions`  WHERE ((`positions`.`description` LIKE '%secre%' OR `positions`.`code` LIKE 0))"

任何帮助将不胜感激。提前致谢

【问题讨论】:

  • 您是否尝试过使用方括号 [] 而不是哈希 {} 语法?
  • @MurifoX 该方法不接受数组

标签: ruby-on-rails ransack


【解决方案1】:

尝试以下方法:

Position.search( {:description_or_code_cont => field}).result(:distinct => true).to_sql

【讨论】:

  • +1 这是原始问题的答案。另外,为了对 has_x 关系的属性进行 OR,语法并不是很明显,所以我把它放在这里是为了后代。假设 Teacher has_one Syllabus 具有属性 foobarTeacher.search( :syllabus_foo_or_syllabus_bar_eq => "val" )
【解决方案2】:

这可能不是您问题的确切答案,但因为我自己在 Ransack 中搜索了正确的方法来进行 OR 搜索,但没有找到好的答案,但确实自己解决了问题,所以我我想我会分享解决方案。

在我正在处理的一个应用程序中,有一个搜索页面将模型Customer 的各个字段作为参数(链接到数据库表customers),然后列出与搜索结果匹配的这些客户的表.客户拥有三个不同的电话号码字段,并且之前的搜索页面为每种类型的号码提供了不同的搜索字段。我想将它们合并到一个字段中,以便在所有数字中方便地搜索。

数据库有这三个字段定义(在这些sn-ps中,我只更改了一些标识符名称):

mysql> desc customers;
+--------------------------+------------------+------+-----+---------+----------------+
| Field                    | Type             | Null | Key | Default | Extra          |
+--------------------------+------------------+------+-----+---------+----------------+
...
| customer_phone_a         | varchar(50)      | YES  |     | NULL    |                |
| customer_phone_b         | varchar(50)      | YES  |     | NULL    |                |
| customer_phone_c         | varchar(50)      | YES  |     | NULL    |                |
+--------------------------+------------------+------+-----+---------+----------------+

以前,搜索页面(文件app/views/customers/index.html.erb)包含以下内容:

<%= search_form_for @search do |f| %> <!-- this is a Ransack-provided form -->
  <div class="field">
...
    <%= f.label :customer_phone_a_spaces_match_anything, "Phone number A is or contains:" %>
    <%= f.text_field :customer_phone_a_spaces_match_anything %>

    <%= f.label :customer_phone_b_spaces_match_anything, "Phone number B is or contains:" %>
    <%= f.text_field :customer_phone_b_spaces_match_anything %>

    <%= f.label :customer_phone_c_spaces_match_anything, "Phone number C is or contains:" %>
    <%= f.text_field :customer_phone_c_spaces_match_anything %>
...
  </div>

  <div class="actions">
    <%= f.submit "Search", class: "btn btn-large btn-primary" %>
  </div>

<% end %> <!-- search_form_for -->

(这与现在无关,但文件config/initializers/ransack.rb的内容是:

Ransack.configure do |config|

  config.add_predicate 'spaces_match_anything',
  :arel_predicate => 'matches', # so we can use the SQL wildcard "%"
  # Format the incoming value: add the SQL wildcard character "%" to the beginning and the end of
  # the string, replace spaces by "%", and replace multiple occurrences of "%" by a single "%".
  :formatter => proc {|v| ("%"+v.gsub(" ", "%")+"%").squeeze("%")}

end

这意味着除了 Ransack 的默认 eqcont 等之外,我还可以在搜索中使用我的自定义谓词 spaces_match_anything。谓词做它所说的。)

无论如何,从您所做的同一个示例中获得灵感,我将以下 ransacker 添加到模型 app/models/customer.rb

ransacker :all_phones do |parent|
  Arel::Nodes::InfixOperation.new('||',
    Arel::Nodes::InfixOperation.new('||',
      Arel::Nodes::InfixOperation.new('||', parent.table[:customer_phone_a]||"", ' '),
                                    parent.table[:customer_phone_b]||"", ' '),
                                  parent.table[:customer_phone_c]||"")
end

最后,我将搜索页面中的三个电话号码搜索字段替换为:

<%= f.label :customer_phone_a_or_customer_phone_b_or_customer_phone_c_cont, "Phone number is or contains:" %>
<%= f.text_field :customer_phone_a_or_customer_phone_b_or_customer_phone_c_cont %>

用户在此搜索字段中输入的数字现在将匹配客户的三个数字中的任何一个。 (注意ransacker 中对空号||"" 的防范。)

经过测试,它可以与 Ruby v1.9.3 和 Rails v3.2.8 一起使用。输入参数不会匹配一个数字的结尾和下一个数字的开头,即使在正确的地方输入了空格,也不会在搜索字段代码中,我将_cont替换为_spaces_match_anything .

【讨论】:

  • Teemu,经过数小时试图让类似的事情发挥作用,你的回答救了我!!!!谢谢!
【解决方案3】:

将其放在您的 search_form_for 标记下。这是假设您使用的是 f:

<%= f.combinator_select %>

它会生成一个带有两个选项的选择。全部或任何。 ANY 将使用 OR 子句。 ALL 将使用 AND 子句。

【讨论】:

    【解决方案4】:

    现在我们可以通过 ransack 非常简单地使用 or-query:

    Position.ransack(description: 'secret').result.or(Position.ransack(code: 0).result)
    

    【讨论】:

    • 我很高兴过去 7 年的洗劫情况有所改善 :)
    【解决方案5】:

    在对Ransack demo代码进行了一段时间的挖掘之后,我做了如下:

    field = "secre"
    q= {
      "m"=>"or", 
      "c"=>{
        "0"=>{
          "a"=>{
            "0"=>{
              "name"=>"description"
            }
          }, 
          "p"=>"cont", 
          "v"=>{
            "0"=>{
              "value"=>field
            }
          }
        }, 
        "1"=>{
          "a"=>{
            "0"=>{
              "name"=>"code"
            }
          }, 
          "p"=>"cont", 
          "v"=>{
            "0"=>{
              "value"=>field
            }
          }
        }
      }
    }
    Position.search(q).result(:distinct => true).to_sql
     => "SELECT DISTINCT `positions`.* FROM `positions`  WHERE ((`positions`.`description` LIKE '%secre%' OR `positions`.`code` LIKE 0))"
    

    是的,这很糟糕,当然这不应该是最好的方法,但它暂时救了我。还有其他想法吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多