MultiPolygon column by migration - ruby-on-rails

I'm trying to change my polygon column type to a multipolygon column type.
My code is a simple line.
change_column :messages, :area_shape, :multipolygon, srid: 3785
But Postgres doesn't know this type. I thought that I missed something on my PostGIS configuration but I can't see it.
This my exact error:
rake aborted!
PG::UndefinedObject: ERROR: type "multipolygon" does not exist
: ALTER TABLE "messages" ALTER COLUMN "area_shape" TYPE multipolygon
This is how I've created my area_shape as a polygon type:
add_column :messages, :area_shape, :polygon, srid: 3785
Thank you for your help.

I have no idea how RGeo attempts to implement change_column (is there documentation for it?), but it isn't correct since there is no such multipolygon type.
If you have direct access to PostgreSQL, following from this answer, use this DDL:
ALTER TABLE my_table
ALTER COLUMN area_shape TYPE geometry(MultiPolygon,3785)
USING ST_Multi(area_shape);

Finally I had to remove then recreate my column:
remove_column :messages, :area_shape
add_column :messages, :area_shape, :multi_polygon, srid: 3785
I think I can now understand why it's not possible. Indeed, it seems difficult to change a polygon type to a multi_polygon type without losing data logique.
If you really need to change the type, you can use what was said by #Mike (manually) and create a small method to convert polygon to multi_polygon but it's not really safe in my mind.
Tip: a multi_polygon type is an Enumerable that means multi_polygon accepts Array type.

Related

rails association for legacy data when foreign_key is string

I have the unfortunate task of making some legacy data work with a rails ap. Some of the id fields are strings
:client_id => "30430"
But in postgres (which we’d prefer to use) the association query chokes because of the data type mismatch. Is there a way around this?
PG::UndefinedFunction: ERROR: operator does not exist: bigint = character varying LINE 1: ...ients" INNER JOIN "reservation" ON "clients"."id" = "reserva...
No operator matches the given name and argument type(s). You might need to add explicit type casts.
To be explicit, I'm looking for solutions that don't involve altering the underlying data (though that would be my first choice)
At first I thought you could just write a migration to convert the column type to integer, which works with mysql but apparently not postgres:
https://makandracards.com/makandra/18691-postgresql-vs-rails-migration-how-to-change-columns-from-string-to-integer
Assuming your table is called products, the following should work:
change_column :products, :client_id, 'integer USING CAST(column_name AS integer)'
As the link states, any strings with a non numeric value will obviously give weird results, if not throw an error. And it's obviously worth trying out locally first.

Rails migration. Cast integer values into boolean with change_column

I'm using RoR5 with PostgreSQL. I have a table with a column status. Which is of a type integer and holds three values 0, 1, 2. Those values represent three statuses allowed, not_allowed and no_tests.
I'm gonna change the logic. I want to convert two statuses allowed and not_allowed into boolean. Then I'll create a separate column for the no_tests.
Right now I have this accordance:
enum status: %i[allowed not_allowed no_tests].
How should I write a migration to have allowed as true and both not_allowed and no_tests as false in the changed column?
Actually this is two separate operations (change table structure, convert existing data), only one of would typically be done in a migration. If I were you I would first run a migration to add the new status column, and then do an update either in sql if you have easy access to the postgres console or at the rails console to recode the existing data in your new column. After you've recoded the data you can drop the old columns in another migration.
I think you will need some stuff in up method within migration.
def up
add_column :table_name, :status2, :boolean
Loop on each element of model check for status and update status2
remove_column :table_name, :status
rename_column :table_name, :status2, :status
end
You can also go with case statement in update here It will help. You can put your query in migration as well something similar to here.
You can also do one more thing like :
1. Make migration to add new column.
2. Make rake task to populate data.
3. Make one more migration to delete old column and rename newly added column.
Update : For better understanding on migration do read this article.

Rails migration - change column from varchar to jsonb

I'm trying to convert an existing column of type varchar to jsonb. The column contains strings like "black white orange" and want to convert it into jsonb format such that it will be converted to ["black", "white", "orange"].
class AlterColorsDatatype < ActiveRecord::Migration[5.0]
def change
change_column :quotes, :colors, :jsonb, default: '[]', using: 'colors::jsonb'
end
end
I expected to this to convert the column type to jsonb and the using: part would convert existing data to jsonb as well.
Instead, I get this error:
ActiveRecord::StatementInvalid: PG::InvalidTextRepresentation: ERROR: invalid input syntax for type json
DETAIL: Token "Indigo" is invalid.
CONTEXT: JSON data, line 1: Indigo
: ALTER TABLE "quotes" ALTER COLUMN "colors" TYPE jsonb USING colors::jsonb
I have tried other syntax but still ends up with the same error. I am thinking I would have to convert the whole column attribute by attribute with something like to_json but am not sure how to approach solving this error. After many google searches, other people with the same error did not seem to find a solution.
You can't get from a space-delimited string to a JSON with a simple cast. An easy way is to first break the string up to get a PostgreSQL array (text[]):
regexp_split_to_array(colors, E'\\s+')
and then convert that array to JSON:
to_json(regexp_split_to_array(colors, E'\\s+'))
You have to be careful with the quoting and backslashes to get that bit of SQL through Ruby and into the database so you'd say:
using: %q{to_json(regexp_split_to_array(colors, E'\\\\s+'))}
The %q{...} is like a single quoted string but lets you avoid having to escape the single quotes in the SQL string literal, then double up your backslashes to keep them from being interpreted by %q{...}.

Rails: Effects of changing the data type of an existing column with existing data

I could be asking this in the wrong place so go easy and point me in the right direction if I am.
I can't get my head around how changing the data type of an existing column in an existing table with existing data in Rails will effect any app I am working on.
If I have a boolean column called football. The football can be either true or false. Either, I have a football or I don't. I realise that, for example, the football can sometimes be loaned. So, I want to change the data type of the column football to be a string. The value of the string can be true, false or loaned.
How bad a time am I going to have after running the migration for this? What will happen to my existing data? What do I need to do to mitigate against messing up all my existing data? And, am I asking this question in the right place?
If you change the column type from boolean to string in rails then you have no worry because the existing data will automatically change into string. Like if you have boolean true this will automatically convert into string "true".
FYI, I checked this on my system ;)
If I were you I would do this by creating a new column, then updating everything from the old column, and then removing the old column, and renaming the new one. Also I wouldn't save a boolean as "true" or "false" either (which Rails should give you by default if you DO just change the type)... If I were you I would create an enum for this column.
First your migration:
class ChangeFootball < ActiveRecord::Migration
def change
# Add the new column. Use an integer type, so you can set up an Enum in your model
add_column :examples, :football_new, :integer, default: 0
# Set up the new column values for all your existing records:
Example.where(football: true).update_all football_new: 0
Example.where(football: false).update_all football_new: 1
# Now remove the old column:
remove_column :examples, :football
# Finally, rename the new column to the same as the old one
rename_column :examples, :football_new, :football
# Oh, and add an index:
add_index :examples, :football
end
end
Now set up the enum in your model:
enum football: { own: 0, lost: 1, misplaced: 2, loaned: 3 }

activerecord (rails) migration -- set option values for a column

I'm using the rgeo gem in conjunction with the activerecord-postgis-adapter gem for some columns, and I'm wanting the change an option for an existing column
Allow me to show it this way, in the schema.rb file, currently this is the line recording the column
t.spatial "shape", :limit => {:srid=>0, :type=>"multi_polygon"}
So this column has some special metatag options recognized by postgis.
I want to set the default srid of these :shape columns from 0 to 4326. I'd either like to do this with a migration, or even better, to be able to set the values for these points individually. Currently, I haven't located a setter method in the RGeo documentation for the :srid tag, only a reader. So I'm thinking my best bet is a migration.
I've tried this:
change_column :parcels, :shape, :srid, 4326
But got this error:
== AddSridOptToShapes: migrating =============================================
-- change_column(:zones, :shape, :srid, 4326)
rake aborted!
An error has occurred, this and all later migrations canceled:
can't convert Symbol into Integer
I know that its possible to solve this problem by executing a string of SQL, but am hoping active record provides a way as well
I solved this problem by simply creating a new column, and in my case, on for every projection I needed to use with a particular shapefile record
:proj_shape_0
:proj_shape_3361
:proj_shape_2264
etc where the number is the srid code

Resources