Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/couchbaseorm
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ when 'index:schema:load'
when 'index:dump'
name = parse_name_argument(ARGV) || CouchbaseOrm::IndexMigration::MigrationGenerator::DEFAULT_NAME
introspected = CouchbaseOrm::IndexMigration::IndexIntrospector.new.indexes
definitions = introspected.map { |index_data| CouchbaseOrm::IndexMigration::IndexDefinition.from_introspected(index_data) }
definitions = introspected.map { |index_data| CouchbaseOrm::IndexDefinition.from_introspected(index_data) }
path = CouchbaseOrm::IndexMigration::MigrationGenerator.new.generate(definitions, name: name)
puts path
when 'index:adopt'
Expand Down
19 changes: 19 additions & 0 deletions docs/reference/index-migrations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ Deferred index example:
defer_build: true
)

Index names can be provided as symbols or strings.

Use strings for non-conventional names (for example names containing ``-``):

.. code-block:: ruby

add_index(
'type-company',
keys: [:type, :company_id]
)

The same rule applies to ``remove_index`` and ``rename_index``.

Example generated query:

.. code-block:: sql
Expand Down Expand Up @@ -188,6 +201,12 @@ Polling runs every second and does not apply a timeout.

remove_index :type_company

For names containing ``-``, pass a string:

.. code-block:: ruby

remove_index 'type-company'

Example generated query:

.. code-block:: sql
Expand Down
15 changes: 13 additions & 2 deletions lib/couchbase-orm/index_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module CouchbaseOrm
class IndexDefinition
attr_reader :name, :keys, :where, :defer_build, :num_replica

NAME_SYMBOL_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*\z/

def initialize(name:, keys:, where: nil, defer_build: false, num_replica: nil)
@name = normalize_name(name)
@keys = normalize_keys(keys)
Expand All @@ -21,13 +23,22 @@ def self.from_introspected(index_data)
end

def <=>(other)
name <=> other.name
name.to_s <=> other.name.to_s
end

private

def normalize_name(name)
name.to_sym
self.class.normalize_name(name)
end

class << self
def normalize_name(name)
value = name.to_s.strip
return value.to_sym if value.match?(NAME_SYMBOL_PATTERN)

value
end
end

def normalize_keys(raw_keys)
Expand Down
8 changes: 4 additions & 4 deletions lib/couchbase-orm/index_migration/migration_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def generate(index_definitions, name: DEFAULT_NAME)
end

def source_for(index_definitions, class_name: DEFAULT_NAME)
definitions = Array(index_definitions).sort_by(&:name)
definitions = Array(index_definitions).sort_by { |d| d.name.to_s }

<<~RUBY
class #{migration_class_name(class_name)} < CouchbaseOrm::IndexMigration
Expand Down Expand Up @@ -66,14 +66,14 @@ def up_body(definitions)
end

def down_body(definitions)
lines = definitions.reverse.map { |definition| "remove_index :#{definition.name}" }
lines = definitions.reverse.map { |definition| "remove_index #{ruby_value(definition.name)}" }
indent(lines)
end

def add_index_lines(definition)
lines = [
'add_index(',
" :#{definition.name},",
" #{ruby_value(definition.name)},",
" keys: #{ruby_array(definition.keys)},"
]
lines << " where: #{definition.where.inspect}," if definition.where
Expand All @@ -86,7 +86,7 @@ def build_indexes_lines(definitions)
lines = ['build_indexes(']
definitions.each_with_index do |definition, index|
suffix = index == definitions.length - 1 ? '' : ','
lines << " :#{definition.name}#{suffix}"
lines << " #{ruby_value(definition.name)}#{suffix}"
end
lines << ')'
lines
Expand Down
6 changes: 5 additions & 1 deletion lib/couchbase-orm/index_migration/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def create_index(index_definition)
raise ArgumentError.new('Missing index keys configuration') if Array(index_definition.keys).empty?

query = +"CREATE INDEX `#{index_definition.name}`\n"
query << "ON `#{bucket}`(#{Array(index_definition.keys).map { |key| "`#{key}`" }.join(',')})"
query << "ON `#{bucket}`(#{Array(index_definition.keys).map { |key| format_key(key) }.join(',')})"
query << "\nWHERE (#{index_definition.where})" if index_definition.where
options = with_options(defer_build: index_definition.defer_build, num_replica: index_definition.num_replica)
query << "\nWITH #{JSON.pretty_generate(options)}" unless options.empty?
Expand Down Expand Up @@ -52,6 +52,10 @@ def states_query(bucket, index_names)

private

def format_key(key)
key.is_a?(Symbol) ? "`#{key}`" : key.to_s
end

def with_options(defer_build:, num_replica:)
options = {}
options['defer_build'] = true if defer_build
Expand Down
4 changes: 2 additions & 2 deletions lib/couchbase-orm/index_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ def add_index(name, keys:, where: nil, num_replica: nil, defer_build: false)
end

def remove_index(name)
@indexes.delete(name.to_sym)
@indexes.delete(CouchbaseOrm::IndexDefinition.normalize_name(name))
end

def rename_index(old_name, new_name)
index_definition = @indexes.delete(old_name.to_sym)
index_definition = @indexes.delete(CouchbaseOrm::IndexDefinition.normalize_name(old_name))
return unless index_definition

renamed_index = CouchbaseOrm::IndexDefinition.new(
Expand Down
10 changes: 5 additions & 5 deletions lib/couchbase-orm/index_schema/dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def source_for(indexes, version: nil)
lines = []
lines << definition_header(version)

index_names = indexes.keys.sort
index_names = indexes.keys.sort_by(&:to_s)
index_names.each_with_index do |name, index|
lines.concat(index_lines(indexes[name]))
lines << '' unless index == index_names.length - 1
Expand Down Expand Up @@ -70,13 +70,13 @@ def define_add_index(klass, indexes)

def define_remove_index(klass, indexes)
klass.define_method(:remove_index) do |name|
indexes.delete(name.to_sym)
indexes.delete(CouchbaseOrm::IndexDefinition.normalize_name(name))
end
end

def define_rename_index(klass, indexes)
klass.define_method(:rename_index) do |old_name, new_name|
index_definition = indexes.delete(old_name.to_sym)
index_definition = indexes.delete(CouchbaseOrm::IndexDefinition.normalize_name(old_name))
next unless index_definition

renamed_index = CouchbaseOrm::IndexDefinition.new(
Expand Down Expand Up @@ -105,7 +105,7 @@ def definition_header(version)
end

def index_lines(index_definition)
lines = [" add_index :#{index_definition.name},"]
lines = [" add_index #{ruby_value(index_definition.name)},"]

option_lines = ["keys: #{ruby_array(index_definition.keys)}"]
option_lines << "where: #{index_definition.where.inspect}" if index_definition.where
Expand All @@ -125,7 +125,7 @@ def ruby_array(values)
end

def ruby_value(value)
value.is_a?(Symbol) ? ":#{value}" : value.inspect
value.inspect
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/couchbase-orm/index_schema/loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def apply_definition(definition)
create_operation_class = @migration_class::Operations::CreateIndex
build_operation_class = @migration_class::Operations::BuildIndexes

definition.keys.sort.each do |name|
definition.keys.sort_by(&:to_s).each do |name|
index_definition = definition[name]
create_operation_class.new(index_definition).execute(migration)
deferred_indexes << name if index_definition.defer_build
Expand Down
6 changes: 6 additions & 0 deletions spec/index_definition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
expect(definition.keys).to eq(['LOWER(`name`)'])
end

it 'keeps non-conventional names as strings' do
definition = described_class.new(name: 'type-company', keys: [:type])

expect(definition.name).to eq('type-company')
end

it 'tracks defer_build and num_replica attributes' do
definition = described_class.new(
name: :type_company,
Expand Down
12 changes: 12 additions & 0 deletions spec/index_schema_definition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,16 @@
expect(indexes[:date_on_type_v2].where).to eq('type is valued')
expect(indexes[:date_on_type_v2].defer_build).to be(true)
end

it 'supports renaming and removing non-conventional names' do
indexes = described_class.define do
add_index('date-on-type', keys: [:date])
rename_index('date-on-type', 'date-on-type-v2')
remove_index('date-on-type-v2')
add_index('date-on-type-v3', keys: [:date])
end

expect(indexes.keys).to eq(['date-on-type-v3'])
expect(indexes['date-on-type-v3']).to be_a(CouchbaseOrm::IndexDefinition)
end
end
13 changes: 13 additions & 0 deletions spec/index_schema_dumper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@
end
end

it 'dumps non-conventional names as valid ruby values' do
indexes = {
'type-company' => CouchbaseOrm::IndexDefinition.new(name: 'type-company', keys: [:type]),
:date_on_type => CouchbaseOrm::IndexDefinition.new(name: :date_on_type, keys: [:date])
}

source = described_class.new.source_for(indexes, version: 20260101120000)

expect(source).to include('add_index :date_on_type,')
expect(source).to include('add_index "type-company",')
expect { RubyVM::InstructionSequence.compile(source) }.not_to raise_error
end

def write_migrations(migrations_path)
FileUtils.mkdir_p(migrations_path)

Expand Down
29 changes: 29 additions & 0 deletions spec/index_schema_loader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@
end
end

it 'loads schema file containing non-conventional index names' do
schema_with_hyphen_name = <<~RUBY
CouchbaseOrm::IndexSchema.define(version: 20260101120001) do
add_index "type-company",
keys: [:type],
defer_build: true
end
RUBY

Dir.mktmpdir do |dir|
schema_path = File.join(dir, 'index_schema.rb')
File.write(schema_path, schema_with_hyphen_name)

cluster = instance_double(Couchbase::Cluster)
allow(CouchbaseOrm::Connection).to receive(:cluster).and_return(cluster)

expect(cluster).to receive(:query).with(/CREATE INDEX `type-company`/, instance_of(Couchbase::Options::Query)).ordered
expect(cluster).to receive(:query).with(<<~SQL.strip, instance_of(Couchbase::Options::Query)).ordered
BUILD INDEX ON `fleet-prod`
(
`type-company`
);
SQL

version = described_class.new(path: schema_path).load
expect(version).to eq(20260101120001)
end
end

def expect_schema_load_queries(cluster)
expect(cluster).to receive(:query).with(/CREATE INDEX `date_on_type`/, instance_of(Couchbase::Options::Query)).ordered
expect(cluster).to receive(:query).with(/CREATE INDEX `type_company`/, instance_of(Couchbase::Options::Query)).ordered
Expand Down
13 changes: 13 additions & 0 deletions specs/005-add-index-schema-dump-and-load/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ The DSL supports:
add_index
```

Index names can be symbols or strings.
For non-conventional names (for example with a hyphen `-`), use strings:

```ruby
add_index "type-company",
keys: [:type, :company_id]
```

The same applies to `remove_index` and `rename_index`.

---

# Schema Dump
Expand Down Expand Up @@ -192,6 +202,9 @@ Generated schema:
CouchbaseOrm::IndexSchema.define(version: 2026_01_01_120000) do
add_index :type_company,
keys: [:type, :company_id]

add_index "type-company",
keys: [:type]
end
```

Expand Down
Loading