Seed embedded documents to MongoDB using Rails rake db:seed - ruby-on-rails

How should db.seeds be structured in order to provide data for MongoDB, especially when containing embedded documents?
I ran into the following error when trying to seed a supposedly wrongly structured db.seeds file :
Cannot serialize an object of class ClassName into BSON.

Start by creating a 'new' nested object like address, with attribute street and city, and setting it equal to a variable. Then create the parent object, in this case user with an attribute address, and assign the variable you created above to it. Since it is a nested object, you need to wrap your variable in an array []. Doing it this way will make it easy to read especially if you have a lot of nested objects. When referencing the nested object make sure to exclude any spaces after the colon or you will get an error.
a = Address.new(street: 'Any_Street', city: 'Any_City')
User.create(address:[a])
This will seed mongoDB with an address object which is nested in the user object.

Parent.create(title: 'foo', children: [
Child.create(title: 'bar', date: Time.utc(2011,10,13)),
Child.create(...),
Child.create(...)
])

Related

Undefined method for NilClass in rails console

I've a model named "ListingInfo", whenever I try to generate new object from this model, it shows the "undefined method for nilclass". I'm unable to add its attributes other than id.
I've also tried "create" method but on running "ListingInfo.count.all", it still returns zero(please see second image for more clarity). Means, it is still not being saved to database.
You need to create the object. Using ListingInfo.new will only instantiate it but won't validate it or save it to the DB. Also you shouldn't pass a value on the id that's generated automatically from Rails.
Solutions:
Use new and then save the object
listing = ListingInfo.new
listing.save
Use create
ListingInfo.create
ActiveRecord::Base documentation
create(attributes = nil) {|object| ...}
Creates an object (or multiple objects) and saves it to the database,
if validations pass. The resulting object is returned whether the
object was saved successfully to the database or not.
new(attributes = nil) {|self if block_given?| ...}
New objects can be instantiated as either empty (pass no construction
parameter) or pre-set with attributes but not yet saved (pass a hash
with key names matching the associated table column names). In both
instances, valid attribute keys are determined by the column names of
the associated table — hence you can‘t have attributes that aren‘t
part of the table columns.

Rails + MongoDB not returning all documents in collection

I need all the documents in one of my collections to create association in between my parent model to child. The problem is I only have the string of my ObjectId. So I am finding the object by string and then set via parent.child = foundObject. So, to achieve this I created a private method as below, to not to create DB request each time I need that child object.
def childs
#childs ||= Child.all
end
But this is not working as expected. When I run ModelName.all it returns below result; not all the docs in collection.
=>
#<Mongoid::Criteria
selector: {}
options: {}
class: ModelName
embedded: false>
And this causes my loop to create another DB request each time I try to associate child to parent. I prevent this by using below method.
def childs
#childs ||= Child.all.select { |v| v.id.present? }
end
I believe there should be a way of collecting all documents in MongoDB, I know the idea of Mongoid::Criteria and what it actually does. But in some case, I need all the objects to be stored in one variable. Do not want to create unwanted DB queries each time I need one specific document in a model.
I could not find a way to solve this specific problem and I think it's kind of impossible since MongoDB is not a relational DB It's quite hard to collect information at the same time with querying. What I used is "MongoDB views" and this solved a lot. Here is the docs. There you can read and find yourself an approach to figure out your own problem.

Rails merge temporary attributes to model object

I have a Rails model object that has attributes maps to columns in a DB. I compute couple of temporary fields name and age which are not in the DB columns. Is there a way to merge them into the object temporarily without making changes to the model object?
class MyModel < ApplicationRecord
end
I tried using attributes.merge. and tried :
my_obj = my_obj.attributes.merge({ age: compute_age })
my_obj = my_obj.attributes.merge({ name: compute_name })
The second merge however fails with NoMethodError: undefined method `attributes' for #Hash:0x0000000120060010.
Looks like the moment I merge first one it makes the object a Hash. So I need to do
my_obj = my_obj.attributes.merge({ age: compute_age })
my_obj[:name]=compute_name
All the other properties also are accessed as a hash.
However this feels inconsistent and weird to me!
Is there a better option to this than using attributes.merge i.e preserve the object?
I guess it maybe cleaner to add the attr to the model object but those attributes are temporary and should not be accessed outside.
As soon as you call .attributes you turn the MyModel instance into a hash.
To turn it back into a model you can just pass that hash to the MyModel constructor (assuming you have an attr_accessor for the virtual attribute):
my_obj = MyModel.new(my_obj.attributes.merge(age: compute_age))
my_obj = MyModel.new(my_obj.attributes.merge(name: compute_name))
You can also see here for some other options: Rails update_attributes without save?
edit
to clarify a little more. You can turn a model instance into a hash via .attributes. You can turn it back into a model using Model.new(attributes_hash). While it's a hash, you can set whatever key-vals you want - it's just a hash. But in order to convert it back to a model instance, you need to make sure that all the hash keys correspond to actual attributes on the model (be they persisted attributes defined by the schema, or virtual attributes defined by attr_accessor).
If you just want to turn it into a hash and add multiple arbitrary keyvals there, you can do it with a single merge:
attrs = my_obj.attributes.merge({ age: compute_age, name: compute_name })

Using Variables for Model Names in Active Record

I am writing a seed file that will make several API calls via HTTParty in order to populate the database. I am pulling the same information for several different models and I would like to be able to use a single method for all of them. However, I cannot figure out how to reference the model name through a variable. Specifically I am having difficulties because each of these must belong to another model. I have tried the following:
def create_assets(subject, model, geokit_hoods)
response = HTTParty.get("https://raw.githubusercontent.com/benbalter/dc-maps/master/maps/#{subject}.geojson")
parsed = JSON.parse(response)
collection = parsed["features"]
collection.each do |station|
coordinates = station["geometry"]["coordinates"].reverse
point = Geokit::LatLng.new(coordinates[0], coordinates[1])
geokit_hoods.each do |hood|
if hood[1].contains?(point)
hood[0][model].create(coordinates: coordinates, name: station["properties"]["NAME"], address: station["properties"]["ADDRESS"])
break
end
end
end
end
Which I called via the following:
create_assets("metro-stations-district", "metros", geokit_hoods)
hood[0] refers to an existing neighborhood model, and hood[1] is the polygon associated with that neighborhood. The code works when referring to hood[0].metros.create(...), but I am looking for a way to make this method useful across many models.
Any ideas would be appreciated!
For now I'm going to assume that what you have in the variable is a String that is the name of the class in table-name format. eg in your example you have metros in the variable... from that I assume you have a Metro class which you are trying to create.
If so... you first need to convert your lowercase table-name style variable ("metros") into a class name-style eg "Metro"
Note: this is title cased and singular (rather than plural).
Rails has a method to do this to strings for exactly the purpose you want: classify eg you could use it thus:
model_name = hood[0][model] # 'metros'
model_name.classify # 'Metro'
Note that it's still just a string, and you can't call create on a string.. so how do you make it the real class? constantize
Use this to turn the string into the actual model-class you're trying to find... which you can then call create on eg:
model_name = hood[0][model] # 'metros'
the_klass = model_name.classify.constantize # Metro
your_instance = the_klass.create(...)

Difference on working of object_id method with Fixnum object and String object

I am confused about the working of one of the innate method present in all ruby objects i.e object_id method. When I run object_id method on any Fixnum object again and again, for example in irb if I do this,
>>100.object_id
=>201
and do this again,
>>100.object_id
=>201
But when I work with String object for example
>>"Hello".object_id
=>162333336
and do this again,
>>"Hello".object_id
=>15502236
Why so? In ruby, everything is an object, and every object has an innate method named object_id which uniquely identifies the object. But here, ruby is confusing me as it treats two strings with same text (i.e "Hello") as different, but two Fixnum objects with same value (i.e.100) as the same and gives the same object id for them. Why so? Can any one please help me?
Fixnums are immutable objects in Ruby. There is exactly one instance created and you work with that object "directly". i.e references are not used unlike other regular objects. So They have a fixed object_id. This is ok because you have only one instance of the object.
But when you write "hello", a new string object is created. And in the same script, if you give another "hello", even though they have same content, a new object is created. Hence the different object_ids.
Such behaviour is a matter of Ruby implementation, not specification. Most likely, you're using MRI (compiled from C source), in JRuby you can get different results.
For performance purposes, MRI handles true, false, nil, Fixnum and symbol specially. A couple of links where you can find more info about it: http://www.oreillynet.com/ruby/blog/2006/02/ruby_values_and_object_ids.html , http://rhg.rubyforge.org/chapter02.html
If you wants to get same object id of string you have to convert it to symbol first then your object_id remains constant
'Hello'.to_sym.object_id

Resources