我正在使用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
如果查询需要持续时间(秒,整数)范围和否定,这很复杂。
在当前系统中,用户可以搜索标题中的内容,不包括其他字段中包含其他内容的记录,并按持续时间进行查找。
简而言之,如何动态生成这些块?
答案 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