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.
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: