Topic: SOLVED: complex meta_search problem

Hi,

I just implemented meta_search to create an advanced search on a model and its relations.

One relation search is rather puzzling me and I was wondering if it is possible to implement the search I want:

I have a parent model which has many children:

class Workingwell < ActiveRecord::Base
  has_many :analytical_results, :dependent => :destroy
end

class AnalyticalResult < ActiveRecord::Base
  belongs_to :workingwell
end

Analytical results is used by an external program to save key-value pairs related to a workingwell via workingwell_id

e.g.

:name => 'efficiency',  :value => '0.5', :workingwell_id => '1'
:name => 'level',  :value => '1', :workingwell_id => '1'

What I would like to do is the following:

1. I want to list all existing analytical_results name fields as label for a multiparameter_field (AnalyticalResult.uniq.collect{|x| x.name})
2. let the user search the values on a per name base

So in my example I would like a multiparameter_field for efficiency and level and then the search would return all workingwells that fit the search parameters for either one of them.

So instead of searching the all values of analytical_results I would like to search the values of those records where the name is set to either efficiency or level, respectively.

So something like this:

<%= f.label :analytical_results_value.where(:name => "efficiency"), "Efficiency"
<%= f.multiparameter_field :analytical_results_value.where(:name => "efficiency"),
{:field_type => :text_field}, {:field_type => :text_field}, :size => 5 %>

Reading up on the documentation I read about custom "Wheres". Unfortunately the name of analytical results are completely custom, meaning I dont know about them as they are created dynamically as users see fit.

Is there a way to do what I would like to do using meta_search?

Thanks Juergen

Last edited by helmerj (2012-07-02 15:04:34)

Re: SOLVED: complex meta_search problem

Hi,

I just played in the rails console and figured out to do what I want:

AnalyticalResult.search :value_contains => "0.7", :name_equals => "test"

So I just have to figure out a way to get that into my view... :-)

J

Re: SOLVED: complex meta_search problem

Hi

unfortunately the simple addition of the extra condition does not work:

    %dl.search
      %dt
        = f.label :analytical_results_value_contains, "test"
      %dd
        = f.text_field :analytical_results_value_contains, :analytical_results_name_equals => "test"

Still searches all analytical result records and not only the ones where the name is set to test... :-(

J

Last edited by helmerj (2012-06-14 10:17:12)

Re: SOLVED: complex meta_search problem

I have solved the problem of searching my child records.

I wrote a custom search method for meta_search:

scope :with_name_and_value_range,lambda {|name, low, high|
    joins(:analytical_results).where(['analytical_results.name = ?', name]).with_low(low).with_high(high) }

  scope :with_name,lambda {|name|
    joins(:analytical_results).where(['analytical_results.name = ?', name])}
  scope :with_low,lambda {|low|
    joins(:analytical_results).where(['analytical_results.value >= ?', low]) unless low.blank?}
  scope :with_high,lambda {|high|
    joins(:analytical_results).where(['analytical_results.value <= ?', high]) unless high.blank?}

  search_methods :with_name_and_value_range,
    :splat_param => true, :type => [:string, :string, :string]

Then in my view I had:

-if @analytical_results.present?
      %h4.search
        Analytical Results
      %dl.search
        - count = 1
        -@analytical_results.each do |result|
          %dt
            = f.label :with_name_and_value_range, result.name
          %dd
            = f.multiparameter_field :with_name_and_value_range,
            {:field_type => :hidden_field, :value => "#{result.name}", :id => "search_with_name_and_value_range(#{count})", :name => "search[with_name_and_value_range(#{count})]"},
            {:field_type => :text_field, :placeholder => "from", :class => "small_field",:id => "search_with_name_and_value_range(#{count + 1})",:value => params[:search]["with_name_and_value_range(#{count +1})"],:name => "search[with_name_and_value_range(#{count + 1})]"},
            {:field_type => :text_field, :placeholder => "to", :class => "small_field",:value => params[:search]["with_name_and_value_range(#{count +2})"],:id => "search_with_name_and_value_range(#{count + 2})", :name => "search[with_name_and_value_range(#{count + 2})]"}
            - count += 3

This would create a label, a hidden_field for the name, and a low and high value text field. With a single type of analytical result present this worked like a charm. An array of three values would be passed to the lambda block and evaluated in my custom search method.

BUT:

With additional analytical_results type present, the array became longer by three values for each type, e.g. for two :

[type1, low1, high1, type2,low2,high2]

So I got an error that I did provide 6 parameters when only 3 where allowed.

I could not be bothered to rewrite the meta_search gem so I created a work around in my workingwell controller. Basically I check the params[:search] hash if it contains any keys where the name contains "with_name_and_value_range" indicating my custom search method.  I extract the key value pairs from the params[:search] hash into a new hash called "new" and delete the key value pairs from the params[:search] hash so they do not become evaluated when calling

search(params[:search])

I initialize the search instance variable with all activewells. In case the new hash does exist and is longer than 0 I slice it by 3 into single value triplets (name, low,high). I loop over them and use them to restrict the @search variable. I merge the new and params[:search] hash so I can initialize the search form once the search is submitted with the values for my custom search method fields.

Works like a charm...

def index
    @analytical_results = AnalyticalResult.select(:name).uniq if AnalyticalResult.all.length > 0
    new = {}                                        # instantiate a new has to hold extra values
    if params[:search].present?
      params[:search].each do |key, value|            # loop over the params[:search] hash
        if key.include? "with_name_and_value_range"   # check if the key contains "with_name_and_value_range"
          new[key] = value                            # if yes add the new key-value pair to the new hash
          params[:search].delete(key)                 # remove the key-value pair from the params[:search] hash
        end
      end
    else
      params[:search] = {}
    end
    @search = Workingwell.where("workingwells.name != ? AND workingwells.name != ?", "PBS", "empty")
    if new.length > 0
      new_a = new.to_a.each_slice(3).to_a                     # convert new has into an 2d array
      count = 0
      while count < new_a.length
        if new_a[count][1][1] != ""
          @search = @search.joins(:analytical_results).where("analytical_results.name = ? AND analytical_results.value >= ?", "#{new_a[count][0][1]}", "#{new_a[count][1][1]}")
        end
        if new_a[count][2][1] != ""
          @search = @search.joins(:analytical_results).where("analytical_results.name = ? AND analytical_results.value <= ?", "#{new_a[count][0][1]}", "#{new_a[count][2][1]}")
        end
        count += 1
      end
    end
    @search = @search.search(params[:search])
    if new.length > 0
      params[:search] = params[:search].merge(new)
    end
    @workingwells = @search.paginate(:page => params[:page])

  end

Last edited by helmerj (2012-07-02 15:25:36)