0

Ok, I've been trying to figure out how to approach this all morning. Let's say you have a hash that looks like this:

{"luxury_shopper"=>"10", "movie_goer"=>"80"}

and you want to build a query that looks like this:

Observation.search("something", boost_by: {biz_trv:{factor: 10}} && {day_com:{factor:100}})

My approach so far is to convert the incoming hash to look like this:

[{:luxury_shopper=>{:factor=>"10"}}, {:movie_goer=>{:factor=>"80"}}]

by iterating over the original hash like this:

params[:filter].map {|k,v| {k.to_sym => {factor:v}}}

The problem I'm having is that since the query I'm trying to build is made up of an unknown number of hashes that I will "boost_by", I can't just do something like filters[0] && filters[1] I'm stumped here. Does anyone know how I could dynamically insert hashes into my query and place the operator between them?

EDIT:

The query looks like this:

Observation.search("someting", boost_by: `insert hash` `insert operator` `insert hash` etc...)

The above query could have any number of hashes and operators. So if a user wanted to pass in 10 fields to boost_by, I need to be able to accept all 10 of those hashes and whatever operator goes between them, so for example, my query could look like either of the below examples:

Observation.search("something", boost_by: {biz_trv:{factor: 10}})

Observation.search("something", boost_by: {biz_trv:{factor: 12}} && {day_com:{factor:50}} && {location:{factor:40}})
Arel
  • 3,888
  • 6
  • 37
  • 91
  • it sounds like you just need one more step to go from your converted hash to an && of filters. Don't try and get fancy, just make it work :) – Jeff Price May 27 '15 at 16:36
  • Please provide the output expected. I am not that familiar with `searchkick` but there are many users on here that can certainly help with `Hash` manipulation if you specify input and output. – engineersmnky May 27 '15 at 19:01
  • Thanks @engineersmnky. The output needs to look like the example above `Observation.search("something", boost_by: {biz_trv:{factor: 10}} && {day_com:{factor:100}})`. The dynamic part is the part after `boost_by:`. That search produces a list of search kick results, and information about the quality of the results and other things that aren't relevant to the question. – Arel May 27 '15 at 19:13

3 Answers3

3

i have a solution that involves forming a string of your query, and then using the #send method to deliver your query string to the object.

hash = {"luxury_shopper"=>"10", "movie_goer"=>"80"}
BLANK_PARAMS = "boost_by: "
params = BLANK_PARAMS
hash.each do |k, v|
  if params == BLANK_PARAMS
    params += "{#{k}: {factor: #{v}}}"
  else
    params += " && {#{k}: {factor: #{v}}}"
  end
end

at the end of this operation, the params string will be the following:

"boost_by: {luxury_shopper: {factor: 10}} && {movie_goer: { factor: 80}}"

so if you now wanted to run the following command:

Observation.search("something", boost_by: {luxury_shopper: {factor: 10}} && {movie_goer: {factor: 80}})

you could do so using #send as such:

Observation.send('search', 'something', params)
ian root
  • 349
  • 1
  • 11
  • That's a great solution! Thanks Ian. I'll award you the bounty in an hour when it lets me. – Arel Jun 03 '15 at 15:57
  • 1
    thank, arel. also occurs to me, you may want to have some safety checks in there for bad input data or sql injection attacks. – ian root Jun 03 '15 at 18:58
1

I'm not sure if correctly understand your question, but if you need apply && between arbitrary number of elements, you may simply use Enumerable#all? method, like

filters = [{biz_trv:{factor: 10}}, {day_com:{factor:100}}, …]
Observation.search("something", boost_by: filters.all?)

Update:

filters = [{biz_trv:{factor: 12}}, {day_com:{factor:50}},
           {location:{factor:40}}]
Observation.search("something",
                   boost_by: filters.inject {|ac,e| ac && e})
David Unric
  • 7,421
  • 1
  • 37
  • 65
  • `filters.all?` just returns `true` – Arel Jun 03 '15 at 13:58
  • @Arel I'm sorry, the question was probably not clear to me. Updating my answer with more generic solution which is equivalent to your example but works for arbitrary number of `boost_by` arguments and `&&` can by freely interchanged. – David Unric Jun 03 '15 at 21:57
0

Assuming you always want to use the same operator, you want to pass the output of your map to something that behaves like inject:

(params[:filter].map {|k,v| {k.to_sym => {factor:v}}}).inject() { |r,c| r && c }

Note: not tested!

Derrell Durrett
  • 544
  • 10
  • 26