neo4j 8.0.0.alpha.1 → 8.0.0.alpha.2
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/CHANGELOG.md +14 -1
- data/Gemfile +3 -1
- data/lib/neo4j/active_node.rb +8 -0
- data/lib/neo4j/migrations/base.rb +43 -13
- data/lib/neo4j/migrations/helpers/schema.rb +5 -7
- data/lib/neo4j/migrations/runner.rb +62 -5
- data/lib/neo4j/migrations/schema_migration.rb +5 -0
- data/lib/neo4j/model_schema.rb +27 -5
- data/lib/neo4j/session_manager.rb +1 -1
- data/lib/neo4j/shared/property.rb +1 -1
- data/lib/neo4j/tasks/migration.rake +16 -0
- data/lib/neo4j/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74a57c27a28cfa8eda0a7a71413d428e40d2802a
|
4
|
+
data.tar.gz: 51e4a2ef533ae4fbe85daa56365f8e7244ffa7f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f0bdf1994b8871a6c5f333661fa6f77d46f3cb7a0c811b1612580509d8829979dac47461a5978758a732fd04fa940489ba53da41bdf78b8d14303545a8c94f7
|
7
|
+
data.tar.gz: 68279d917be513365d828850cd5967c350d574fe9d0bbf0d4b7aff6d7fb767e2720c0a911e9076a7bd89706401e775a6545801a0ca632fd17dc06aa51b51a102
|
data/CHANGELOG.md
CHANGED
@@ -3,7 +3,20 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
This file should follow the standards specified on [http://keepachangelog.com/]
|
4
4
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
5
5
|
|
6
|
-
## [
|
6
|
+
## [8.0.0.alpha.2] 2016-08-05
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
- Improve migration output format / show execution time in migrations
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
|
14
|
+
- Caching of model index and constraint checks
|
15
|
+
- Error when running schema migrations. Migrations now give a warning and instructions if a migration fails and cannot be recovered
|
16
|
+
- Error when running rake tasks to generate "force" creations of indexes / constraints and there is no migration directory
|
17
|
+
- `WARNING` is no longer displayed for constraints defined from `id_property` (either one which is implict or explict)
|
18
|
+
|
19
|
+
## [8.0.0.alpha.1] 2016-08-02
|
7
20
|
|
8
21
|
### Changed
|
9
22
|
|
data/Gemfile
CHANGED
@@ -7,8 +7,10 @@ gemspec
|
|
7
7
|
if branch = ENV['TRAVIS_BRANCH']
|
8
8
|
same_branch_exists = `curl --head https://github.com/neo4jrb/neo4j-core/tree/#{branch} | head -1`.match(/200 OK/)
|
9
9
|
gem 'neo4j-core', github: 'neo4jrb/neo4j-core', branch: same_branch_exists ? branch : 'master'
|
10
|
+
elsif ENV['USE_LOCAL_CORE']
|
11
|
+
gem 'neo4j-core', path: '../neo4j-core'
|
10
12
|
else
|
11
|
-
gem 'neo4j-core'
|
13
|
+
gem 'neo4j-core'
|
12
14
|
end
|
13
15
|
|
14
16
|
# gem 'active_attr', github: 'neo4jrb/active_attr', branch: 'performance'
|
data/lib/neo4j/active_node.rb
CHANGED
@@ -56,6 +56,12 @@ module Neo4j
|
|
56
56
|
_persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
|
57
57
|
end
|
58
58
|
|
59
|
+
LOADED_CLASSES = []
|
60
|
+
|
61
|
+
def self.loaded_classes
|
62
|
+
LOADED_CLASSES
|
63
|
+
end
|
64
|
+
|
59
65
|
module ClassMethods
|
60
66
|
def nodeify(object)
|
61
67
|
if object.is_a?(::Neo4j::ActiveNode) || object.nil?
|
@@ -68,12 +74,14 @@ module Neo4j
|
|
68
74
|
|
69
75
|
included do
|
70
76
|
include Neo4j::Timestamps if Neo4j::Config[:record_timestamps]
|
77
|
+
LOADED_CLASSES << self
|
71
78
|
|
72
79
|
def self.inherited?
|
73
80
|
!!@inherited
|
74
81
|
end
|
75
82
|
|
76
83
|
def self.inherited(other)
|
84
|
+
LOADED_CLASSES << other
|
77
85
|
other.instance_variable_set('@inherited', true)
|
78
86
|
inherit_id_property(other)
|
79
87
|
attributes.each_pair do |k, v|
|
@@ -11,17 +11,8 @@ module Neo4j
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def migrate(method)
|
14
|
-
ensure_schema_migration_constraint
|
15
14
|
Benchmark.realtime do
|
16
|
-
|
17
|
-
if method == :up
|
18
|
-
up
|
19
|
-
SchemaMigration.create!(migration_id: @migration_id)
|
20
|
-
else
|
21
|
-
down
|
22
|
-
SchemaMigration.find_by!(migration_id: @migration_id).destroy
|
23
|
-
end
|
24
|
-
end
|
15
|
+
method == :up ? migrate_up : migrate_down
|
25
16
|
end
|
26
17
|
end
|
27
18
|
|
@@ -35,9 +26,48 @@ module Neo4j
|
|
35
26
|
|
36
27
|
private
|
37
28
|
|
38
|
-
def
|
39
|
-
SchemaMigration.
|
40
|
-
|
29
|
+
def migrate_up
|
30
|
+
schema = SchemaMigration.create!(migration_id: @migration_id, incomplete: true)
|
31
|
+
begin
|
32
|
+
run_migration(:up)
|
33
|
+
rescue StandardError => e
|
34
|
+
schema.destroy
|
35
|
+
handle_migration_error!(e)
|
36
|
+
end
|
37
|
+
schema.update!(incomplete: nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
def migrate_down
|
41
|
+
schema = SchemaMigration.find_by!(migration_id: @migration_id)
|
42
|
+
schema.update!(incomplete: true)
|
43
|
+
begin
|
44
|
+
run_migration(:down)
|
45
|
+
rescue StandardError => e
|
46
|
+
schema.update!(incomplete: nil)
|
47
|
+
handle_migration_error!(e)
|
48
|
+
end
|
49
|
+
schema.destroy
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_migration(direction)
|
53
|
+
migration_transaction { log_queries { public_send(direction) } }
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_migration_error!(e)
|
57
|
+
fail e unless e.message =~ /Cannot perform data updates in a transaction that has performed schema updates./
|
58
|
+
fail MigrationError,
|
59
|
+
"#{e.message}. Please add `disable_transactions!` in your migration file."
|
60
|
+
end
|
61
|
+
|
62
|
+
def migration_transaction(&block)
|
63
|
+
ActiveBase.run_transaction(transactions?, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def log_queries
|
67
|
+
subscriber = Neo4j::Core::CypherSession::Adaptors::Base.subscribe_to_query(&method(:output))
|
68
|
+
yield
|
69
|
+
ensure
|
70
|
+
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
41
71
|
end
|
42
72
|
end
|
43
73
|
end
|
@@ -8,9 +8,10 @@ module Neo4j
|
|
8
8
|
|
9
9
|
def add_constraint(label, property, options = {})
|
10
10
|
force = options[:force] || false
|
11
|
+
type = options[:type] || :uniqueness
|
11
12
|
label_object = ActiveBase.label_object(label)
|
12
13
|
fail_duplicate_constraint_or_index!(:constraint, label, property) if !force && label_object.constraint?(property)
|
13
|
-
label_object.create_constraint(property, type:
|
14
|
+
label_object.create_constraint(property, type: type)
|
14
15
|
end
|
15
16
|
|
16
17
|
def add_index(label, property, options = {})
|
@@ -20,14 +21,11 @@ module Neo4j
|
|
20
21
|
label_object.create_index(property)
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def drop_constraint(label, property)
|
24
|
+
def drop_constraint(label, property, options = {})
|
25
|
+
type = options[:type] || :uniqueness
|
28
26
|
label_object = ActiveBase.label_object(label)
|
29
27
|
fail_missing_constraint_or_index!(:constraint, label, property) if !label_object.constraint?(property)
|
30
|
-
label_object.drop_constraint(property, type:
|
28
|
+
label_object.drop_constraint(property, type: type)
|
31
29
|
end
|
32
30
|
|
33
31
|
def drop_index(label, property)
|
@@ -7,15 +7,18 @@ module Neo4j
|
|
7
7
|
STATUS_TABLE_HEADER = ['Status'.freeze, 'Migration ID'.freeze, 'Migration Name'.freeze].freeze
|
8
8
|
UP_MESSAGE = 'up'.freeze
|
9
9
|
DOWN_MESSAGE = 'down'.freeze
|
10
|
+
INCOMPLETE_MESSAGE = 'incomplete'.freeze
|
10
11
|
MIGRATION_RUNNING = {up: 'running'.freeze, down: 'reverting'.freeze}.freeze
|
11
12
|
MIGRATION_DONE = {up: 'migrated'.freeze, down: 'reverted'.freeze}.freeze
|
12
13
|
|
13
14
|
def initialize
|
14
15
|
SchemaMigration.mapped_label.create_constraint(:migration_id, type: :unique)
|
15
|
-
@
|
16
|
+
@schema_migrations = SchemaMigration.all.to_a
|
17
|
+
@up_versions = SortedSet.new(@schema_migrations.map(&:migration_id))
|
16
18
|
end
|
17
19
|
|
18
20
|
def all
|
21
|
+
handle_incomplete_states!
|
19
22
|
migration_files.each do |migration_file|
|
20
23
|
next if up?(migration_file.version)
|
21
24
|
migrate(:up, migration_file)
|
@@ -23,18 +26,21 @@ module Neo4j
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def up(version)
|
29
|
+
handle_incomplete_states!
|
26
30
|
migration_file = find_by_version!(version)
|
27
31
|
return if up?(version)
|
28
32
|
migrate(:up, migration_file)
|
29
33
|
end
|
30
34
|
|
31
35
|
def down(version)
|
36
|
+
handle_incomplete_states!
|
32
37
|
migration_file = find_by_version!(version)
|
33
38
|
return unless up?(version)
|
34
39
|
migrate(:down, migration_file)
|
35
40
|
end
|
36
41
|
|
37
42
|
def rollback(steps)
|
43
|
+
handle_incomplete_states!
|
38
44
|
@up_versions.to_a.reverse.first(steps).each do |version|
|
39
45
|
down(version)
|
40
46
|
end
|
@@ -44,15 +50,55 @@ module Neo4j
|
|
44
50
|
output STATUS_TABLE_FORMAT, *STATUS_TABLE_HEADER
|
45
51
|
output SEPARATOR
|
46
52
|
all_migrations.each do |version|
|
47
|
-
status =
|
53
|
+
status = migration_status(version)
|
48
54
|
migration_file = find_by_version(version)
|
49
55
|
migration_name = migration_file ? migration_file.class_name : FILE_MISSING
|
50
56
|
output STATUS_TABLE_FORMAT, status, version, migration_name
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
60
|
+
def resolve(version)
|
61
|
+
SchemaMigration.find_by!(migration_id: version).update!(incomplete: false)
|
62
|
+
output "Migration #{version} resolved."
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset(version)
|
66
|
+
SchemaMigration.find_by!(migration_id: version).destroy
|
67
|
+
output "Migration #{version} reset."
|
68
|
+
end
|
69
|
+
|
54
70
|
private
|
55
71
|
|
72
|
+
def migration_status(version)
|
73
|
+
return DOWN_MESSAGE unless up?(version)
|
74
|
+
incomplete_states.find { |v| v.migration_id == version } ? INCOMPLETE_MESSAGE : UP_MESSAGE
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_incomplete_states!
|
78
|
+
return unless incomplete_states.any?
|
79
|
+
incomplete_versions = incomplete_states.map(&:migration_id)
|
80
|
+
fail MigrationError, <<-MSG
|
81
|
+
There are migrations struck in an incomplete states, that could not be fixed automatically:
|
82
|
+
#{incomplete_versions.join('\n')}
|
83
|
+
This can happen when there's a critical error inside a migration.
|
84
|
+
|
85
|
+
If you think they were was completed correctly, run:
|
86
|
+
|
87
|
+
#{task_migration_messages('resolve', incomplete_versions)}
|
88
|
+
|
89
|
+
If you want to reset and run the migration again, run:
|
90
|
+
|
91
|
+
#{task_migration_messages('reset', incomplete_versions)}
|
92
|
+
|
93
|
+
MSG
|
94
|
+
end
|
95
|
+
|
96
|
+
def task_migration_messages(type, versions)
|
97
|
+
versions.map do |version|
|
98
|
+
"rake neo4j:migrate:#{type} VERSION=#{version}"
|
99
|
+
end.join("\n")
|
100
|
+
end
|
101
|
+
|
56
102
|
def up?(version)
|
57
103
|
@up_versions.include?(version)
|
58
104
|
end
|
@@ -65,15 +111,22 @@ module Neo4j
|
|
65
111
|
end
|
66
112
|
|
67
113
|
def migration_message(direction, migration)
|
68
|
-
|
69
|
-
yield
|
70
|
-
|
114
|
+
output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_RUNNING[direction]}..."
|
115
|
+
time = format('%.4fs', yield)
|
116
|
+
output_migration_message "#{migration.version} #{migration.class_name}: #{MIGRATION_DONE[direction]} (#{time})"
|
117
|
+
output ''
|
71
118
|
end
|
72
119
|
|
73
120
|
def output(*string_format)
|
74
121
|
puts format(*string_format) unless !!ENV['MIGRATIONS_SILENCED']
|
75
122
|
end
|
76
123
|
|
124
|
+
def output_migration_message(message)
|
125
|
+
out = "== #{message} "
|
126
|
+
tail = '=' * [0, 80 - out.length].max
|
127
|
+
output "#{out}#{tail}"
|
128
|
+
end
|
129
|
+
|
77
130
|
def find_by_version!(version)
|
78
131
|
find_by_version(version) || fail(UnknownMigrationVersionError, "No such migration #{version}")
|
79
132
|
end
|
@@ -94,6 +147,10 @@ module Neo4j
|
|
94
147
|
files.map { |file_path| MigrationFile.new(file_path) }
|
95
148
|
end
|
96
149
|
|
150
|
+
def incomplete_states
|
151
|
+
@incomplete_states ||= SortedSet.new(@schema_migrations.select(&:incomplete?))
|
152
|
+
end
|
153
|
+
|
97
154
|
def files
|
98
155
|
Dir[files_path].sort
|
99
156
|
end
|
data/lib/neo4j/model_schema.rb
CHANGED
@@ -30,22 +30,26 @@ module Neo4j
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def model_constraints
|
33
|
+
return @model_constraints if @model_constraints
|
34
|
+
|
33
35
|
constraints = Neo4j::ActiveBase.current_session.constraints.each_with_object({}) do |row, result|
|
34
36
|
result[row[:label]] ||= []
|
35
37
|
result[row[:label]] << row[:properties]
|
36
38
|
end
|
37
39
|
|
38
|
-
schema_elements_list(MODEL_CONSTRAINTS, constraints)
|
40
|
+
@model_constraints = schema_elements_list(MODEL_CONSTRAINTS, constraints)
|
39
41
|
end
|
40
42
|
|
41
43
|
def model_indexes
|
44
|
+
return @model_indexes if @model_indexes
|
45
|
+
|
42
46
|
indexes = Neo4j::ActiveBase.current_session.indexes.each_with_object({}) do |row, result|
|
43
47
|
result[row[:label]] ||= []
|
44
48
|
result[row[:label]] << row[:properties]
|
45
49
|
end
|
46
50
|
|
47
|
-
schema_elements_list(MODEL_INDEXES, indexes) +
|
48
|
-
|
51
|
+
@model_indexes = schema_elements_list(MODEL_INDEXES, indexes) +
|
52
|
+
schema_elements_list(REQUIRED_INDEXES, indexes).reject(&:last)
|
49
53
|
# reject required indexes which are already in the DB
|
50
54
|
end
|
51
55
|
|
@@ -60,12 +64,28 @@ module Neo4j
|
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
67
|
+
def ensure_model_data_state!
|
68
|
+
# If we load a new model, reset everything
|
69
|
+
if @previously_loaded_models_count != Neo4j::ActiveNode.loaded_classes.size
|
70
|
+
# Make sure we've finalized id_property details and have called
|
71
|
+
# add_ constraint/index methods above
|
72
|
+
Neo4j::ActiveNode.loaded_classes.each(&:ensure_id_property_info!)
|
73
|
+
reload_models_data!
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def reload_models_data!
|
78
|
+
@previously_loaded_models_count = Neo4j::ActiveNode.loaded_classes.size
|
79
|
+
@model_indexes = @model_constraints = nil
|
80
|
+
end
|
81
|
+
|
63
82
|
def validate_model_schema!
|
83
|
+
ensure_model_data_state!
|
64
84
|
messages = {index: [], constraint: []}
|
65
85
|
[[:constraint, model_constraints], [:index, model_indexes]].each do |type, schema_elements|
|
66
86
|
schema_elements.map do |model, label, property_name, exists|
|
67
87
|
if exists
|
68
|
-
log_warning!(type, model, property_name)
|
88
|
+
log_warning!(type, model, property_name) if model.id_property_name.to_sym != property_name
|
69
89
|
else
|
70
90
|
messages[type] << force_add_message(type, label, property_name)
|
71
91
|
end
|
@@ -79,11 +99,13 @@ module Neo4j
|
|
79
99
|
|
80
100
|
def validation_error_message(messages)
|
81
101
|
<<MSG
|
82
|
-
Some schema elements were defined by the model (which is no longer
|
102
|
+
Some schema elements were defined by the model (which is no longer supported), but they do not exist in the database. Run the following to create them:
|
83
103
|
|
84
104
|
#{messages[:constraint].join("\n")}
|
85
105
|
#{messages[:index].join("\n")}
|
86
106
|
|
107
|
+
And then run `rake neo4j:migrate`
|
108
|
+
|
87
109
|
(zshell users may need to escape the brackets)
|
88
110
|
MSG
|
89
111
|
end
|
@@ -113,7 +113,7 @@ module Neo4j::Shared
|
|
113
113
|
|
114
114
|
def_delegators :declared_properties, :serialized_properties, :serialized_properties=, :serialize, :declared_property_defaults
|
115
115
|
|
116
|
-
VALID_PROPERTY_OPTIONS = %w(type default index constraint serializer typecaster
|
116
|
+
VALID_PROPERTY_OPTIONS = %w(type default index constraint serializer typecaster).map(&:to_sym)
|
117
117
|
# Defines a property on the class
|
118
118
|
#
|
119
119
|
# See active_attr gem for allowed options, e.g which type
|
@@ -62,6 +62,20 @@ namespace :neo4j do
|
|
62
62
|
runner = Neo4j::Migrations::Runner.new
|
63
63
|
runner.status
|
64
64
|
end
|
65
|
+
|
66
|
+
desc 'Resolve an incomplete version state'
|
67
|
+
task resolve: :environment do
|
68
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
69
|
+
runner = Neo4j::Migrations::Runner.new
|
70
|
+
runner.resolve version
|
71
|
+
end
|
72
|
+
|
73
|
+
desc 'Resolve an incomplete version state'
|
74
|
+
task reset: :environment do
|
75
|
+
version = ENV['VERSION'] || fail(ArgumentError, 'VERSION is required')
|
76
|
+
runner = Neo4j::Migrations::Runner.new
|
77
|
+
runner.reset version
|
78
|
+
end
|
65
79
|
end
|
66
80
|
|
67
81
|
desc 'Rollbacks migrations given a STEP number'
|
@@ -84,6 +98,8 @@ namespace :neo4j do
|
|
84
98
|
|
85
99
|
migration_class_name = "ForceCreate#{label.camelize}#{property_name.camelize}#{index_or_constraint.capitalize}"
|
86
100
|
|
101
|
+
require 'fileutils'
|
102
|
+
FileUtils.mkdir_p('db/neo4j/migrate')
|
87
103
|
path = File.join('db/neo4j/migrate', "#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration_class_name.underscore}.rb")
|
88
104
|
|
89
105
|
content = <<-CONTENT
|
data/lib/neo4j/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neo4j
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.0.alpha.
|
4
|
+
version: 8.0.0.alpha.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Ronge, Brian Underwood, Chris Grigg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: orm_adapter
|
@@ -210,8 +210,10 @@ dependencies:
|
|
210
210
|
- - "~>"
|
211
211
|
- !ruby/object:Gem::Version
|
212
212
|
version: 0.39.0
|
213
|
-
description:
|
214
|
-
|
213
|
+
description: 'A Neo4j OGM (Object-Graph-Mapper) for use in Ruby on Rails and Rack
|
214
|
+
frameworks heavily inspired by ActiveRecord.
|
215
|
+
|
216
|
+
'
|
215
217
|
email: andreas.ronge@gmail.com, brian@brian-underwood.codes, chris@subvertallmedia.com
|
216
218
|
executables:
|
217
219
|
- neo4j-jars
|
@@ -358,7 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
358
360
|
version: 1.3.1
|
359
361
|
requirements: []
|
360
362
|
rubyforge_project: neo4j
|
361
|
-
rubygems_version: 2.
|
363
|
+
rubygems_version: 2.5.1
|
362
364
|
signing_key:
|
363
365
|
specification_version: 4
|
364
366
|
summary: A graph database for Ruby
|