activegraph 10.0.0.pre.alpha.6
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 +7 -0
- data/CHANGELOG.md +1989 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +24 -0
- data/README.md +107 -0
- data/bin/rake +17 -0
- data/config/locales/en.yml +5 -0
- data/config/neo4j/add_classnames.yml +1 -0
- data/config/neo4j/config.yml +38 -0
- data/lib/neo4j.rb +116 -0
- data/lib/neo4j/active_base.rb +89 -0
- data/lib/neo4j/active_node.rb +108 -0
- data/lib/neo4j/active_node/callbacks.rb +8 -0
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
- data/lib/neo4j/active_node/enum.rb +26 -0
- data/lib/neo4j/active_node/has_n.rb +612 -0
- data/lib/neo4j/active_node/has_n/association.rb +278 -0
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/id_property.rb +224 -0
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/initialize.rb +21 -0
- data/lib/neo4j/active_node/labels.rb +207 -0
- data/lib/neo4j/active_node/labels/index.rb +37 -0
- data/lib/neo4j/active_node/labels/reloading.rb +21 -0
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +54 -0
- data/lib/neo4j/active_node/orm_adapter.rb +82 -0
- data/lib/neo4j/active_node/persistence.rb +187 -0
- data/lib/neo4j/active_node/property.rb +60 -0
- data/lib/neo4j/active_node/query.rb +76 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
- data/lib/neo4j/active_node/query_methods.rb +68 -0
- data/lib/neo4j/active_node/reflection.rb +86 -0
- data/lib/neo4j/active_node/rels.rb +11 -0
- data/lib/neo4j/active_node/scope.rb +166 -0
- data/lib/neo4j/active_node/unpersisted.rb +48 -0
- data/lib/neo4j/active_node/validations.rb +59 -0
- data/lib/neo4j/active_rel.rb +67 -0
- data/lib/neo4j/active_rel/callbacks.rb +15 -0
- data/lib/neo4j/active_rel/initialize.rb +28 -0
- data/lib/neo4j/active_rel/persistence.rb +134 -0
- data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
- data/lib/neo4j/active_rel/property.rb +95 -0
- data/lib/neo4j/active_rel/query.rb +101 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
- data/lib/neo4j/active_rel/related_node.rb +87 -0
- data/lib/neo4j/active_rel/types.rb +82 -0
- data/lib/neo4j/active_rel/validations.rb +8 -0
- data/lib/neo4j/ansi.rb +14 -0
- data/lib/neo4j/class_arguments.rb +39 -0
- data/lib/neo4j/config.rb +135 -0
- data/lib/neo4j/core.rb +14 -0
- data/lib/neo4j/core/connection_failed_error.rb +6 -0
- data/lib/neo4j/core/cypher_error.rb +37 -0
- data/lib/neo4j/core/driver.rb +66 -0
- data/lib/neo4j/core/has_uri.rb +63 -0
- data/lib/neo4j/core/instrumentable.rb +36 -0
- data/lib/neo4j/core/label.rb +158 -0
- data/lib/neo4j/core/logging.rb +44 -0
- data/lib/neo4j/core/node.rb +23 -0
- data/lib/neo4j/core/querable.rb +88 -0
- data/lib/neo4j/core/query.rb +487 -0
- data/lib/neo4j/core/query_builder.rb +32 -0
- data/lib/neo4j/core/query_clauses.rb +727 -0
- data/lib/neo4j/core/query_ext.rb +24 -0
- data/lib/neo4j/core/query_find_in_batches.rb +49 -0
- data/lib/neo4j/core/relationship.rb +13 -0
- data/lib/neo4j/core/responses.rb +50 -0
- data/lib/neo4j/core/result.rb +33 -0
- data/lib/neo4j/core/schema.rb +30 -0
- data/lib/neo4j/core/schema_errors.rb +12 -0
- data/lib/neo4j/core/wrappable.rb +30 -0
- data/lib/neo4j/errors.rb +57 -0
- data/lib/neo4j/migration.rb +148 -0
- data/lib/neo4j/migrations.rb +27 -0
- data/lib/neo4j/migrations/base.rb +77 -0
- data/lib/neo4j/migrations/check_pending.rb +20 -0
- data/lib/neo4j/migrations/helpers.rb +105 -0
- data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
- data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
- data/lib/neo4j/migrations/helpers/schema.rb +51 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +195 -0
- data/lib/neo4j/migrations/schema.rb +44 -0
- data/lib/neo4j/migrations/schema_migration.rb +14 -0
- data/lib/neo4j/model_schema.rb +139 -0
- data/lib/neo4j/paginated.rb +27 -0
- data/lib/neo4j/railtie.rb +105 -0
- data/lib/neo4j/schema/operation.rb +102 -0
- data/lib/neo4j/shared.rb +60 -0
- data/lib/neo4j/shared/attributes.rb +216 -0
- data/lib/neo4j/shared/callbacks.rb +68 -0
- data/lib/neo4j/shared/cypher.rb +37 -0
- data/lib/neo4j/shared/declared_properties.rb +204 -0
- data/lib/neo4j/shared/declared_property.rb +109 -0
- data/lib/neo4j/shared/declared_property/index.rb +37 -0
- data/lib/neo4j/shared/enum.rb +167 -0
- data/lib/neo4j/shared/filtered_hash.rb +79 -0
- data/lib/neo4j/shared/identity.rb +34 -0
- data/lib/neo4j/shared/initialize.rb +64 -0
- data/lib/neo4j/shared/marshal.rb +23 -0
- data/lib/neo4j/shared/mass_assignment.rb +64 -0
- data/lib/neo4j/shared/permitted_attributes.rb +28 -0
- data/lib/neo4j/shared/persistence.rb +282 -0
- data/lib/neo4j/shared/property.rb +240 -0
- data/lib/neo4j/shared/query_factory.rb +102 -0
- data/lib/neo4j/shared/rel_type_converters.rb +43 -0
- data/lib/neo4j/shared/serialized_properties.rb +30 -0
- data/lib/neo4j/shared/type_converters.rb +433 -0
- data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
- data/lib/neo4j/shared/typecaster.rb +53 -0
- data/lib/neo4j/shared/validations.rb +44 -0
- data/lib/neo4j/tasks/migration.rake +202 -0
- data/lib/neo4j/timestamps.rb +11 -0
- data/lib/neo4j/timestamps/created.rb +9 -0
- data/lib/neo4j/timestamps/updated.rb +9 -0
- data/lib/neo4j/transaction.rb +139 -0
- data/lib/neo4j/type_converters.rb +7 -0
- data/lib/neo4j/undeclared_properties.rb +53 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/neo4j/wrapper.rb +4 -0
- data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
- data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
- data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
- data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
- data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
- data/lib/rails/generators/neo4j_generator.rb +119 -0
- data/neo4j.gemspec +51 -0
- metadata +421 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
extend ActiveSupport::Autoload
|
|
4
|
+
autoload :Helpers
|
|
5
|
+
autoload :MigrationFile
|
|
6
|
+
autoload :Base
|
|
7
|
+
autoload :Runner
|
|
8
|
+
autoload :SchemaMigration
|
|
9
|
+
autoload :CheckPending
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def check_for_pending_migrations!
|
|
13
|
+
return if Neo4j::Config.configuration['skip_migration_check']
|
|
14
|
+
|
|
15
|
+
runner = Neo4j::Migrations::Runner.new
|
|
16
|
+
pending = runner.pending_migrations
|
|
17
|
+
fail ::Neo4j::PendingMigrationError, pending if pending.any?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_accessor :currently_running_migrations
|
|
21
|
+
|
|
22
|
+
def maintain_test_schema!
|
|
23
|
+
Neo4j::Migrations::Runner.new(silenced: true).all
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
class Base
|
|
4
|
+
include Neo4j::Migrations::Helpers
|
|
5
|
+
include Neo4j::Migrations::Helpers::Schema
|
|
6
|
+
include Neo4j::Migrations::Helpers::IdProperty
|
|
7
|
+
include Neo4j::Migrations::Helpers::Relationships
|
|
8
|
+
|
|
9
|
+
def initialize(migration_id, options = {})
|
|
10
|
+
@migration_id = migration_id
|
|
11
|
+
@silenced = options[:silenced]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def migrate(method)
|
|
15
|
+
Benchmark.realtime do
|
|
16
|
+
method == :up ? migrate_up : migrate_down
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def up
|
|
21
|
+
fail NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def down
|
|
25
|
+
fail NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def migrate_up
|
|
31
|
+
schema = SchemaMigration.create!(migration_id: @migration_id, incomplete: true)
|
|
32
|
+
begin
|
|
33
|
+
run_migration(:up)
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
schema.destroy if transactions?
|
|
36
|
+
handle_migration_error!(e)
|
|
37
|
+
else
|
|
38
|
+
schema.update!(incomplete: nil)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def migrate_down
|
|
43
|
+
schema = SchemaMigration.find_by!(migration_id: @migration_id)
|
|
44
|
+
schema.update!(incomplete: true)
|
|
45
|
+
begin
|
|
46
|
+
run_migration(:down)
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
schema.update!(incomplete: nil) if transactions?
|
|
49
|
+
handle_migration_error!(e)
|
|
50
|
+
else
|
|
51
|
+
schema.destroy
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def run_migration(direction)
|
|
56
|
+
migration_transaction { log_queries { public_send(direction) } }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def handle_migration_error!(e)
|
|
60
|
+
fail e unless e.message =~ /Cannot perform data updates in a transaction that has performed schema updates./
|
|
61
|
+
fail MigrationError,
|
|
62
|
+
"#{e.message}. Please add `disable_transactions!` in your migration file."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def migration_transaction(&block)
|
|
66
|
+
ActiveBase.run_transaction(transactions?, &block)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def log_queries
|
|
70
|
+
subscriber = Neo4j::Transaction.subscribe_to_query(&method(:output))
|
|
71
|
+
yield
|
|
72
|
+
ensure
|
|
73
|
+
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
class CheckPending
|
|
4
|
+
def initialize(app)
|
|
5
|
+
@app = app
|
|
6
|
+
@last_check = 0
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
latest_migration = Neo4j::Migrations::Runner.latest_migration
|
|
11
|
+
mtime = latest_migration ? latest_migration.version.to_i : 0
|
|
12
|
+
if @last_check < mtime
|
|
13
|
+
Neo4j::Migrations.check_for_pending_migrations!
|
|
14
|
+
@last_check = mtime
|
|
15
|
+
end
|
|
16
|
+
@app.call(env)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Migrations
|
|
5
|
+
module Helpers
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
extend ActiveSupport::Autoload
|
|
8
|
+
|
|
9
|
+
autoload :Schema
|
|
10
|
+
autoload :IdProperty
|
|
11
|
+
autoload :Relationships
|
|
12
|
+
|
|
13
|
+
PROPERTY_ALREADY_DEFINED = 'Property `%{new_property}` is already defined in `%{label}`. '\
|
|
14
|
+
'To overwrite, call `remove_property(:%{label}, :%{new_property})` before this method.'.freeze
|
|
15
|
+
|
|
16
|
+
def remove_property(label, property)
|
|
17
|
+
by_label(label).remove("n.#{property}").exec
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def rename_property(label, old_property, new_property)
|
|
21
|
+
fail Neo4j::MigrationError, format(PROPERTY_ALREADY_DEFINED, new_property: new_property, label: label) if property_exists?(label, new_property)
|
|
22
|
+
by_label(label).set("n.#{new_property} = n.#{old_property}")
|
|
23
|
+
.remove("n.#{old_property}").exec
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def drop_nodes(label)
|
|
27
|
+
query.match(n: label)
|
|
28
|
+
.optional_match('(n)-[r]-()')
|
|
29
|
+
.delete(:r, :n).exec
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_labels(label, new_labels)
|
|
33
|
+
by_label(label).set(n: new_labels).exec
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def add_label(label, new_label)
|
|
37
|
+
add_labels(label, [new_label])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def remove_labels(label, labels_to_remove)
|
|
41
|
+
by_label(label).remove(n: labels_to_remove).exec
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def remove_label(label, label_to_remove)
|
|
45
|
+
remove_labels(label, [label_to_remove])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def rename_label(old_label, new_label)
|
|
49
|
+
by_label(old_label).set(n: new_label).remove(n: old_label).exec
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def execute(string, params = {})
|
|
53
|
+
ActiveBase.query(string, params).to_a
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def say_with_time(message)
|
|
57
|
+
say(message)
|
|
58
|
+
result = nil
|
|
59
|
+
time = Benchmark.measure { result = yield }
|
|
60
|
+
say format('%.4fs', time.real), :subitem
|
|
61
|
+
say("#{result} rows", :subitem) if result.is_a?(Integer)
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def say(message, subitem = false)
|
|
66
|
+
output "#{subitem ? ' ->' : '--'} #{message}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def query(*args)
|
|
70
|
+
ActiveBase.new_query(*args)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
|
|
75
|
+
def output(*string_format)
|
|
76
|
+
puts format(*string_format) unless @silenced
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def transactions?
|
|
80
|
+
self.class.transaction?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def property_exists?(label, property)
|
|
86
|
+
by_label(label).where("EXISTS(n.#{property})").return(:n).any?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def by_label(label, options = {})
|
|
90
|
+
symbol = options[:symbol] || :n
|
|
91
|
+
query.match(symbol => label)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
module ClassMethods
|
|
95
|
+
def disable_transactions!
|
|
96
|
+
@disable_transactions = true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def transaction?
|
|
100
|
+
!@disable_transactions
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
module Helpers
|
|
4
|
+
module IdProperty
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
def populate_id_property(label)
|
|
8
|
+
model = label.to_s.constantize
|
|
9
|
+
max_per_batch = (ENV['MAX_PER_BATCH'] || default_max_per_batch).to_i
|
|
10
|
+
|
|
11
|
+
last_time_taken = nil
|
|
12
|
+
|
|
13
|
+
until (nodes_left = idless_count(label, model.primary_key)) == 0
|
|
14
|
+
print_status(last_time_taken, max_per_batch, nodes_left)
|
|
15
|
+
|
|
16
|
+
count = [nodes_left, max_per_batch].min
|
|
17
|
+
last_time_taken = Benchmark.realtime do
|
|
18
|
+
max_per_batch = id_batch_set(label, model.primary_key, Array.new(count) { new_id_for(model) }, count)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def idless_count(label, id_property)
|
|
26
|
+
query.match(n: label).where("NOT EXISTS(n.#{id_property})").pluck('COUNT(n) AS ids').first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def id_batch_set(label, id_property, new_ids, count)
|
|
30
|
+
tx = ActiveBase.new_transaction
|
|
31
|
+
|
|
32
|
+
execute("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
|
33
|
+
with COLLECT(n) as nodes, #{new_ids} as ids
|
|
34
|
+
FOREACH(i in range(0,#{count - 1})|
|
|
35
|
+
FOREACH(node in [nodes[i]]|
|
|
36
|
+
SET node.#{id_property} = ids[i]))
|
|
37
|
+
RETURN distinct(true)
|
|
38
|
+
LIMIT #{count}")
|
|
39
|
+
|
|
40
|
+
count
|
|
41
|
+
rescue Neo4j::Server::CypherResponse::ResponseError, Faraday::TimeoutError
|
|
42
|
+
new_max_per_batch = (max_per_batch * 0.8).round
|
|
43
|
+
output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
|
44
|
+
new_max_per_batch
|
|
45
|
+
ensure
|
|
46
|
+
tx.close
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def print_status(last_time_taken, max_per_batch, nodes_left)
|
|
50
|
+
time_per_node = last_time_taken / max_per_batch if last_time_taken
|
|
51
|
+
message = if time_per_node
|
|
52
|
+
eta_seconds = (nodes_left * time_per_node).round
|
|
53
|
+
"#{nodes_left} nodes left. Last batch: #{(time_per_node * 1000.0).round(1)}ms / node (ETA: #{eta_seconds / 60} minutes)"
|
|
54
|
+
else
|
|
55
|
+
'Running first batch...'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
output message
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def default_max_per_batch
|
|
62
|
+
900
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def new_id_for(model)
|
|
66
|
+
if model.id_property_info[:type][:auto]
|
|
67
|
+
SecureRandom.uuid
|
|
68
|
+
else
|
|
69
|
+
model.new.send(model.id_property_info[:type][:on])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
module Helpers
|
|
4
|
+
module Relationships
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
DEFAULT_MAX_PER_BATCH = 1000
|
|
8
|
+
|
|
9
|
+
def change_relations_style(relationships, old_style, new_style, params = {})
|
|
10
|
+
relationships.each do |rel|
|
|
11
|
+
relabel_relation(relationship_style(rel, old_style), relationship_style(rel, new_style), params)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def relabel_relation(old_name, new_name, params = {})
|
|
16
|
+
relation_query = match_relation(old_name, params)
|
|
17
|
+
|
|
18
|
+
max_per_batch = (ENV['MAX_PER_BATCH'] || DEFAULT_MAX_PER_BATCH).to_i
|
|
19
|
+
|
|
20
|
+
count = count_relations(relation_query)
|
|
21
|
+
output "Indexing #{count} #{old_name}s into #{new_name}..."
|
|
22
|
+
while count > 0
|
|
23
|
+
relation_query.create("(a)-[r2:`#{new_name}`]->(b)").set('r2 = r').with(:r).limit(max_per_batch).delete(:r).exec
|
|
24
|
+
count = count_relations(relation_query)
|
|
25
|
+
output "... #{count} #{old_name}'s left to go.." if count > 0
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def match_relation(label, params = {})
|
|
32
|
+
from = params[:from] ? "(a:`#{params[:from]}`)" : '(a)'
|
|
33
|
+
to = params[:to] ? "(b:`#{params[:to]}`)" : '(b)'
|
|
34
|
+
relation = arrow_cypher(label, params[:direction])
|
|
35
|
+
|
|
36
|
+
query.match("#{from}#{relation}#{to}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def arrow_cypher(label, direction)
|
|
40
|
+
case direction
|
|
41
|
+
when :in
|
|
42
|
+
"<-[r:`#{label}`]-"
|
|
43
|
+
when :both
|
|
44
|
+
"<-[r:`#{label}`]->"
|
|
45
|
+
else
|
|
46
|
+
"-[r:`#{label}`]->"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def count_relations(query)
|
|
51
|
+
query.pluck('COUNT(r)').first
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def relationship_style(relationship, format)
|
|
55
|
+
case format.to_s
|
|
56
|
+
when 'lower_hashtag' then "##{relationship.downcase}"
|
|
57
|
+
when 'lower' then relationship.downcase
|
|
58
|
+
when 'upper' then relationship.upcase
|
|
59
|
+
else
|
|
60
|
+
fail("Invalid relationship type style `#{format}`.")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
module Helpers
|
|
4
|
+
module Schema
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
MISSING_CONSTRAINT_OR_INDEX = 'No such %{type} for %{label}#%{property}'.freeze
|
|
7
|
+
DUPLICATE_CONSTRAINT_OR_INDEX = 'Duplicate %{type} for %{label}#%{property}'.freeze
|
|
8
|
+
|
|
9
|
+
def add_constraint(label, property, options = {})
|
|
10
|
+
force = options[:force] || false
|
|
11
|
+
type = options[:type] || :uniqueness
|
|
12
|
+
label_object = ActiveBase.label_object(label)
|
|
13
|
+
fail_duplicate_constraint_or_index!(:constraint, label, property) if !force && label_object.constraint?(property)
|
|
14
|
+
label_object.create_constraint(property, type: type)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_index(label, property, options = {})
|
|
18
|
+
force = options[:force] || false
|
|
19
|
+
label_object = ActiveBase.label_object(label)
|
|
20
|
+
fail_duplicate_constraint_or_index!(:index, label, property) if !force && label_object.index?(property)
|
|
21
|
+
label_object.create_index(property)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def drop_constraint(label, property, options = {})
|
|
25
|
+
type = options[:type] || :uniqueness
|
|
26
|
+
label_object = ActiveBase.label_object(label)
|
|
27
|
+
fail_missing_constraint_or_index!(:constraint, label, property) if !options[:force] && !label_object.constraint?(property)
|
|
28
|
+
label_object.drop_constraint(property, type: type)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def drop_index(label, property, options = {})
|
|
32
|
+
label_object = ActiveBase.label_object(label)
|
|
33
|
+
fail_missing_constraint_or_index!(:index, label, property) if !options[:force] && !label_object.index?(property)
|
|
34
|
+
label_object.drop_index(property)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
protected
|
|
38
|
+
|
|
39
|
+
def fail_missing_constraint_or_index!(type, label, property)
|
|
40
|
+
fail Neo4j::MigrationError,
|
|
41
|
+
format(MISSING_CONSTRAINT_OR_INDEX, type: type, label: label, property: property)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fail_duplicate_constraint_or_index!(type, label, property)
|
|
45
|
+
fail Neo4j::MigrationError,
|
|
46
|
+
format(DUPLICATE_CONSTRAINT_OR_INDEX, type: type, label: label, property: property)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Migrations
|
|
3
|
+
class MigrationFile
|
|
4
|
+
attr_reader :file_name, :symbol_name, :class_name, :version
|
|
5
|
+
|
|
6
|
+
def initialize(file_name)
|
|
7
|
+
@file_name = file_name
|
|
8
|
+
extract_data!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create(options = {})
|
|
12
|
+
require @file_name
|
|
13
|
+
class_name.constantize.new(@version, options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def extract_data!
|
|
19
|
+
@version, @symbol_name = File.basename(@file_name, '.rb').split('_', 2)
|
|
20
|
+
@class_name = @symbol_name.camelize
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|