Your problem is that you're using serialize to store your data and serialize just slops a big pile of unqueryable YAML into the database. Parsing YAML inside PostgreSQL is certainly possible but it would be slow and a little pointless.
If you really need to store an array of hashes then PostgreSQL's jsonb type would be your best bet. You won't get symbol keys in the database but you will get string keys so this Ruby data:
[{:A => "1", :B => "2"}, {:C => 6, :D => 11}]
would look like this JSON:
[ { "A": "1", "B": "2" }, { "C": 6, "D": 11 } ]
inside the database.
Once you have jsonb in the database, you can query it using all the JSON functions and operators that PostgreSQL providers and you could even index the JSON to support faster querying. In your case, your query would look like:
Model.where("column->0->>'A' = ?", '1')
the -> operator with an integer on the RHS acts like Ruby's Array#[] method:
-> int
Get JSON array element (indexed from zero, negative integers count from the end)
With a string on the RHS it acts like Ruby's Hash#[]. Note that -> returns JSON, not text or integer. The ->> operator is the same as -> but it returns text so you'd use that at the end to make the comparison cleaner.
Alternatively you could say:
Model.where("column->0->'A' = ?", '1'.to_json)
to push the string-vs-JSON logic into Ruby.
Everything is pretty easy once your database schema makes sense (i.e. it is using PostgreSQL's jsonb instead of Rails's serialize) but how do you get there from here? First remove the serialize :column, Array from your model, then you'd need a three step migration something like this:
Add a jsonb column:
def change
add_column :models, :column_j, :jsonb
end
Read each column value from the database, unpack the YAML by hand, and write the JSON by hand:
def up
connection.execute('select id, column from models').each do |row|
a = YAML.load(row['column'])
connection.raw_connection.exec(
'update models set column_j = $1 where id = $2',
[ a.to_json, row['id'].to_i ]
)
end
end
Note that you can't use the model for this as the model class and the database no longer agree about the structure and format of the models table.
Replace the old column with the new column_j:
def change
remove_column :models, :column, :text
rename_column :models, :column_j, :column
end
You'll want to backup your database before migrating of course. And hopefully you'll never even think about using serialize again, serialize is a horrible kludge that seems like an easy solution but quickly turns into an RPG aimed right at your foot.
columnan array column? JSONB? Something else?serialize?serializeis an ugly kludge that just slops a big pile of YAML into the database so you're pretty much out of luck as parsing YAML in SQL will be highly unpleasant.arraydatatype I couldn't find a way to build the column in a way so I can then dom.column[0][:A]. It showed an error. I need to be able to do that. @muistooshort