如何在太阳黑子中动态构建搜索块?

时间:2012-02-02 20:03:44

标签: sunspot sunspot-rails

我正在使用acts_as_solr将Rails应用转换为太阳黑子。

该应用程序使用在acts_as_solr中公开的solr中的字段搜索功能。你可以给它一个像这样的查询字符串:

title:"The thing to search"

它将在标题字段中搜索该字符串。

在转换为太阳黑子时,我正在解析查询字符串的字段特定部分,我需要动态生成搜索块。像这样:

Sunspot.search(table_clazz) do
  keywords(first_string, :fields => :title)
  keywords(second_string, :fields => :description)

  ...
  paginate(:page => page, :per_page => per_page)      
end

如果查询需要持续时间(秒,整数)范围和否定,这很复杂。

在当前系统中,用户可以搜索标题中的内容,不包括其他字段中包含其他内容的记录,并按持续时间进行查找。

简而言之,如何动态生成这些块?

3 个答案:

答案 0 :(得分:4)

我最近使用instance_eval在太阳黑子搜索块的上下文中评估了procs(在别处创建)。

优点是这些过程可以在应用程序的任何地方创建,但您可以使用与在太阳黑子搜索块中相同的语法编写它们

以下是一个简单的示例,帮助您了解特定情况:

def build_sunspot_query(conditions)
  condition_procs = conditions.map{|c| build_condition c}

  Sunspot.search(table_clazz) do
    condition_procs.each{|c| instance_eval &c}

    paginate(:page => page, :per_page => per_page)
  end
end

def build_condition(condition)
  Proc.new do
    # write this code as if it was inside the sunspot search block

    keywords condition['words'], :fields => condition[:field].to_sym
  end
end

conditions = [{words: "tasty pizza", field: "title"},
              {words: "cheap",       field: "description"}]

build_sunspot_query conditions

顺便说一句,如果你需要,你甚至可以在另一个proc中使用instance_eval一个proc(在我的例子中,我组成了任意嵌套的'和'/'或'条件)。

答案 1 :(得分:2)

Sunspot提供了一个名为Sunspot.new_search的方法,它允许您逐步构建搜索条件并按需执行。

Sunspot's source code提供的示例:

search = Sunspot.new_search do
  with(:blog_id, 1)
end
search.build do
  keywords('some keywords')
end
search.build do
  order_by(:published_at, :desc)
end
search.execute

# This is equivalent to:
Sunspot.search do
  with(:blog_id, 1)
  keywords('some keywords')
  order_by(:published_at, :desc)
end

凭借这种灵活性,您应该能够动态构建查询。此外,您可以为方法提取常见条件,如下所示:

def blog_facets
  lambda { |s|
    s.facet(:published_year)
    s.facet(:author)
  }
end

search = Sunspot.new_search(Blog)
search.build(&blog_facets)
search.execute

答案 2 :(得分:1)

我自己解决了这个问题。我使用的解决方案是将所需的范围编译为字符串,将它们连接起来,然后在搜索块内进行评估。

这需要一个单独的查询构建器库来询问solr索引,以确保不为不存在的索引字段创建范围。

代码对我的项目非常具体,而且发布的时间太长,但这就是我的工作:

<强> 1。拆分搜索字词

这给了我一系列术语或术语加上字段:

['field:term', 'non field terms']

<强> 2。这将传递给查询构建器。

构建器根据可用的索引将数组转换为范围。此方法是一个示例,它接受模型类,字段和值,并在索引字段时返回范围。

def convert_text_query_to_search_scope(model_clazz, field, value)
  if field_is_indexed?(model_clazz, field)
    escaped_value = value.gsub(/'/, "\\\\'")
    "keywords('#{escaped_value}', :fields => [:#{field}])"
  else
    ""
  end
end

第3。加入所有范围

生成的范围已加入join("\n"),即eval ed。

此方法允许用户选择他们想要搜索的模型,并可选择进行特定于字段的搜索。然后,系统将仅搜索具有任何指定字段(或公共字段)的模型,忽略其余字段。

检查字段是否已编入索引的方法是:

# based on http://blog.locomotivellc.com/post/6321969631/sunspot-introspection
def field_is_indexed?(model_clazz, field)
  # first part returns an array of all indexed fields - text and other types - plus ':class'
  Sunspot::Setup.for(model_clazz).all_field_factories.map(&:name).include?(field.to_sym)
end

如果有人需要,请检查可排序性:

def field_is_sortable?(classes_to_check, field)
  if field.present?
    classes_to_check.each do |table_clazz|
      return false if ! Sunspot::Setup.for(table_clazz).field_factories.map(&:name).include?(field.to_sym)
    end
    return true
  end
  false
end