Getting Hash with Symbol as Keys for Mongo in Rails

Getting hash with symbol as keys for mongo in rails

According to the FAQ, Even though there is a symbol type in Mongo, and you can store symbols in values, the BSON format specifies that keys must be strings.

Search Mongoid hash field by key

Course.where(:'skills_available.52ab84929938c7f966d4f0cc'.ne => nil)
  • Only restriction is that it will skip: {'52ab84929938c7f966d4f0cc' => nil}

Elegant way to stringify keys with Rails and Mongo?

Something along these lines could work. Basically this code redefines Mongoid's field macro (its setter).

require 'mongoid'

module Stringifier
def field name, args = {}
super # call mongoid implementation

define_method "#{name}=" do |val|
val.stringify_keys! if val && val.respond_to?(:stringify_keys!)
super(val)
end
end
end

class Foo
include Mongoid::Document
extend Stringifier

field :subhash, type: Hash
end

f = Foo.new
f.subhash = {a: 1, b: 2}

puts f.subhash
# >> {"a"=>1, "b"=>2}

This may not be the cleanest implementation, but you get the idea.

setting default hash keys for mongoid hash field

The :default option can take a lambda as its value. Inside that lambda, self will be the new instance you're creating. That means that you can (indirectly) call methods to supply defaults:

class Sport
include Mongoid::Document
field :rules, type: Hash, default: lambda { default_rules }
end

class Sport::Football < Sport
private
def default_rules
{ :offside => '', :penalty => '' }
end
end

class Sport::Boxing < Sport
private
def default_rules
{ :biting => 'not allowed except for ears' }
end
end

You don't have to make default_rules private of course. You'll also want default_rules in Sport if you want to instantiate Sport directly.

You could also use one of the callbacks to set the rules by hand:

class Sport
include Mongoid::Document
field :rules, type: Hash
after_initialize :set_default_rules, :if => :new_record? # Or one of the other callbacks.
end

and subclasses could say self.rules = ... in their set_default_rules implementations.

saving and fetching hash in Mongoid Document

What version of Ruby and Mongoid are you using? Accessing hashes in Mongoid objects via symbols works fine in Mongoid 3.0.4 and Ruby 1.9.3. So I can do something like:

@metrics = Metrics.find(params[:id])[:metrics]
@metrics[:users][:year][:male]

or even:

Metrics.find(params[:id])[:metrics][:users][:year][:male]

Also, why not just leave the metrics field out, then treat instances of the Metrics class like instances of Hash? With Mongoid you can dynamically set and get attributes using the standard Ruby Hash symbol syntax without ever "declaring" the fields. With the metrics field removed:

m = Metric.new
m[:users] = {}
m[:users][:year] = {}
m[:users][:year][:male] = "data"

Additionally, if you need methods to auto-initialize nested hashes, so you can do things like:

m = Metric.new
m[:users][:year][:male] = "data"

you can put all of that logic into the Metrics class. To get started on that you could try adapting something like the [] and []= methods from the AutoHash class explained here. That would give you the cleanest interface I think.

Symbols used as Hash keys get converted to Strings when serialized

More than likely this has to do with the ORM you are using to provide the persistance layer for the model. You can probably wrap some_attr with a method that returns it in the form of a HashWithIndifferentAccess which you can then access with either strings or arrays. Since you are using Rails, this functionality can be activated by calling the with_indifferent_access method on the Hash object. (If you have an array of Hash objects, you'll need to call it on each one of course) The method will return the same hash, but then symbol lookups will work.

From your code:

new_hash = MyMongoModel.last.some_attr.with_indifferent_access
new_hash[:a] # Will return the same as new_hash['a']

Hope this helps!

setting mongoid hash field values

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

Using IN while querying on mongoid hash field

The query methods such as in and ne are added to Symbol, not String. So you can say:

:'keys.location_id'.in => location_ids

where location_ids is an array. Also, the query methods are just short forms for building the longer native-style nested Hash queries. For example, if you say:

:field.op => value

for some operator op, then Mongoid actually sends this (JavaScript) into MongoDB:

field: { $op: value }

So if you don't want to write :'keys.location_id'.in then you can say:

'keys.location_id' => { :$in  => location_ids }
# or
'keys.location_id' => { '$in' => location_ids }

instead.

Also note that if location_ids is an array then [location_ids] is an array of arrays and that's not what you want in your query, you want just location_ids as above.



Related Topics



Leave a reply



Submit