Skip to content

Commit

Permalink
Merge pull request #12 from getlago/upstream
Browse files Browse the repository at this point in the history
feat: Support map of arrays
  • Loading branch information
vincent-pochet authored Oct 8, 2024
2 parents 683125a + de89340 commit 0be4d04
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 14 deletions.
4 changes: 4 additions & 0 deletions lib/active_record/connection_adapters/clickhouse/oid/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def type
def deserialize(value)
if value.is_a?(::Hash)
value.map { |k, item| [k.to_s, deserialize(item)] }.to_h
elsif value.is_a?(::Array)
value.map { |item| deserialize(item) }
else
return value if value.nil?
case @subtype
Expand All @@ -44,6 +46,8 @@ def deserialize(value)
def serialize(value)
if value.is_a?(::Hash)
value.map { |k, item| [k.to_s, serialize(item)] }.to_h
elsif value.is_a?(::Array)
value.map { |item| serialize(item) }
else
return value if value.nil?
case @subtype
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ def add_column_options!(sql, options)
if options[:array]
sql.gsub!(/\s+(.*)/, ' Array(\1)')
end
if options[:map]
if options[:map] == :array
sql.gsub!(/\s+(.*)/, ' Map(String, Array(\1))')
end
if options[:map] == true
sql.gsub!(/\s+(.*)/, ' Map(String, \1)')
end
sql.gsub!(/(\sString)\(\d+\)/, '\1')
Expand Down
7 changes: 7 additions & 0 deletions lib/clickhouse-activerecord/schema_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ def schema_array(column)
end

def schema_map(column)
if column.sql_type =~ /Map\(([^,]+),\s*(Array)\)/
return :array
end

(column.sql_type =~ /Map?\(/).nil? ? nil : true
end

Expand All @@ -157,6 +161,9 @@ def prepare_column_options(column)
spec[:unsigned] = schema_unsigned(column)
spec[:array] = schema_array(column)
spec[:map] = schema_map(column)
if spec[:map] == :array
spec[:array] = nil
end
spec[:low_cardinality] = schema_low_cardinality(column)
spec.merge(super).compact
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ def up
end
end
end

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ def up
t.datetime :map_datetime, null: false, map: true
t.string :map_string, null: false, map: true
t.integer :map_int, null: false, map: true

t.datetime :map_array_datetime, null: false, map: :array
t.string :map_array_string, null: false, map: :array
t.integer :map_array_int, null: false, map: :array

t.date :date, null: false
end
end
end

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def up
t.string :fixed_string1, fixed_string: 1, null: false
t.string :fixed_string16_array, fixed_string: 16, array: true, null: true
t.string :fixed_string16_map, fixed_string: 16, map: true, null: true
t.string :fixed_string16_map_array, fixed_string: 16, map: :array, null: true
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def up
t.string :col2, low_cardinality: true, null: true
t.string :col3, low_cardinality: true, array: true, null: true
t.string :col4, low_cardinality: true, map: true, null: true
t.string :col5, low_cardinality: true, map: :array, null: true
end
end
end
9 changes: 7 additions & 2 deletions spec/single/migration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,18 @@

current_schema = schema(model)

expect(current_schema.keys.count).to eq(4)
expect(current_schema.keys.count).to eq(5)
expect(current_schema).to have_key('col1')
expect(current_schema).to have_key('col2')
expect(current_schema).to have_key('col3')
expect(current_schema).to have_key('col4')
expect(current_schema).to have_key('col5')
expect(current_schema['col1'].sql_type).to eq('LowCardinality(String)')
expect(current_schema['col1'].default).to eq('col')
expect(current_schema['col2'].sql_type).to eq('LowCardinality(Nullable(String))')
expect(current_schema['col3'].sql_type).to eq('Array(LowCardinality(Nullable(String)))')
expect(current_schema['col4'].sql_type).to eq('Map(String, LowCardinality(Nullable(String)))')
expect(current_schema['col5'].sql_type).to eq('Map(String, Array(LowCardinality(Nullable(String))))')
end
end

Expand All @@ -176,13 +178,16 @@

current_schema = schema(model)

expect(current_schema.keys.count).to eq(3)
expect(current_schema.keys.count).to eq(4)
expect(current_schema).to have_key('fixed_string1')
expect(current_schema).to have_key('fixed_string16_array')
expect(current_schema).to have_key('fixed_string16_map')
expect(current_schema).to have_key('fixed_string16_map_array')
expect(current_schema['fixed_string1'].sql_type).to eq('FixedString(1)')
expect(current_schema['fixed_string16_array'].sql_type).to eq('Array(Nullable(FixedString(16)))')
expect(current_schema['fixed_string16_map'].sql_type).to eq('Map(String, Nullable(FixedString(16)))')
expect(current_schema['fixed_string16_map_array'].sql_type).to eq('Map(String, Array(Nullable(FixedString(16))))')

end
end

Expand Down
38 changes: 29 additions & 9 deletions spec/single/model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -401,16 +401,29 @@ class ModelWithoutPrimaryKey < ActiveRecord::Base
map_datetime: {a: 1.day.ago, b: Time.now, c: '2022-12-06 15:22:49'},
map_string: {a: 'asdf', b: 'jkl' },
map_int: {a: 1, b: 2},
map_array_datetime: {a: [1.day.ago], b: [Time.now, '2022-12-06 15:22:49']},
map_array_string: {a: ['str'], b: ['str1', 'str2']},
map_array_int: {a: [1], b: [1, 2, 3]},
date: date
)
}.to change { model.count }
}.to change { model.count }.by(1)

record = model.first
expect(record.map_datetime.is_a?(Hash)).to be_truthy
expect(record.map_datetime['a'].is_a?(DateTime)).to be_truthy
expect(record.map_string['a'].is_a?(String)).to be_truthy
expect(record.map_datetime).to be_a Hash
expect(record.map_string).to be_a Hash
expect(record.map_int).to be_a Hash
expect(record.map_array_datetime).to be_a Hash
expect(record.map_array_string).to be_a Hash
expect(record.map_array_int).to be_a Hash

expect(record.map_datetime['a']).to be_a DateTime
expect(record.map_string['a']).to be_a String
expect(record.map_string).to eq({'a' => 'asdf', 'b' => 'jkl'})
expect(record.map_int.is_a?(Hash)).to be_truthy
expect(record.map_int).to eq({'a' => 1, 'b' => 2})

expect(record.map_array_datetime['b']).to be_a Array
expect(record.map_array_string['b']).to be_a Array
expect(record.map_array_int['b']).to be_a Array
end

it 'create with insert all' do
Expand All @@ -419,21 +432,28 @@ class ModelWithoutPrimaryKey < ActiveRecord::Base
map_datetime: {a: 1.day.ago, b: Time.now, c: '2022-12-06 15:22:49'},
map_string: {a: 'asdf', b: 'jkl' },
map_int: {a: 1, b: 2},
map_array_datetime: {a: [1.day.ago], b: [Time.now, '2022-12-06 15:22:49']},
map_array_string: {a: ['str'], b: ['str1', 'str2']},
map_array_int: {a: [1], b: [1, 2, 3]},
date: date
}])
}.to change { model.count }
}.to change { model.count }.by(1)
end

it 'get record' do
model.connection.insert("INSERT INTO #{model.table_name} (id, map_datetime, date) VALUES (1, {'a': '2022-12-05 15:22:49', 'b': '2022-12-06 15:22:49'}, '2022-12-06')")
model.connection.insert("INSERT INTO #{model.table_name} (id, map_datetime, map_array_datetime, date) VALUES (1, {'a': '2022-12-05 15:22:49', 'b': '2024-01-01 12:00:08'}, {'c': ['2022-12-05 15:22:49','2024-01-01 12:00:08']}, '2022-12-06')")
expect(model.count).to eq(1)
record = model.first
expect(record.date.is_a?(Date)).to be_truthy
expect(record.date).to eq(Date.parse('2022-12-06'))
expect(record.map_datetime.is_a?(Hash)).to be_truthy
expect(record.map_datetime).to be_a Hash
expect(record.map_datetime['a'].is_a?(DateTime)).to be_truthy
expect(record.map_datetime['a']).to eq(DateTime.parse('2022-12-05 15:22:49'))
expect(record.map_datetime['b']).to eq(DateTime.parse('2022-12-06 15:22:49'))
expect(record.map_datetime['b']).to eq(DateTime.parse('2024-01-01 12:00:08'))
expect(record.map_array_datetime).to be_a Hash
expect(record.map_array_datetime['c']).to be_a Array
expect(record.map_array_datetime['c'][0]).to eq(DateTime.parse('2022-12-05 15:22:49'))
expect(record.map_array_datetime['c'][1]).to eq(DateTime.parse('2024-01-01 12:00:08'))
end
end
end
Expand Down

0 comments on commit 0be4d04

Please sign in to comment.