skip to Main Content

I’m querying a Searchkick endpoint where I want to find all results where a field is nil.

The query in the console

Product.search('*', where: { category_id: nil })

correctly returns results.

When passed from the client nil, null and all others are interpreted as strings so my query becomes

Product.search('*', where: { category_id: 'nil' })

Is there a rails way or a specific encoding to use for query strings to allow it to be correctly parsed as nil without diving into the params hash and searching for strings?

2

Answers


  1. To handle this scenario, you need to manipulate the query parameters on the server side to convert the string ‘nil’ (or ‘null’) back into a proper nil value before passing it to the Searchkick query. Here are a few approaches:

    def search
      category_id = params[:category_id]
      category_id = nil if category_id.blank? || category_id == '_nil'
      
      products = Product.search('*', where: { category_id: category_id })
      render json: products
    end
    
    Login or Signup to reply.
  2. The FormData format doesn’t actually have a (working) concept of nothing as it’s not typed and the values are just strings.

    It’s kind of implied that with formdata the client should not serialize incomplete inputs (like unchecked checkboxes).

    The closest you get is an empty string which can be sent by just omitting the RVAL.

    irb(main):001:0> Rack::Utils.parse_nested_query("foo=&bar=2&baz=3")
    => {"foo"=>"", "bar"=>"2", "baz"=>"3"}
    

    While it’s trivial to convert the empty strings into nils:

    irb(main):007:0> params = ActionController::Parameters.new(Rack::Utils.parse_nested_query("foo=&bar=2&baz=3"))
    => #<ActionController::Parameters {"foo"=>"", "bar"=>"2", "baz"=>"3"} permitted: false>
    irb(main):008:0> params.transform_values { |v| v.empty? ? nil : v }
    => #<ActionController::Parameters {"foo"=>nil, "bar"=>"2", "baz"=>"3"} permitted: false>
    

    YMMV as there are a lot of potential bugs that can occur if you equate "not filled in" with explicitly nothing.

    A better idea is to invent a token for nothing (Rails doesn’t have one) or send a POST request with a JSON payload instead of using the query string. Or you can embrace the Form Object pattern instead of just forwarding whatever the internet happens to throw at your controller into the model.

    class SearchForm
      include ActiveModel::Model
      include ActiveModel::Attributes
     
      attribute :foo
    
      def foo=(val)
        super(val.empty? ? nil : v)
      end
    end
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search