How can I save array of hashes into db? - ruby-on-rails

I used nokogiri to parse a xml document into array of hashes:
helpers/countries.helper
module CountriesHelper
def parse
#countries = ['australia', 'canada', 'france']
#countries.inject([]) do |memo, country|
File.open("public/#{country}.xml") do |f|
xml = Nokogiri::XML(f)
path = "//country/stores/"
memo << xml.xpath(path).map do |x|
{ 'country' => x.parent['country'],
'store' => x['store']}
end
end
end
# [{"country"=>"australia", "store"=>"store1"}, {"country"=>"france", "store"=>"store2"}]
How can I save this array of hashes format into my database? Lets say I have two models Country and Store.

You can serialize an attribute, which means saving it as a particular type of object.
#in your model
serialize :store_hashes, Array
The field should be a text field in the database. I don't know whether or not that's a good idea in this particular instance - i suspect it isn't. But that's how you save an array of hashes to the database.
http://apidock.com/rails/ActiveRecord/Base/serialize/class

You can store your array of hashes in a text field in your database.
Something like this in your migration file:
create_table "your_table", force: true do |t|
t.text "your_column_name"
end
Or, if you already have the table in the database and just want to add the new column to the table:
class Migration0001
def change
add_column :your_table, :your_column_name, :text
end
end
Just so you know, if you want to save a Hash object in your database and if you define the column type as :text, then Rails should be able to serialize it properly, and you won't need to use serialize explicitly in your model.
But, in your case, it's an Array of Hash, so it's an Array object that needs to be saved in the database, so you need to serialize the field in your model:
serialize :your_column_name, Array
That way, you can save an Array of Hashes in the database. Hope this helps.

Assuming country has many stores. Storing the hash in database would make very little sense (in my opinion). Storing in individual tables would make much more sense and easy for querying.
module CountriesHelper
def parse
#countries = ['australia', 'canada', 'france']
#countries.inject([]) do |memo, country|
File.open("public/#{country}.xml") do |f|
xml = Nokogiri::XML(f)
path = "//country/stores/"
memo << xml.xpath(path).map do |x|
{ 'country' => x.parent['country'],
'store' => x['store']}
country = Country.find_by_name(x.parent['country'])
if country.nil?
country = Country.create(name: x.parent['country'])
end
country.stores.create(name: x['store'])
end
end
end
Database transactions are meant to be invoked from Model; you can refactor later.
class Country < ActiveRecord::Base
has_many :stores
end
class Store < ActiveRecord::Base
belongs_to :country
end

Related

How to convert string from array field and save it as array in db column

I am not able to store array in database column.
text_field:
= text_field_tag 'product[keywords][]', #product.keywords, class: 'tab-input
product_keywords'
controller params:
params.require(:product).permit(:id, :name, :keywords => [])
model:
serialize :keywords, Array
migration:
class AddKeywordsToProducts < ActiveRecord::Migration[5.1]
def change
add_column :products, :keywords, :text
end
end
So, if someone writes, abc mbc csx and hit submit it should save in db column as array like below:
["abc", "mbc", "csx"]
now I want to store it as array in column but its not storing properly.
it stores as:
["abc mbc csx"]
Also what are the best practices to deal with these cases?
Solution for this use case:
Removed serialize array from model. Post without array params.
Post with commas on the front end. On the view, use .join(',')
You can keep storing it as a text with a separator like a "," for example. And you convert it to an array when you read it as the following:
keywords = Product.find(id).keywords.split(",")

Rails - How to save a hash to the DB and using it as a hash once you pull it form the DB (not pulling it as a sting)

If I save a hash to the DB
hash_value = {"1"=>"val1", "2"=>"val2", "3"=>"val3", "4"=>"val4"}
#page.update(hash: hash_value)
Then try and loop through each key of the hash on the page page
hash = #page.hash
<%= hash.each do |key, value| %>
<%= value %>
<% end %>
I get the error undefined method 'each' for #<String:0x007fdda1d8b568>. This error made me realise it is saved saved as a string to the DB.
How do I make it save as a hash so when I pull it rom the DB it is in a hash not a string? Doing some research I found serialize but I cant make out how to use it properly. Is it used to change the DB table to have all values saved in that table be hashes? If so what is added in the migration file to do that?
create_table :pages do |t|
t.timestamps null: false
t.text :title
t.text :content_top
t.text :content_bottom
t.text :hash
t.timestamps null: false
end
Just confused as to how saving a hash to the DB and calling it as a hash is accomplished.
The column type for :hash on :pages table is text, which is the correct column type to use when you wish for a column to store a hash. You must now also indicate on the Page model that you wish to serialize this column. Something like this:
class Page < ActiveRecord::Base
serialize :hash
Then you can attempt to test your new setup like this:
#page = Page.new
#page.hash = {"1"=>"val1", "2"=>"val2", "3"=>"val3", "4"=>"val4"}
#page.save
You can use :json datatype, available in latest postgres 9.3 version onwards, it would be easy to save hash.
you should use serialize :hash to the model and then use it while save into the database.

Rails condition in Model in order to sort results using sunspot solr

currently I am using solr for the search in the database.
I want is to sort that by a date.
Problem: The date is serialized as array in the database, since it can be localized.
My logic would then look for a country specific entry and - if not there, use the default one.
So it can look like this:
[{"DE" => "localizeddate"}, {"EN" => "localizeddate"}, ...]
or like this:
[{"default" => "defaultdate"}]
Now, my class looks like this
class Product < ActiveRecord::Base
serialize :colors, Array
serialize :variants, Array
serialize :images, Array
serialize :sizes, Array
serialize :online_from, Array
searchable do
text :dw_productid, :display_name, :short_description
time :online_from # This is the date that solr will pick up for sorting
end
end
The problem is that I cannot just pass :online_from to solr.
How can I insert a logic here? Do I do that in the model??
Thanks
Benjamin
I finally figured out how to do it:
So inside the searchable method you can add time :something do in order to do more stuff with it.
class Product < ActiveRecord::Base
serialize :colors, Array
serialize :variants, Array
serialize :images, Array
serialize :sizes, Array
serialize :online_from, Array
searchable do
text :dw_productid, :display_name, :short_description
# Sort products according to the online from date
time :online_from do |onlinefromdate|
if onlinefromdate.online_from[0] != nil
#onlinefromdate = onlinefromdate.online_from[0]["DE"] if onlinefromdate.online_from[0]["DE"] != nil
#onlinefromdate = onlinefromdate.online_from[0]["default"] if onlinefromdate.online_from[0]["default"] != nil
#onlinefromdate = onlinefromdate.online_from[0][0]
else
#onlinefromdate = Date.parse('2001-02-03')
end
end
end
end

Ruby On Rails - Can we use varchar column for saving serialized data

Need to store a serialized hash into mysql. Since the size of hash is going to be very small, i decided to use a varchar for saving the serialized data instead of text column. I am using mysql with rails 3.
Model:
class User < ActiveRecord::Base
serialize :monday
end
When I do the following,
u = User.new
u.monday = {:from => "10:00", :to => "04:00"}
u.save
I get following error "TypeError: class or module required". Shouldn't we use varchar for serialized data?
You need a :text database datatype to use the serialize option.

Rails: Serialize value as comma-separated and not as YAML

I'm looking for a way to store a serialized value of eg. IDs in a column. In before claims that this is not an optimal design: the column is used for IDs of associated records, but will only be used when displaying the record - so no queries are made with selection on the column and no joins will be made on this column either.
In Rails I can serialize the column by using:
class Activity
serialize :data
end
This encodes the column as YAML. For legacy sake and since I'm only storing one dimensional arrays containing only integers, I find it more suitable to store it as a comma-separated value.
I've successfully implemented basic accessors like this:
def data=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:data, ids)
end
def data
(read_attribute(:data) || "").split(",")
end
This works pretty fine. However I'd like to add array-like methods to this attribute:
activity = Activity.first
activity.data << 42
...
How would I do this?
You can do it with composed_of feature as explained in this post.
It should be something like:
composed_of :data, :class_name => 'Array', :mapping => %w(data to_csv),
:constructor => Proc.new {|column| column.to_csv},
:converter => Proc.new {|column| column.to_csv}
after_validation do |u|
u.data = u.data if u.data.dirty? # Force to serialize
end
Haven't tested it though.
You can use serialize with a custom coder in rails 3.1.
See my answer to this question. :-)

Resources