Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails + Mongoid - Don't return nil values in JSON

I have a lot of optional fields in Mongoid, like:

  field :key, type: String             
  field :element, type: String         
  field :rect, type: Array             

If I return a json of this model with only one of them filled I get null values on all the other fields. How can I remove those fields?

My model has nested attributes, which means null values can be on several levels.

Clarifications:

I need a way to remove null fields from the json representation of a model, including null fields in all nested attributes.

Code Example:

1.9.3-p0 :005 > u=Muse.new(:key=>'ram').to_json
 => "{\"_id\":\"4f1ced749c2ee4219d000003\",\"element\":null,\"key\":\"ram\",\"rect\":null}" 
like image 730
CamelCamelCamel Avatar asked Jan 04 '12 16:01

CamelCamelCamel


3 Answers

By default mongoid has the ability to remove empty fields. If you let some fields empty, mongoid will removes them on insert.

in the below example, I left out the fields element & rect

class User
  include Mongoid::Document

    field :key, type: String             
    field :element, type: String         
    field :rect, type: Array       

    embeds_one :home  
end
>> u=User.new(:key=>'ram').to_json
=> "{"_id":"4f1c3722b356f82e4a000001","_type":"key":"ram"}"

and it works perfectly. But if you put a nil value in the field

>> u=User.new(:key=>'ram',:element=>nil).to_json
=> "{"_id":"4f1c3722b356f82e4a000001","_type":"User","key":"ram","element":null}"

It gets inserted. I assume that's the exact problem in your code. So you can work around this by converting JSON hash representation using as_json and remove the nil fields

x=u.as_json.reject! {|k,v| v.nil?}
=> "{"_id":"4f1c3722b356f82e4a000001","_type":"User","key":"ram"}"

But to go to the inner levels, you cannot use as_json. check the below code

  >>h=Home.new(:address=>'xxxx',:dummy=>nil)
  >>u.home = h 
  >>x=u.as_json.reject! {|k,v| v.nil?}
  =>{"_id"=>BSON::ObjectId('4f1c39b4b356f82e4a000003'), "_type"=>"User","key":"ram","home"=>#<Home _id: 4f1c3c5db356f82e4a000004,address:'xxxx' , dummy: nil >}

Now you see the field dummy inside the embedded doc house is still with nil. so my best advice is Dont put the nil values in db at all. To do that put a before_save callback on your models (embedded too) and remove the empty fields.

Also I will show you how to remove nil fields from nested objects too. Use it if there is no other way

We can use attributes of mongoid model to get the hash representation of the object including the nested levels

x=u.attributes
=> {"_id"=>BSON::ObjectId4f1c39b4b356f82e4a000003,"key"=>"ram","element"=>nil,"home"=>{"address"=>"xxxx", "_id"=>BSON::ObjectId4f1c3c5db356f82e4a000004,"dummy"=>nil}}

and you have to find is there any Hash inside the mongoid object, if one, we have to use the reject! {|k,v| v.nil?} on that Hash too

to put together all

def to_json(obj)
     obj.reject! {|k,v| v.nil?}
     obj.find_all {|x| x[1].class==BSON::OrderedHash}.each do |arr| 
           obj[arr[0]] = to_json(arr[1])    
     end
     obj     
end

and call this with attributes of the model,

 >> to_json u.attributes
 => {"_id"=>BSON::ObjectId4f1c39b4b356f82e4a000003,"key"=>"ram","home"=>{"address"=>"xxxx", "_id"=>BSON::ObjectId4f1c3c5db356f82e4a000004}}

Thats all. Hope it helps

like image 153
RameshVel Avatar answered Oct 21 '22 11:10

RameshVel


If you are just trying to remove field keys with nil values from query results before sending JSON, say, to the browser, you can simply do this (works in my case):

doc = Model.all.entries.map {|d| d.as_document}
respond_width(doc)
like image 21
Jackpile Avatar answered Oct 21 '22 11:10

Jackpile


As far as I know, there's no such feature in the Mongoid iself. My suggestion would be to go with one of the API generators then:

http://fabrik42.github.com/acts_as_api/

https://github.com/nesquena/rabl

or

https://github.com/rails/jbuilder

like image 28
Roman Avatar answered Oct 21 '22 12:10

Roman