How to dynamically build a search block in a sunspot?

I am converting a Rails application from using acts_as_solr to a sunspot.

The application takes advantage of the field search feature in solr, which was introduced in acts_as_solr. You could give it a query string, for example:

title:"The thing to search"

and will look for this line in the header field.

When converting to a sunspot, I analyze the field-specific parts of the query string, and I need to dynamically generate a search block. Like that:

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

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

This is complicated by the fact that you also need to do duration intervals (seconds, integers) and negation if requested by the request.

In the current system, users can search for something in the header, excluding entries with something else in another field and viewing by duration.

In a nutshell, how can I generate these blocks dynamically?

+6
source share
3 answers

I decided it myself. The solution I used was to compile the required scopes as strings, concatenate them, and then check them inside the search block.

This required a separate query builder library that queries solr indexes to ensure that the region is not created for a nonexistent index field.

, , :

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

, instance_eval procs ( ) Sunspot.

, 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 proc ( - / ).

+4

Sunspot Sunspot.new_search, .

Example provided by Sunspot 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

Thanks to this flexibility, you can dynamically build a query. In addition, you can extract general conditions for the method, for example:

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
source

All Articles