ruby-on-railsmongodbmongoidmongoid4

setting mongoid hash field values


I'm using Mongoid in a Rails project (both 4.0.x), and I've got a document with a hash field that stores some schema-less data.

class Thing
  field :name, type: String
  field :mass, type: Integer
  field :info, type: Hash
end

With this setup, I can query for things, say, that have a key :endDate like so:

Thing.where("info.endDate"=>{'$exists'=>true})

And that's all nice and handy. Using a hash field for this :info field is nice because what I want to store doesn't have a fixed schema and varies from one thing to another.

Ok, but, I can't use the same dot syntax to $set key/value pairs in the :info hash. [1]

thing.set("info.endDate"=>Time.now) 

Raises a Mongoid::Errors::UnknownAttribute error.

It tells me I'd have to include Mongoid::Attributes::Dynamic in my model to do this, but that doesn't seem right to me. The point of the hash field type seems to be to allow you to work with data that doesn't have a fixed schema. It doesn't seem like I should have to include a special "dynamic attributes" module to use hash fields.

So right now, I'm updating values using regular old [] syntax, and then calling save on the model, like so:

thing.info[:endDate] = Time.now
thing.save

But a lot of the time it happens that it would be nicer to just $set the value. Is there some other syntax for setting hash field values? Am I wrong about the above error message and Dynamic Attributes being wrong-headed? Am I stuck doing two step updates to hash fields for now?

[1] admittedly, I've recently migrated from mongomapper, and so my expectations of this syntax are partly set by having been able to do this previously in mongomapper.


Solution

  • The thing with Hash field is, it can be dynamic as much as you want. Therefore to prevent polluting your DB schema with unintended fields caused by bugs in your code this functionality is disabled by default.

    No you are not stuck using 2-step updates for your hashes at all!

    [],[]= are the shortcuts for read_attribute() and write_attribute() and should be used if you don't include Mongoid::Attributes::Dynamic. If you try to use $set without enabling dynamic attributes you will get a no-method error because it does not see your dynamic attributes as defined attributes.

    If you'll read the source of Mongoid::Attributes::Dynamic then you'd find that this is required to add the dynamic attributes functionality.

    To update the values by including Mongoid::Attributes::Dynamic you need to follow these steps:

    thing = Thing.first
    thing.set("info.endDate" => Time.now)
    thing.reload # This will update the current variable 
    

    Otherwise if you need you can easily skip this and do the value update by 2-step method

    I hope this sheds some light over your query.

    Source:

    Rails mongoid dynamic fields - no method error

    Dynamic attributes with Rails and Mongoid