2012-02-02 65 views
5

我正在將使用acts_as_solr的Rails應用程序轉換爲太陽黑子。如何在太陽黑子中動態構建搜索塊?

該應用程序使用solr中的字段搜索功能,該功能在acts_as_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

只是一個想法:我們可以在紅寶石創建塊動態,並把它傳遞給搜索功能? – 2012-03-12 08:00:18

回答

1

我自己解決了這個問題。我使用的解決方案是將所需的作用域編譯爲字符串,連接它們,然後在搜索塊內對它們進行評估。

這需要一個單獨的查詢構建器庫,用於詢問solr索引以確保不爲不存在的索引字段創建範圍。

的代碼是非常具體的,以我的項目,而過長全面發佈,但是這是我做的:

1斯普利特搜索字詞

這給了我一個數組條款或條款加場:

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

2.這是傳遞給查詢生成器。

構建器根據可用的索引將數組轉換爲作用域。此方法是一個示例,它接受模型類,字段和值,並在字段被索引時返回範圍。

3.加入所有範圍

生成的範圍被接合join("\n")那就是eval編輯。

該方法允許用戶選擇他們想要搜索的模型,並且可選地進行特定領域的搜索。然後系統將只搜索具有任何指定字段(或公共字段)的模型,忽略其餘部分。

的方法來檢查該字段建立索引是:

# 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 

如果有人需要它,因爲sortability檢查:

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 
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 

順便說一句,如果你需要,你甚至可以instance_eval的另一個PROC內一個進程(在我的情況下,我由任意-nested「和」/「或」條件)。

+0

謝謝,這真的很有用。你如何測試生成器? – 2012-05-18 20:32:02

+0

@RichardHulse - 每個測試用例都可以構造一個條件列表並將其傳遞給查詢構建器,然後檢查結果是否如預期的那樣(給定測試數據庫中已知的一組數據)......是那種類型的東西你的意思是? – antinome 2012-05-22 17:00:41

+0

我在想更多的是孤立的,只是測試正確的特效是編譯好的,但我說我只是回答我自己的問題! – 2012-05-22 20:17:17

2

太陽黑子提供了一種名爲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 

這種靈活性,你應該能夠動態地構建查詢。此外,您還可以提取共同條件的方法,像這樣:

​​