Search code examples
mysqlruby-on-railsrubysql-like

Rails SQL query on unknown (dynamic) number of queries using LIKE


I have a Rails search form that performs a dynamic query. That is, the user enters a search like:

(hobby="skiing") OR (gender="male" AND hobby="jogging") 

So, I don't know how many queries I will be searching by until runtime.

I parse the search query by converting it into a valid SQL query, so the above search would be converted to the following format:

query = "hobby LIKE ? OR (gender LIKE ? AND hobby LIKE ?)"
queries = ["skiing", "male", "jogging"]

For the following query:

where(query, queries)

However, the general syntax of the Rails search query is very limiting:

where(query, query_1, query_2, query_3)

I cannot replace the 'query_n' arguments with an array like I want to, without Rails throwing an error.

NoMethodError (undefined method `where' for ["a", "b", "c"]:Array)

Attempting to splat the array yields the same error:

where(query, *queries)

Again:

NoMethodError (undefined method `where' for ["a", "b", "c"]:Array)

So what can I do?

EDIT:

The full search function looks like this:

def self.search(search)
    query = "%#{search}%"
    if search
        includes(:games, :jobs)
        strngs = ["hobby = ? OR name = ? OR gender = ?", "dsfgdsfg", "dsgsdfgsd", "sdfsfsdf"]
        .where(strngs)

Solution

  • What you'll want to do is pass an array as a single argument to where which contains both the query AND the dynamic values. For example:

    where(["att_1 LIKE ? OR att_2 LIKE ?", "value1", "value2"])
    

    If an array is passed as the first and only argument, then the first element of the array is treated as a template. The following array values are treated as the dynamic values for the query template.

    For your example, instead of having two separate variables queries and query, combine them into one query variable:

    # A single array with the query AND values
    query = ["hobby LIKE ? OR (gender LIKE ? AND hobby LIKE ?)", "skiing", "male", "jogging"]
    
    # Run the `where` with a single array as the argument
    YourModel.where(query)
    

    This will allow you to query the DB with an unknown number of values using LIKE.

    For reference: Rails where() docs