mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #9944 from where/rebased-fulltext-spacial
Add support for FULLTEXT and SPATIAL indexes using the :type flag for MySQL
This commit is contained in:
commit
cf46c314e2
13 changed files with 106 additions and 35 deletions
|
@ -15,7 +15,7 @@
|
|||
|
||||
*Martin Schuerrer*
|
||||
|
||||
* Add suport for concurrent indexing in PostgreSQL adapter via the
|
||||
* Add support for concurrent indexing in PostgreSQL adapter via the
|
||||
`algorithm: :concurrently` option
|
||||
|
||||
add_index(:people, :last_name, algorithm: :concurrently)
|
||||
|
@ -27,6 +27,14 @@
|
|||
|
||||
*Dan McClain*
|
||||
|
||||
* Add support for fulltext and spatial indexes on MySQL tables with MyISAM database
|
||||
engine via the `type: 'FULLTEXT'` / `type: 'SPATIAL'` option
|
||||
|
||||
add_index(:people, :last_name, type: 'FULLTEXT')
|
||||
add_index(:people, :last_name, type: 'SPATIAL')
|
||||
|
||||
*Ken Mazaika*
|
||||
|
||||
* Add an `add_index` override in Postgresql adapter and MySQL adapter
|
||||
to allow custom index type support. Fixes #6101.
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ module ActiveRecord
|
|||
# Abstract representation of an index definition on a table. Instances of
|
||||
# this type are typically created and returned by methods in database
|
||||
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
|
||||
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :using) #:nodoc:
|
||||
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
|
||||
end
|
||||
|
||||
# Abstract representation of a column definition. Instances of this type
|
||||
|
|
|
@ -498,13 +498,25 @@ module ActiveRecord
|
|||
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
||||
#
|
||||
# ====== Creating an index with a specific method
|
||||
#
|
||||
# add_index(:developers, :name, :using => 'btree')
|
||||
#
|
||||
# generates
|
||||
#
|
||||
# CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
|
||||
# CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
|
||||
#
|
||||
# Note: only supported by PostgreSQL and MySQL
|
||||
#
|
||||
# ====== Creating an index with a specific type
|
||||
#
|
||||
# add_index(:developers, :name, :type => :fulltext)
|
||||
#
|
||||
# generates
|
||||
#
|
||||
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
||||
#
|
||||
# Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
|
||||
def add_index(table_name, column_name, options = {})
|
||||
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
||||
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
||||
|
@ -755,9 +767,10 @@ module ActiveRecord
|
|||
index_name = index_name(table_name, column: column_names)
|
||||
|
||||
if Hash === options # legacy support, since this param was a string
|
||||
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm)
|
||||
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
||||
|
||||
index_type = options[:unique] ? "UNIQUE" : ""
|
||||
index_type = options[:type].to_s if options.key?(:type)
|
||||
index_name = options[:name].to_s if options.key?(:name)
|
||||
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
||||
|
||||
|
|
|
@ -151,6 +151,9 @@ module ActiveRecord
|
|||
:boolean => { :name => "tinyint", :limit => 1 }
|
||||
}
|
||||
|
||||
INDEX_TYPES = [:fulltext, :spatial]
|
||||
INDEX_USINGS = [:btree, :hash]
|
||||
|
||||
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
|
||||
include Arel::Visitors::BindVisitor
|
||||
end
|
||||
|
@ -435,8 +438,11 @@ module ActiveRecord
|
|||
if current_index != row[:Key_name]
|
||||
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
||||
current_index = row[:Key_name]
|
||||
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
|
||||
indexes.last.using = row[:Index_type].downcase.to_sym
|
||||
|
||||
mysql_index_type = row[:Index_type].downcase.to_sym
|
||||
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
||||
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
||||
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
|
||||
end
|
||||
|
||||
indexes.last.columns << row[:Column_name]
|
||||
|
|
|
@ -160,9 +160,9 @@ module ActiveRecord
|
|||
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
||||
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
||||
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
||||
type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
||||
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
||||
|
||||
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, type)
|
||||
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
||||
end.compact
|
||||
end
|
||||
|
||||
|
|
|
@ -187,6 +187,8 @@ HEADER
|
|||
|
||||
statement_parts << ('using: ' + index.using.inspect) if index.using
|
||||
|
||||
statement_parts << ('type: ' + index.type.inspect) if index.type
|
||||
|
||||
' ' + statement_parts.join(', ')
|
||||
end
|
||||
|
||||
|
|
|
@ -36,9 +36,14 @@ class ActiveSchemaTest < ActiveRecord::TestCase
|
|||
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
|
||||
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
|
||||
|
||||
%w(btree hash).each do |type|
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING #{type} ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :using => type)
|
||||
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
|
||||
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :type => type)
|
||||
end
|
||||
|
||||
%w(btree hash).each do |using|
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING #{using} ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :using => using)
|
||||
end
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) "
|
||||
|
|
|
@ -37,22 +37,25 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_dump_indexes
|
||||
index_a_name = 'index_post_title'
|
||||
index_b_name = 'index_post_body'
|
||||
index_a_name = 'index_key_tests_on_snack'
|
||||
index_b_name = 'index_key_tests_on_pizza'
|
||||
index_c_name = 'index_key_tests_on_awesome'
|
||||
|
||||
table = Post.table_name
|
||||
|
||||
@connection.execute "CREATE INDEX `#{index_a_name}` ON `#{table}` (`title`);"
|
||||
@connection.execute "CREATE INDEX `#{index_b_name}` USING btree ON `#{table}` (`body`(10));"
|
||||
table = 'key_tests'
|
||||
|
||||
indexes = @connection.indexes(table).sort_by {|i| i.name}
|
||||
assert_equal 2,indexes.size
|
||||
assert_equal 3,indexes.size
|
||||
|
||||
assert_equal :btree, indexes.select{|i| i.name == index_a_name}[0].using
|
||||
assert_equal :btree, indexes.select{|i| i.name == index_b_name}[0].using
|
||||
index_a = indexes.select{|i| i.name == index_a_name}[0]
|
||||
index_b = indexes.select{|i| i.name == index_b_name}[0]
|
||||
index_c = indexes.select{|i| i.name == index_c_name}[0]
|
||||
assert_equal :btree, index_a.using
|
||||
assert_nil index_a.type
|
||||
assert_equal :btree, index_b.using
|
||||
assert_nil index_b.type
|
||||
|
||||
@connection.execute "DROP INDEX `#{index_a_name}` ON `#{table}`;"
|
||||
@connection.execute "DROP INDEX `#{index_b_name}` ON `#{table}`;"
|
||||
assert_nil index_c.using
|
||||
assert_equal :fulltext, index_c.type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,9 +36,14 @@ class ActiveSchemaTest < ActiveRecord::TestCase
|
|||
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
|
||||
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
|
||||
|
||||
%w(btree hash).each do |type|
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING #{type} ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :using => type)
|
||||
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
|
||||
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :type => type)
|
||||
end
|
||||
|
||||
%w(btree hash).each do |using|
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING #{using} ON `people` (`last_name`) "
|
||||
assert_equal expected, add_index(:people, :last_name, :using => using)
|
||||
end
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) "
|
||||
|
|
|
@ -45,22 +45,25 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_dump_indexes
|
||||
index_a_name = 'index_post_title'
|
||||
index_b_name = 'index_post_body'
|
||||
index_a_name = 'index_key_tests_on_snack'
|
||||
index_b_name = 'index_key_tests_on_pizza'
|
||||
index_c_name = 'index_key_tests_on_awesome'
|
||||
|
||||
table = Post.table_name
|
||||
|
||||
@connection.execute "CREATE INDEX `#{index_a_name}` ON `#{table}` (`title`);"
|
||||
@connection.execute "CREATE INDEX `#{index_b_name}` USING btree ON `#{table}` (`body`(10));"
|
||||
table = 'key_tests'
|
||||
|
||||
indexes = @connection.indexes(table).sort_by {|i| i.name}
|
||||
assert_equal 2,indexes.size
|
||||
assert_equal 3,indexes.size
|
||||
|
||||
assert_equal :btree, indexes.select{|i| i.name == index_a_name}[0].using
|
||||
assert_equal :btree, indexes.select{|i| i.name == index_b_name}[0].using
|
||||
index_a = indexes.select{|i| i.name == index_a_name}[0]
|
||||
index_b = indexes.select{|i| i.name == index_b_name}[0]
|
||||
index_c = indexes.select{|i| i.name == index_c_name}[0]
|
||||
assert_equal :btree, index_a.using
|
||||
assert_nil index_a.type
|
||||
assert_equal :btree, index_b.using
|
||||
assert_nil index_b.type
|
||||
|
||||
@connection.execute "DROP INDEX `#{index_a_name}` ON `#{table}`;"
|
||||
@connection.execute "DROP INDEX `#{index_b_name}` ON `#{table}`;"
|
||||
assert_nil index_c.using
|
||||
assert_equal :fulltext, index_c.type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -225,6 +225,12 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|||
assert_match %r{t.text\s+"medium_text",\s+limit: 16777215$}, output
|
||||
assert_match %r{t.text\s+"long_text",\s+limit: 2147483647$}, output
|
||||
end
|
||||
|
||||
def test_schema_dumps_index_type
|
||||
output = standard_dump
|
||||
assert_match %r{add_index "key_tests", \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
|
||||
assert_match %r{add_index "key_tests", \["pizza"\], name: "index_key_tests_on_pizza", using: :btree}, output
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_decimal_options
|
||||
|
|
|
@ -14,6 +14,16 @@ ActiveRecord::Schema.define do
|
|||
|
||||
add_index :binary_fields, :var_binary
|
||||
|
||||
create_table :key_tests, force: true, :options => 'ENGINE=MyISAM' do |t|
|
||||
t.string :awesome
|
||||
t.string :pizza
|
||||
t.string :snacks
|
||||
end
|
||||
|
||||
add_index :key_tests, :awesome, :type => :fulltext, :name => 'index_key_tests_on_awesome'
|
||||
add_index :key_tests, :pizza, :using => :btree, :name => 'index_key_tests_on_pizza'
|
||||
add_index :key_tests, :snacks, :name => 'index_key_tests_on_snack'
|
||||
|
||||
ActiveRecord::Base.connection.execute <<-SQL
|
||||
DROP PROCEDURE IF EXISTS ten;
|
||||
SQL
|
||||
|
|
|
@ -14,6 +14,16 @@ ActiveRecord::Schema.define do
|
|||
|
||||
add_index :binary_fields, :var_binary
|
||||
|
||||
create_table :key_tests, force: true, :options => 'ENGINE=MyISAM' do |t|
|
||||
t.string :awesome
|
||||
t.string :pizza
|
||||
t.string :snacks
|
||||
end
|
||||
|
||||
add_index :key_tests, :awesome, :type => :fulltext, :name => 'index_key_tests_on_awesome'
|
||||
add_index :key_tests, :pizza, :using => :btree, :name => 'index_key_tests_on_pizza'
|
||||
add_index :key_tests, :snacks, :name => 'index_key_tests_on_snack'
|
||||
|
||||
ActiveRecord::Base.connection.execute <<-SQL
|
||||
DROP PROCEDURE IF EXISTS ten;
|
||||
SQL
|
||||
|
|
Loading…
Reference in a new issue