【问题标题】:Rails comma separated search with scopesRails 逗号分隔搜索与范围
【发布时间】:2014-11-24 09:58:58
【问题描述】:

我希望我的搜索允许多个输入。我的模型中有一个范围:

scope :by_description, lambda { |description| where('description LIKE ?', "%#{description}%") unless description.nil? }

目前,如果我搜索“abc, efg”,它会寻找那个确切的字符串。如何修改我的范围以允许“abc, efg”搜索描述字段中包含“abc”或“efg”的任何记录?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 search sqlite


    【解决方案1】:

    这样的事情(基于其他答案):

      scope :by_description, ->(desc=nil) {
        if desc.blank?
          all
        else
          terms = desc.split(/\s*,\s*/).map { |t| t.strip }.map { |t| "%#{t}%" }
          where( ( ["#{table_name}.description like ?"] * terms.count).join(' or '), *terms )
        end
      }
    

    【讨论】:

    • 编辑后现在是正确的。基本概念是用逗号分割字符串,删除多余的空格,并将每个术语包装在“%”中。然后,根据在每个项目之间使用“或”的术语数量生成 where 子句。如果传入的描述是空白的,它只会返回“all”,但你可以把它变成你想要的任何东西。
    • 谢谢!我不熟悉 ->(desc=nil) 语法。那到底是在做什么?
    • 唯一的问题是我将每个搜索字段的一堆范围串在一起,如果我将描述留空,它会抛出一个 no method 错误,说它找不到下一个范围方法。任何想法为什么会发生?
    • 如果 desc 为 nil/blank,可能只需要将其更改为什么都不做。至于“->(desc=nil)”,如果没有传入任何内容,它只会导致“desc”默认为“nil”。您可能希望摆脱“=nil”以强制传入一个值。您可能希望让您的控制器检查以确保用户在执行此搜索之前确实在框中输入了某些内容。
    • @sabrams 啊,对不起。在 rails 4 中,all 的功能更改为返回可作用域的对象。我应该多注意问题标签^^"
    【解决方案2】:

    这是基于托德的回答:

    scope :by_description, ->(desc=nil) {
        if desc.present?
            desc.split(",").map(&:strip).inject(self) do |memo, term|
                memo.where("#{table_name}.description LIKE ?", "%#{term}%")
            end
        else
            all # or none, if you want no results returned      
        end
    }
    

    逻辑上基本相同,但我使用了 stabby 语法,并确保作用域总是返回可链接的东西。为了安全起见,我还希望将 table_name 添加到手动构建的查询中,以防止不明确的表错误(当 2 个或更多表具有描述字段时)。

    【讨论】:

    • 我已经尝试了这两个答案并且它们都运行了,但是无论输入如何都没有给出任何结果,如果我将输入留空,则会引发错误。
    【解决方案3】:

    试试这个:

    scope :by_description, lambda { |description|
        description.split(",").
        map(&:strip).
        inject(self) { |memo, term|
            memo.where("description like ?", term)
        }
        unless description.nil?
    }
    

    这是做什么的:

    • 获取传入的字符串并将其拆分为逗号分隔的字符串数组
    • 在每个字符串上调用 strip 以删除前导和尾随空格
    • 使用injectwhere 子句的集合链接在一起,每个子句对应数组中的每个搜索词

    我还没有真正尝试过,所以它可能需要一些调整。但这种方法是合理的。关键是您需要多个 WHERE 子句来获得您正在寻找的“和”行为。

    【讨论】:

    • 好的,所以我相信我说错了。我想搜索“abc”或“efg”并找到任何包含这些字符串的记录。此外,它不喜欢你有两组花括号和除非语句。如果我去掉了 unless 语句,则没有错误,但如果它保持原样,则会引发错误。
    • 还有几个问题:我了解 map() 但不了解 &:strip 语法。我也对注入语句感到困惑。备忘录是否只是数组的位置,而术语是位于该位置的字符串?抱歉所有问题,我只是想确保我理解所有代码,所以我正在学习,而不仅仅是粘贴。
    • map(&:strip) 等价于 map { |s|条带}。它将给定的方法应用于数组中的每个项目。
    • 如果你想要OR 而不是AND,代码看起来有点不同——你将有一个带有可变数量的WHERE 子句(一个用于每个搜索词),例如where("description like ? or ? or ?", *terms)。并注意使用 splat 运算符 (*) 将术语数组转换为形式参数列表。
    • 注入语句令人困惑。这是一种将提供的块 (memo.where...) 的结果链接在一起的方法,为术语数组中的每个参数调用一次。但是,如果您想要 OR 行为,正如您现在所指出的那样,无论如何您都不会使用注入。
    猜你喜欢
    • 1970-01-01
    • 2020-02-11
    • 2018-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多