elasticsearch_record 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +22 -8
- data/docs/CHANGELOG.md +20 -1
- data/lib/active_record/connection_adapters/elasticsearch/column.rb +9 -2
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/clone_table_definition.rb +88 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb +1 -1
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +29 -27
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +67 -12
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +48 -13
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_meta_definition.rb +24 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb +9 -4
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +38 -13
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +3 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +41 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +36 -10
- data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +102 -8
- data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +17 -2
- data/lib/arel/collectors/elasticsearch_query.rb +3 -0
- data/lib/arel/visitors/elasticsearch_query.rb +1 -0
- data/lib/arel/visitors/elasticsearch_schema.rb +48 -7
- data/lib/elasticsearch_record/core.rb +24 -8
- data/lib/elasticsearch_record/gem_version.rb +1 -1
- data/lib/elasticsearch_record/model_schema.rb +26 -2
- data/lib/elasticsearch_record/persistence.rb +45 -34
- data/lib/elasticsearch_record/query.rb +4 -1
- data/lib/elasticsearch_record/schema_migration.rb +49 -0
- data/lib/elasticsearch_record/tasks/elasticsearch_database_tasks.rb +15 -5
- data/lib/elasticsearch_record.rb +1 -0
- metadata +6 -3
@@ -3,12 +3,19 @@ module ElasticsearchRecord
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
+
|
7
|
+
# Rails resolves the primary_key's value by accessing the +#id+ method.
|
8
|
+
# Since Elasticsearch also supports an additional, independent +id+ attribute, it would only be able to access
|
9
|
+
# this through +read_attribute(:id)+.
|
10
|
+
# To also have the ability of accessing this attribute through the default, this flag can be enabled.
|
11
|
+
# @attribute! Boolean
|
6
12
|
class_attribute :relay_id_attribute, instance_writer: false, default: false
|
7
13
|
end
|
8
14
|
|
9
15
|
# overwrite to provide a Elasticsearch version of returning a 'primary_key' attribute.
|
10
16
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
11
|
-
# To provide functionality of returning the +id+ attribute, this method must also support it
|
17
|
+
# To provide functionality of returning the +id+ attribute, this method must also support it
|
18
|
+
# with enabled +relay_id_attribute+.
|
12
19
|
# @return [Object]
|
13
20
|
def id
|
14
21
|
# check, if the model has a +id+ attribute
|
@@ -19,7 +26,8 @@ module ElasticsearchRecord
|
|
19
26
|
|
20
27
|
# overwrite to provide a Elasticsearch version of setting a 'primary_key' attribute.
|
21
28
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
22
|
-
# To provide functionality of setting the +id+ attribute, this method must also support it
|
29
|
+
# To provide functionality of setting the +id+ attribute, this method must also support it
|
30
|
+
# with enabled +relay_id_attribute+.
|
23
31
|
# @param [Object] value
|
24
32
|
def id=(value)
|
25
33
|
# check, if the model has a +id+ attribute
|
@@ -33,23 +41,26 @@ module ElasticsearchRecord
|
|
33
41
|
|
34
42
|
# overwrite to provide a Elasticsearch version of returning a 'primary_key' was attribute.
|
35
43
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
36
|
-
# To provide functionality of returning the +id_Was+ attribute, this method must also support it
|
44
|
+
# To provide functionality of returning the +id_Was+ attribute, this method must also support it
|
45
|
+
# with enabled +relay_id_attribute+.
|
37
46
|
def id_was
|
38
|
-
relay_id_attribute? && has_attribute?('id') ? attribute_was('id') :
|
47
|
+
relay_id_attribute? && has_attribute?('id') ? attribute_was('id') : super
|
39
48
|
end
|
40
49
|
|
41
|
-
# overwrite the write_attribute method to write 'id', if present
|
50
|
+
# overwrite the write_attribute method to always write to the 'id'-attribute, if present.
|
51
|
+
# This methods does not check for +relay_id_attribute+ flag!
|
42
52
|
# see @ ActiveRecord::AttributeMethods::Write#write_attribute
|
43
53
|
def write_attribute(attr_name, value)
|
44
|
-
return _write_attribute('id', value) if attr_name.to_s == 'id' &&
|
54
|
+
return _write_attribute('id', value) if attr_name.to_s == 'id' && has_attribute?('id')
|
45
55
|
|
46
56
|
super
|
47
57
|
end
|
48
58
|
|
49
|
-
# overwrite read_attribute method to read 'id', if present
|
59
|
+
# overwrite read_attribute method to read from the 'id'-attribute, if present.
|
60
|
+
# This methods does not check for +relay_id_attribute+ flag!
|
50
61
|
# see @ ActiveRecord::AttributeMethods::Read#read_attribute
|
51
62
|
def read_attribute(attr_name, &block)
|
52
|
-
return _read_attribute('id', &block) if attr_name.to_s == 'id' &&
|
63
|
+
return _read_attribute('id', &block) if attr_name.to_s == 'id' && has_attribute?('id')
|
53
64
|
|
54
65
|
super
|
55
66
|
end
|
@@ -73,6 +84,11 @@ module ElasticsearchRecord
|
|
73
84
|
cache.compute_if_absent(key) { ElasticsearchRecord::StatementCache.create(connection, &block) }
|
74
85
|
end
|
75
86
|
|
87
|
+
# not supported atm - maybe enable in future versions to resolve migrations easier
|
88
|
+
# def primary_class? # :nodoc:
|
89
|
+
# self == ::ElasticsearchRecord::Base
|
90
|
+
# end
|
91
|
+
|
76
92
|
private
|
77
93
|
|
78
94
|
# creates a new relation object and extends it with our own Relation.
|
@@ -3,10 +3,25 @@ module ElasticsearchRecord
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
+
# Rails resolves a pluralized underscore table_name from the class name - which will not work for some models.
|
7
|
+
# To support a general +table_name_prefix+ & +table_name_suffix+ a custom 'index_base_name' can be provided.
|
8
|
+
# @attribute! String|Symbol
|
6
9
|
class_attribute :index_base_name, instance_writer: false, default: nil
|
7
10
|
end
|
8
11
|
|
9
12
|
module ClassMethods
|
13
|
+
# overwrite this method to provide an optional +table_name_prefix+ from the connection config.
|
14
|
+
# @return [String]
|
15
|
+
def table_name_prefix
|
16
|
+
super.presence || connection.table_name_prefix
|
17
|
+
end
|
18
|
+
|
19
|
+
# overwrite this method to provide an optional +table_name_suffix+ from the connection config.
|
20
|
+
# @return [String]
|
21
|
+
def table_name_suffix
|
22
|
+
super.presence || connection.table_name_suffix
|
23
|
+
end
|
24
|
+
|
10
25
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
11
26
|
def undecorated_table_name(model_name)
|
12
27
|
# check the 'index_base_name' first, so +table_name_prefix+ & +table_name_suffix+ can still be used
|
@@ -14,20 +29,29 @@ module ElasticsearchRecord
|
|
14
29
|
end
|
15
30
|
|
16
31
|
# returns the configured +max_result_window+ (default: 10000)
|
32
|
+
# @return [Integer]
|
17
33
|
def max_result_window
|
18
34
|
@max_result_window ||= connection.max_result_window(table_name)
|
19
35
|
end
|
20
36
|
|
37
|
+
# returns true, if the table should behave as +auto_increment+ by creating new records.
|
38
|
+
# resolves the auto_increment status from the tables +_meta+.
|
39
|
+
# @return [Boolean]
|
40
|
+
def auto_increment?
|
41
|
+
@auto_increment ||= !!connection.table_metas(table_name).dig('auto_increment')
|
42
|
+
end
|
43
|
+
|
21
44
|
# returns an array with columns names, that are not virtual (and not a base structure).
|
22
45
|
# so this is a array of real document (+_source+) attributes of the index.
|
23
46
|
# @return [Array<String>]
|
24
47
|
def source_column_names
|
25
|
-
@source_column_names ||= columns.reject(&:virtual).map(&:name) - ActiveRecord::ConnectionAdapters::ElasticsearchAdapter.base_structure_keys
|
48
|
+
@source_column_names ||= columns.reject(&:virtual?).map(&:name) - ActiveRecord::ConnectionAdapters::ElasticsearchAdapter.base_structure_keys
|
26
49
|
end
|
27
50
|
|
28
51
|
# returns an array with columns names, that are searchable (also includes nested fields & properties )
|
52
|
+
# @return [Array<String>]
|
29
53
|
def searchable_column_names
|
30
|
-
@searchable_column_names ||= columns.
|
54
|
+
@searchable_column_names ||= columns.select(&:enabled?).reduce([]) { |m, column|
|
31
55
|
m << column.name
|
32
56
|
m += column.field_names
|
33
57
|
m += column.property_names
|
@@ -3,7 +3,6 @@ module ElasticsearchRecord
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
|
7
6
|
# insert a new record into the Elasticsearch index
|
8
7
|
# NOTICE: We don't want to mess up with the Arel-builder - so we send new data directly to the API
|
9
8
|
# @param [ActiveModel::Attribute] values
|
@@ -12,41 +11,20 @@ module ElasticsearchRecord
|
|
12
11
|
# values is not a "key=>values"-Hash, but a +ActiveModel::Attribute+ - so the casted values gets resolved here
|
13
12
|
values = values.transform_values(&:value)
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
]
|
26
|
-
{id: ids.max}
|
27
|
-
else
|
28
|
-
{}
|
29
|
-
end
|
30
|
-
|
31
|
-
# IMPORTANT: Always drop possible provided 'primary_key' column +_id+.
|
32
|
-
values.delete(self.primary_key)
|
33
|
-
|
34
|
-
# build new query
|
35
|
-
query = ElasticsearchRecord::Query.new(
|
36
|
-
index: table_name,
|
37
|
-
type: ElasticsearchRecord::Query::TYPE_CREATE,
|
38
|
-
body: values,
|
39
|
-
arguments: arguments,
|
40
|
-
refresh: true)
|
41
|
-
|
42
|
-
# execute query and return inserted id
|
43
|
-
id = connection.insert(query, "#{self} Create")
|
14
|
+
# resolve & update a auto_increment value
|
15
|
+
_insert_with_auto_increment(values) do |arguments|
|
16
|
+
# build new query
|
17
|
+
query = ElasticsearchRecord::Query.new(
|
18
|
+
index: table_name,
|
19
|
+
type: ElasticsearchRecord::Query::TYPE_CREATE,
|
20
|
+
# IMPORTANT: always exclude possible provided +_id+ field
|
21
|
+
body: values.except('_id'),
|
22
|
+
arguments: arguments,
|
23
|
+
refresh: true)
|
44
24
|
|
45
|
-
|
46
|
-
connection.
|
25
|
+
# execute query and return inserted id
|
26
|
+
connection.insert(query, "#{self} Create")
|
47
27
|
end
|
48
|
-
|
49
|
-
id
|
50
28
|
end
|
51
29
|
|
52
30
|
# updates a persistent entry in the Elasticsearch index
|
@@ -80,6 +58,39 @@ module ElasticsearchRecord
|
|
80
58
|
# execute query and return total deletes
|
81
59
|
connection.delete(query, "#{self} Delete")
|
82
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# WARNING: BETA!!!
|
65
|
+
# Resolves the +auto_increment+ status from the tables +_meta+ attributes.
|
66
|
+
def _insert_with_auto_increment(values)
|
67
|
+
# check, if the primary_key's values is provided.
|
68
|
+
# so, no need to resolve a +auto_increment+ value, but provide
|
69
|
+
if values[self.primary_key].present?
|
70
|
+
# resolve id from values
|
71
|
+
id = values[self.primary_key]
|
72
|
+
|
73
|
+
yield({id: id})
|
74
|
+
elsif auto_increment?
|
75
|
+
ids = [
|
76
|
+
# we try to resolve the current-auto-increment value from the tables meta
|
77
|
+
connection.table_metas(self.table_name).dig('auto_increment').to_i + 1,
|
78
|
+
# for secure reasons, we also resolve the current maximum value for the primary key
|
79
|
+
self.unscoped.all.maximum(self.primary_key).to_i + 1
|
80
|
+
]
|
81
|
+
|
82
|
+
id = yield({ id: ids.max })
|
83
|
+
|
84
|
+
if id.present?
|
85
|
+
connection.change_meta(self.table_name, :auto_increment, id)
|
86
|
+
end
|
87
|
+
|
88
|
+
# return inserted id
|
89
|
+
id
|
90
|
+
else
|
91
|
+
yield({})
|
92
|
+
end
|
93
|
+
end
|
83
94
|
end
|
84
95
|
end
|
85
96
|
end
|
@@ -22,6 +22,7 @@ module ElasticsearchRecord
|
|
22
22
|
|
23
23
|
# -- INDEX TYPES ---------------------------------------------------------------------------------------------------
|
24
24
|
TYPE_INDEX_CREATE = :index_create
|
25
|
+
TYPE_INDEX_CLONE = :index_clone
|
25
26
|
# INDEX update is not implemented by Elasticsearch
|
26
27
|
# - this is handled through individual updates of +mappings+, +settings+ & +aliases+.
|
27
28
|
# INDEX delete is handled directly as API-call
|
@@ -38,7 +39,8 @@ module ElasticsearchRecord
|
|
38
39
|
TYPE_CREATE, TYPE_UPDATE, TYPE_UPDATE_BY_QUERY, TYPE_DELETE, TYPE_DELETE_BY_QUERY,
|
39
40
|
|
40
41
|
# -- INDEX TYPES
|
41
|
-
TYPE_INDEX_CREATE,
|
42
|
+
TYPE_INDEX_CREATE, TYPE_INDEX_CLONE,
|
43
|
+
TYPE_INDEX_UPDATE_MAPPING, TYPE_INDEX_UPDATE_SETTING, TYPE_INDEX_UPDATE_ALIAS,
|
42
44
|
TYPE_INDEX_DELETE_ALIAS
|
43
45
|
].freeze
|
44
46
|
|
@@ -59,6 +61,7 @@ module ElasticsearchRecord
|
|
59
61
|
GATES = {
|
60
62
|
TYPE_SQL => [:sql, :query],
|
61
63
|
TYPE_INDEX_CREATE => [:indices, :create],
|
64
|
+
TYPE_INDEX_CLONE => [:indices, :clone],
|
62
65
|
TYPE_INDEX_UPDATE_MAPPING => [:indices, :put_mapping],
|
63
66
|
TYPE_INDEX_UPDATE_SETTING => [:indices, :put_settings],
|
64
67
|
TYPE_INDEX_UPDATE_ALIAS => [:indices, :put_alias],
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/schema_migration"
|
4
|
+
|
5
|
+
module ElasticsearchRecord
|
6
|
+
class SchemaMigration < ElasticsearchRecord::Base # :nodoc:
|
7
|
+
class << self
|
8
|
+
def primary_key
|
9
|
+
"version"
|
10
|
+
end
|
11
|
+
|
12
|
+
def table_name
|
13
|
+
"#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_table
|
17
|
+
unless connection.table_exists?(table_name)
|
18
|
+
connection.create_table(table_name, id: false) do |t|
|
19
|
+
t.string :version, **connection.internal_string_options_for_primary_key
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def drop_table
|
25
|
+
connection.drop_table table_name, if_exists: true
|
26
|
+
end
|
27
|
+
|
28
|
+
def normalize_migration_number(number)
|
29
|
+
"%.3d" % number.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def normalized_versions
|
33
|
+
all_versions.map { |v| normalize_migration_number v }
|
34
|
+
end
|
35
|
+
|
36
|
+
def all_versions
|
37
|
+
order(:version).pluck(:version)
|
38
|
+
end
|
39
|
+
|
40
|
+
def table_exists?
|
41
|
+
connection.data_source_exists?(table_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def version
|
46
|
+
super.to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -3,20 +3,20 @@
|
|
3
3
|
module ElasticsearchRecord
|
4
4
|
module Tasks
|
5
5
|
class ElasticsearchDatabaseTasks
|
6
|
-
delegate :connection, :establish_connection, to:
|
6
|
+
delegate :connection, :establish_connection, to: ElasticsearchRecord::Base
|
7
7
|
|
8
8
|
def initialize(db_config)
|
9
9
|
@db_config = db_config
|
10
10
|
end
|
11
11
|
|
12
12
|
def create
|
13
|
-
#
|
14
|
-
|
13
|
+
# 'create' database / cluster is not supported
|
14
|
+
nil
|
15
15
|
end
|
16
16
|
|
17
17
|
def drop
|
18
|
-
#
|
19
|
-
|
18
|
+
# 'drop' database / cluster is not supported
|
19
|
+
nil
|
20
20
|
end
|
21
21
|
|
22
22
|
def purge
|
@@ -24,6 +24,16 @@ module ElasticsearchRecord
|
|
24
24
|
drop
|
25
25
|
end
|
26
26
|
|
27
|
+
def structure_dump(*)
|
28
|
+
$stdout.puts "\n>>> 'structure_dump' elasticsearch is not supported and will be ignored!"
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def structure_load(*)
|
33
|
+
$stdout.puts "\n>>> 'structure_load' elasticsearch is not supported and will be ignored!"
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
27
37
|
private
|
28
38
|
|
29
39
|
attr_reader :db_config
|
data/lib/elasticsearch_record.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Gonsior
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -106,11 +106,13 @@ files:
|
|
106
106
|
- lib/active_record/connection_adapters/elasticsearch/schema_creation.rb
|
107
107
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb
|
108
108
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/attribute_methods.rb
|
109
|
+
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/clone_table_definition.rb
|
109
110
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb
|
110
111
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb
|
111
112
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_alias_definition.rb
|
112
113
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb
|
113
114
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb
|
115
|
+
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_meta_definition.rb
|
114
116
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb
|
115
117
|
- lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb
|
116
118
|
- lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb
|
@@ -161,6 +163,7 @@ files:
|
|
161
163
|
- lib/elasticsearch_record/relation/result_methods.rb
|
162
164
|
- lib/elasticsearch_record/relation/value_methods.rb
|
163
165
|
- lib/elasticsearch_record/result.rb
|
166
|
+
- lib/elasticsearch_record/schema_migration.rb
|
164
167
|
- lib/elasticsearch_record/statement_cache.rb
|
165
168
|
- lib/elasticsearch_record/tasks/elasticsearch_database_tasks.rb
|
166
169
|
- lib/elasticsearch_record/version.rb
|
@@ -189,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
192
|
- !ruby/object:Gem::Version
|
190
193
|
version: '0'
|
191
194
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
195
|
+
rubygems_version: 3.3.7
|
193
196
|
signing_key:
|
194
197
|
specification_version: 4
|
195
198
|
summary: ActiveRecord adapter for Elasticsearch
|