Rails通过AND比较对搜索结果进行排序,然后进行OR比较

时间:2014-09-30 16:51:27

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

使用RoR 3.2,我搜索搜索逗号分隔的关键字;例如“apple,orange”会找到任何包含“apple”或“orange”字样的记录。我希望能够对结果进行排序,以便首先显示包含两个单词的任何记录。例如

搜索“Apple,Orange”会显示记录

“橙片和苹果派”

记录之前

“Apple Juice”

即使首先找到Apple Juice。如何在按字母数字顺序排序之前添加此第一级排序?我想我可以在控制器索引操作中通过设置索引搜索的顺序来首先显示具有“apple”和“orange”的任何记录,然后显示具有“apple”或“orange”的所有其他记录,我只是不知道怎么做这样的事情。

以下是搜索一组逗号分隔值的范围:

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
}

我在控制器中调用范围是这样的:

def index
  order = sortable_column_order
if params[:drawings].blank?
   @drawings = Drawing.paginate page: params[:page], order: order, per_page: 100
else
  @search_string = params[:drawings][:description]
  @drawings = Drawing.by_description(params[:drawings][:description])\
  .by_drawing_number(params[:drawings][:drawing_number])\
  .by_item_number(params[:drawings][:item_number])\
  .by_pump_model(params[:drawings][:pump_model])\
  .by_frame_size(params[:drawings][:frame_size])\
  .by_part_type(params[:drawings][:part_type])\
  .by_created_before(params[:drawings]['created_before(1i)'], params[:drawings]['created_before(2i)'], params[:drawings]['created_before(3i)'])\
  .by_created_after(params[:drawings]['created_after(1i)'], params[:drawings]['created_after(2i)'], params[:drawings]['created_after(3i)'])\
  .paginate page: params[:page], order: order, per_page: 100
  @drawings = @drawings.sort_data(@search_string)

end

这就是我正在尝试将它放在一起用于排序方法,但它还没有用,我不确定如何正确地拉入搜索结果并使用它们。 (我正在使用handles_sortable_columns gem。)

def sort_data(search_string)
    results_array =
    search_array = search_string.gsub(/\s+/, "").split(",")
    result = []
    results_array.each do |string|
      every_word = true
      one_word = false

      search_array.each do |search|
        if string.downcase.include? search.downcase
            one_word = true
        else
            every_word = false
        end
      end
      if every_word
        result.unshift(string)
      elsif one_word
        result << string
      end
    end
    return result
  end

2 个答案:

答案 0 :(得分:1)

我假设您有一个要搜索的输入字符串数组,并以逗号分隔格式在另一个字符串中获取搜索参数。请找到以下代码。

strings = ["Orange slices and apple pie" , "apple juice", "orange juice", "jackfruit juice", "mixed fruit", "apple pie and orange juice"]
search_string = "apple , orange"
search_array = search_string.gsub(/\s+/, "").split(",")
result = []
strings.each do |string|
    every_word = true
    one_word = false

    search_array.each do |search|
        if string.downcase.include? search.downcase
            one_word = true
        else
            every_word = false
        end
    end
    if every_word
        result.unshift(string)
    elsif one_word
        result << string
    end
end

puts result.inspect

<强>结果

["apple pie and orange juice", "Orange slices and apple pie", "apple juice", "orange juice"]

希望它有所帮助: - )

答案 1 :(得分:1)

您可以通过两种不同的方法完成此操作。一个在 Ruby 空间,一个在 SQL 空间。

红宝石

您可以实现一个继承自String的新类,并使用<=>方法搞砸

class FunkyString < String
  def initialize(literal, words = [])
    @words = words
    super(literal)
  end

  def <=>(other)
    if funky_score != other.funky_score
      result = other.funky_score <=> funky_score
    else
      result = super(other)     # fallback to classic string sort
    end
  end

  def funky_score
    @words.map{ |word| self.include?(word) ? 1 : 0 }.inject(:+)
  end
end

所以对于 Saurabh Lodha 提出的字符串:

strings = ["Orange slices and apple pie" , "apple juice", "orange juice", 
           "jackfruit juice", "mixed fruit", "apple pie and orange juice"]

strings.shuffle.map {|s| FunkyString.new(s, ['apple', 'orange']) }.sort

#=> ["apple pie and orange juice", "Orange slices and apple pie", "apple juice",
#    "orange juice", "jackfruit juice", "mixed fruit"]

将此功能集成到drawing模型的一种方法是

class Drawing < ActiveRecord::Base

  def self.my_weird_sort(keywords = [])
    sort_by { |record| record.funky_string_field(keywords) }
  end

  def funky_string_field(keywords = [])
    FunkyString.new(string_field, keywords)
  end
end

SQL

select string_field, IF(string_field LIKE '%apple%', 1, 0) + IF(string_field LIKE '%orange%', 1, 0) AS the_score from test_tab order by the_score desc, string_field asc;

导致:

+-----------------------------+-----------+
| string_field                | the_score |
+-----------------------------+-----------+
| apple pie and orange juice  |         2 |
| Orange slices and apple pie |         2 |
| apple juice                 |         1 |
| orange juice                |         1 |
| jackfruit juice             |         0 |
| mixed fruit                 |         0 |
+-----------------------------+-----------+

这样可以转移到rails范围,如下所示:

scope :weird_order ->(word_1, word_2) { 
  select("select string_field, IF(string_field LIKE '%?%', 1, 0) + IF(string_field LIKE '%?%', 1, 0) AS the_score", word_1, word_2).
  order("he_score desc, string_field asc")
}

所以你有

@drawings.weird_order("apple", "orange")

***** 更新 ************

对于sql方法中的任意数量的元素,您可以构造查询字符串:

选择string_field,IF(string_field LIKE&#39;%apple%&#39;,1,0)+ IF(string_field LIKE &#39;%orange%&#39;,1,0)AS_score desc,test_tab命令的the_score,string_field asc;

q = "select string_field, "
q += keywords.map{|x| "IF(string_field LIKE '%" + x + "%', 1, 0)"}.join(" + ")
q += AS the_score from test_tab order by the_score desc, string_field asc"

然后执行自定义查询。请记住,如果您遵循该规则,则需要检查sql注射!