neo4j 7.2.3 → 8.0.0.alpha.1
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 +30 -46
- data/Gemfile +15 -14
- data/README.md +21 -14
- data/bin/neo4j-jars +1 -1
- data/lib/neo4j.rb +12 -1
- data/lib/neo4j/active_base.rb +68 -0
- data/lib/neo4j/active_base/session_registry.rb +12 -0
- data/lib/neo4j/active_node.rb +13 -21
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +6 -6
- data/lib/neo4j/active_node/enum.rb +3 -6
- data/lib/neo4j/active_node/has_n.rb +24 -19
- data/lib/neo4j/active_node/has_n/association.rb +6 -2
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +1 -1
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +1 -1
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +1 -1
- data/lib/neo4j/active_node/id_property.rb +52 -15
- data/lib/neo4j/active_node/labels.rb +32 -10
- data/lib/neo4j/active_node/labels/index.rb +5 -55
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +39 -37
- data/lib/neo4j/active_node/persistence.rb +27 -13
- data/lib/neo4j/active_node/query/query_proxy.rb +11 -9
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +4 -4
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +1 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +13 -9
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +76 -8
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +1 -1
- data/lib/neo4j/active_node/query_methods.rb +3 -3
- data/lib/neo4j/active_node/scope.rb +24 -7
- data/lib/neo4j/active_rel.rb +21 -3
- data/lib/neo4j/active_rel/initialize.rb +2 -2
- data/lib/neo4j/active_rel/persistence.rb +32 -6
- data/lib/neo4j/active_rel/persistence/query_factory.rb +3 -3
- data/lib/neo4j/active_rel/property.rb +9 -9
- data/lib/neo4j/active_rel/query.rb +6 -4
- data/lib/neo4j/active_rel/rel_wrapper.rb +24 -16
- data/lib/neo4j/active_rel/related_node.rb +5 -1
- data/lib/neo4j/active_rel/types.rb +2 -2
- data/lib/neo4j/config.rb +0 -1
- data/lib/neo4j/errors.rb +3 -0
- data/lib/neo4j/migration.rb +90 -71
- data/lib/neo4j/migrations.rb +10 -0
- data/lib/neo4j/migrations/base.rb +44 -0
- data/lib/neo4j/migrations/helpers.rb +101 -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 +53 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +110 -0
- data/lib/neo4j/migrations/schema_migration.rb +9 -0
- data/lib/neo4j/model_schema.rb +100 -0
- data/lib/neo4j/railtie.rb +29 -110
- data/lib/neo4j/schema/operation.rb +24 -13
- data/lib/neo4j/session_manager.rb +137 -0
- data/lib/neo4j/shared.rb +20 -11
- data/lib/neo4j/shared/attributes.rb +10 -16
- data/lib/neo4j/shared/callbacks.rb +3 -3
- data/lib/neo4j/shared/cypher.rb +1 -1
- data/lib/neo4j/shared/declared_properties.rb +1 -1
- data/lib/neo4j/shared/declared_property.rb +1 -1
- data/lib/neo4j/shared/enum.rb +6 -18
- data/lib/neo4j/shared/identity.rb +27 -21
- data/lib/neo4j/shared/persistence.rb +26 -17
- data/lib/neo4j/shared/property.rb +5 -2
- data/lib/neo4j/shared/query_factory.rb +4 -5
- data/lib/neo4j/shared/type_converters.rb +8 -9
- data/lib/neo4j/shared/validations.rb +1 -5
- data/lib/neo4j/tasks/migration.rake +83 -2
- data/lib/neo4j/version.rb +1 -1
- 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 +1 -3
- data/lib/rails/generators/neo4j_generator.rb +1 -0
- data/neo4j.gemspec +3 -3
- metadata +58 -65
- data/bin/rake +0 -17
- data/lib/neo4j/shared/permitted_attributes.rb +0 -28
@@ -27,7 +27,7 @@ module Neo4j
|
|
27
27
|
query_with_target(identifier) do |target|
|
28
28
|
begin
|
29
29
|
self.query.with(target).optional_match("(#{target})-[#{target}_rel]-()").delete("#{target}, #{target}_rel").exec
|
30
|
-
rescue Neo4j::
|
30
|
+
rescue Neo4j::Core::CypherSession::CypherError # <=- Seems hacky
|
31
31
|
self.query.delete(target).exec
|
32
32
|
end
|
33
33
|
clear_source_object_cache
|
@@ -27,14 +27,14 @@ module Neo4j
|
|
27
27
|
self.query_as(:n).return("count(#{q}) AS count").first.count
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
alias size count
|
31
|
+
alias length count
|
32
32
|
|
33
33
|
def empty?
|
34
34
|
!self.all.exists?
|
35
35
|
end
|
36
36
|
|
37
|
-
|
37
|
+
alias blank? empty?
|
38
38
|
|
39
39
|
def find_in_batches(options = {})
|
40
40
|
self.query_as(:n).return(:n).find_in_batches(:n, primary_key, options) do |batch|
|
@@ -64,7 +64,7 @@ module Neo4j::ActiveNode
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def full_scopes
|
67
|
-
|
67
|
+
self.superclass.respond_to?(:scopes) ? self.superclass.scopes.merge(scopes) : scopes
|
68
68
|
end
|
69
69
|
|
70
70
|
def _call_scope_context(eval_context, query_params, proc)
|
@@ -100,13 +100,30 @@ module Neo4j::ActiveNode
|
|
100
100
|
@target = target
|
101
101
|
end
|
102
102
|
|
103
|
+
def identity
|
104
|
+
query_proxy_or_target.identity
|
105
|
+
end
|
106
|
+
|
103
107
|
Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
define_method(method) do |*args|
|
109
|
+
@target.all.scoping do
|
110
|
+
query_proxy_or_target.public_send(method, *args)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def method_missing(name, *params, &block)
|
116
|
+
if query_proxy_or_target.respond_to?(name)
|
117
|
+
query_proxy_or_target.public_send(name, *params, &block)
|
118
|
+
else
|
119
|
+
super
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def query_proxy_or_target
|
126
|
+
@query_proxy_or_target ||= @query_proxy || @target
|
110
127
|
end
|
111
128
|
end
|
112
129
|
|
data/lib/neo4j/active_rel.rb
CHANGED
@@ -18,14 +18,13 @@ module Neo4j
|
|
18
18
|
include Neo4j::ActiveRel::Query
|
19
19
|
include Neo4j::ActiveRel::Types
|
20
20
|
include Neo4j::Shared::Enum
|
21
|
-
include Neo4j::Shared::PermittedAttributes
|
22
21
|
|
23
22
|
class FrozenRelError < Neo4j::Error; end
|
24
23
|
|
25
24
|
def initialize(from_node = nil, to_node = nil, args = nil)
|
26
25
|
load_nodes(node_or_nil(from_node), node_or_nil(to_node))
|
27
26
|
resolved_args = hash_or_nil(from_node, args)
|
28
|
-
symbol_args =
|
27
|
+
symbol_args = resolved_args.is_a?(Hash) ? resolved_args.symbolize_keys : resolved_args
|
29
28
|
super(symbol_args)
|
30
29
|
end
|
31
30
|
|
@@ -61,7 +60,26 @@ module Neo4j
|
|
61
60
|
end
|
62
61
|
|
63
62
|
def hash_or_nil(node_or_hash, hash_or_nil)
|
64
|
-
|
63
|
+
node_or_hash.is_a?(Hash) ? node_or_hash : hash_or_nil
|
64
|
+
end
|
65
|
+
|
66
|
+
module ClassMethods
|
67
|
+
[:create, :create!].each do |meth|
|
68
|
+
define_method(meth) do |from_node_or_args = nil, to_node = nil, args = nil|
|
69
|
+
return super(from_node_or_args) if from_node_or_args.is_a?(Hash)
|
70
|
+
args_hash = args || {}
|
71
|
+
args_with_node!(:from_node, from_node_or_args, args_hash)
|
72
|
+
args_with_node!(:to_node, to_node, args_hash)
|
73
|
+
super(args_hash)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def args_with_node!(key, node, args)
|
80
|
+
args[key] = node if node.is_a?(Neo4j::ActiveNode)
|
81
|
+
args
|
82
|
+
end
|
65
83
|
end
|
66
84
|
end
|
67
85
|
end
|
@@ -19,8 +19,8 @@ module Neo4j::ActiveRel
|
|
19
19
|
def init_on_reload(unwrapped_reloaded)
|
20
20
|
@attributes = nil
|
21
21
|
init_on_load(unwrapped_reloaded,
|
22
|
-
unwrapped_reloaded.
|
23
|
-
unwrapped_reloaded.
|
22
|
+
unwrapped_reloaded.start_node_id,
|
23
|
+
unwrapped_reloaded.end_node_id,
|
24
24
|
unwrapped_reloaded.rel_type)
|
25
25
|
self
|
26
26
|
end
|
@@ -40,8 +40,7 @@ module Neo4j::ActiveRel
|
|
40
40
|
# @param [Symbol, String] name of the attribute to increment
|
41
41
|
# @param [Integer, Float] amount to increment
|
42
42
|
def concurrent_increment!(attribute, by = 1)
|
43
|
-
|
44
|
-
increment_by_query! query_rel, attribute, by
|
43
|
+
increment_by_query! query_as(:n), attribute, by
|
45
44
|
end
|
46
45
|
|
47
46
|
def create_model
|
@@ -52,16 +51,35 @@ module Neo4j::ActiveRel
|
|
52
51
|
true
|
53
52
|
end
|
54
53
|
|
54
|
+
def query_as(var)
|
55
|
+
# This should query based on the nodes, not the rel neo_id, I think
|
56
|
+
# Also, picky point: Should the var be `n`?
|
57
|
+
self.class.query_as(neo_id, var)
|
58
|
+
end
|
59
|
+
|
55
60
|
module ClassMethods
|
56
61
|
# Creates a new relationship between objects
|
57
62
|
# @param [Hash] props the properties the new relationship should have
|
58
|
-
def create(
|
59
|
-
|
63
|
+
def create(props = {})
|
64
|
+
relationship_props = extract_association_attributes!(props) || {}
|
65
|
+
new(props).tap do |obj|
|
66
|
+
relationship_props.each do |prop, value|
|
67
|
+
obj.send("#{prop}=", value)
|
68
|
+
end
|
69
|
+
obj.save
|
70
|
+
end
|
60
71
|
end
|
61
72
|
|
62
73
|
# Same as #create, but raises an error if there is a problem during save.
|
63
74
|
def create!(*args)
|
64
|
-
|
75
|
+
props = args[0] || {}
|
76
|
+
relationship_props = extract_association_attributes!(props) || {}
|
77
|
+
new(props).tap do |obj|
|
78
|
+
relationship_props.each do |prop, value|
|
79
|
+
obj.send("#{prop}=", value)
|
80
|
+
end
|
81
|
+
obj.save!
|
82
|
+
end
|
65
83
|
end
|
66
84
|
|
67
85
|
def create_method
|
@@ -69,7 +87,11 @@ module Neo4j::ActiveRel
|
|
69
87
|
end
|
70
88
|
|
71
89
|
def load_entity(id)
|
72
|
-
|
90
|
+
query_as(id).pluck(:r).first
|
91
|
+
end
|
92
|
+
|
93
|
+
def query_as(neo_id, var = :r)
|
94
|
+
Neo4j::ActiveBase.new_query.match("()-[#{var}]-()").where(var => {neo_id: neo_id})
|
73
95
|
end
|
74
96
|
end
|
75
97
|
|
@@ -79,6 +101,10 @@ module Neo4j::ActiveRel
|
|
79
101
|
|
80
102
|
private
|
81
103
|
|
104
|
+
def destroy_query
|
105
|
+
query_as(:r).delete(:r)
|
106
|
+
end
|
107
|
+
|
82
108
|
def validate_node_classes!
|
83
109
|
[from_node, to_node].each do |node|
|
84
110
|
type = from_node == node ? :_from_class : :_to_class
|
@@ -15,9 +15,9 @@ module Neo4j::ActiveRel::Persistence
|
|
15
15
|
# TODO: This feels like it should also wrap the rel, but that is handled in Neo4j::ActiveRel::Persistence at the moment.
|
16
16
|
# Builds and executes the query using the objects giving during init.
|
17
17
|
# It holds the process:
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# * Execute node callbacks if needed
|
19
|
+
# * Create and execute the query
|
20
|
+
# * Mix the query response into the unpersisted objects given during init
|
21
21
|
def build!
|
22
22
|
node_before_callbacks! do
|
23
23
|
res = query_factory(rel, rel_id, iterative_query).query.unwrapped.return(*unpersisted_return_ids).first
|
@@ -6,27 +6,27 @@ module Neo4j::ActiveRel
|
|
6
6
|
include Neo4j::Shared::Property
|
7
7
|
|
8
8
|
%w(to_node from_node).each do |direction|
|
9
|
-
define_method(
|
9
|
+
define_method(direction.to_s) { instance_variable_get("@#{direction}") }
|
10
10
|
define_method("#{direction}=") do |argument|
|
11
11
|
fail FrozenRelError, 'Relationship start/end nodes cannot be changed once persisted' if _persisted_obj
|
12
12
|
instance_variable_set("@#{direction}", argument)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
alias start_node from_node
|
17
|
+
alias end_node to_node
|
18
18
|
|
19
19
|
%w(start_node end_node).each do |direction|
|
20
20
|
define_method("#{direction}_neo_id") { send(direction).neo_id if direction }
|
21
21
|
end
|
22
|
-
|
23
|
-
|
22
|
+
alias from_node_neo_id start_node_neo_id
|
23
|
+
alias to_node_neo_id end_node_neo_id
|
24
24
|
|
25
25
|
# @return [String] a string representing the relationship type that will be created
|
26
26
|
def type
|
27
27
|
self.class.type
|
28
28
|
end
|
29
|
-
|
29
|
+
alias rel_type type
|
30
30
|
|
31
31
|
def initialize(attributes = nil)
|
32
32
|
super(attributes)
|
@@ -55,7 +55,7 @@ module Neo4j::ActiveRel
|
|
55
55
|
end
|
56
56
|
|
57
57
|
%w(to_class from_class).each do |direction|
|
58
|
-
define_method(
|
58
|
+
define_method(direction.to_s) do |argument = nil|
|
59
59
|
if !argument.nil?
|
60
60
|
Neo4j::ClassArguments.validate_argument!(argument, direction)
|
61
61
|
|
@@ -73,8 +73,8 @@ module Neo4j::ActiveRel
|
|
73
73
|
(class_argument.is_a?(Array) && class_argument.all? { |c| [String, Symbol].include?(c.class) })
|
74
74
|
end
|
75
75
|
|
76
|
-
|
77
|
-
|
76
|
+
alias start_class from_class
|
77
|
+
alias end_class to_class
|
78
78
|
|
79
79
|
def load_entity(id)
|
80
80
|
Neo4j::Node.load(id)
|
@@ -12,8 +12,10 @@ module Neo4j::ActiveRel
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# Loads the relationship using its neo_id.
|
15
|
-
def find_by_id(key, session =
|
16
|
-
session
|
15
|
+
def find_by_id(key, session = nil)
|
16
|
+
options = session ? {session: session} : {}
|
17
|
+
query ||= Neo4j::ActiveBase.new_query(options)
|
18
|
+
query.match('()-[r]-()').where('ID(r)' => key.to_i).limit(1).return(:r).first.r
|
17
19
|
end
|
18
20
|
|
19
21
|
# Performs a very basic match on the relationship.
|
@@ -47,12 +49,12 @@ module Neo4j::ActiveRel
|
|
47
49
|
|
48
50
|
def where_query
|
49
51
|
deprecation_warning!
|
50
|
-
Neo4j::
|
52
|
+
Neo4j::ActiveBase.new_query.match("#{cypher_string(:outbound)}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
|
51
53
|
end
|
52
54
|
|
53
55
|
def all_query
|
54
56
|
deprecation_warning!
|
55
|
-
Neo4j::
|
57
|
+
Neo4j::ActiveBase.new_query.match("#{cypher_string}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
|
56
58
|
end
|
57
59
|
|
58
60
|
def cypher_string(dir = :outbound)
|
@@ -1,22 +1,30 @@
|
|
1
|
-
|
2
|
-
module Wrapper
|
3
|
-
def wrapper
|
4
|
-
props.symbolize_keys!
|
5
|
-
begin
|
6
|
-
most_concrete_class = class_from_type
|
7
|
-
wrapped_rel = most_concrete_class.constantize.new
|
8
|
-
rescue NameError
|
9
|
-
return self
|
10
|
-
end
|
1
|
+
require 'neo4j/core/relationship'
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
wrapping_proc = proc do |relationship|
|
4
|
+
Neo4j::RelWrapping.wrapper(relationship)
|
5
|
+
end
|
6
|
+
Neo4j::Core::Relationship.wrapper_callback(wrapping_proc)
|
15
7
|
|
16
|
-
|
8
|
+
module Neo4j
|
9
|
+
module RelWrapping
|
10
|
+
class << self
|
11
|
+
def wrapper(rel)
|
12
|
+
rel.props.symbolize_keys!
|
13
|
+
begin
|
14
|
+
most_concrete_class = class_from_type(rel.rel_type)
|
15
|
+
most_concrete_class.constantize.new
|
16
|
+
rescue NameError => e
|
17
|
+
raise e unless e.message =~ /(uninitialized|wrong) constant/
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
return rel
|
20
|
+
end.tap do |wrapped_rel|
|
21
|
+
wrapped_rel.init_on_load(rel, rel.start_node_id, rel.end_node_id, rel.type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def class_from_type(rel_type)
|
26
|
+
Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] || Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] = rel_type.to_s.camelize
|
27
|
+
end
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
@@ -29,7 +29,11 @@ module Neo4j::ActiveRel
|
|
29
29
|
# Loads a node from the database or returns the node if already laoded
|
30
30
|
def loaded
|
31
31
|
fail UnsetRelatedNodeError, 'Node not set, cannot load' if @node.nil?
|
32
|
-
@node = @node.respond_to?(:neo_id)
|
32
|
+
@node = if @node.respond_to?(:neo_id)
|
33
|
+
@node
|
34
|
+
else
|
35
|
+
Neo4j::ActiveBase.new_query.match(:n).where(n: {neo_id: @node}).pluck(:n).first
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
# @param [String, Symbol, Array] clazz An alternate label to use in the event the node is not present or loaded
|
@@ -42,8 +42,8 @@ module Neo4j
|
|
42
42
|
assign_type!(namespaced_model_name, true)
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
46
|
-
|
45
|
+
alias rel_type type
|
46
|
+
alias _type type # should be deprecated
|
47
47
|
|
48
48
|
def namespaced_model_name
|
49
49
|
case Neo4j::Config[:module_handling]
|
data/lib/neo4j/config.rb
CHANGED
@@ -4,7 +4,6 @@ module Neo4j
|
|
4
4
|
# == Configurations keys
|
5
5
|
class Config
|
6
6
|
DEFAULT_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'config', 'neo4j', 'config.yml'))
|
7
|
-
CLASS_NAME_PROPERTY_KEY = 'class_name_property'
|
8
7
|
|
9
8
|
class << self
|
10
9
|
# In keeping with the Rails convention, this class writer lets you globally configure
|
data/lib/neo4j/errors.rb
CHANGED
@@ -25,4 +25,7 @@ module Neo4j
|
|
25
25
|
|
26
26
|
class DangerousAttributeError < ScriptError; end
|
27
27
|
class UnknownAttributeError < NoMethodError; end
|
28
|
+
class MigrationError < Error; end
|
29
|
+
class IrreversibleMigration < MigrationError; end
|
30
|
+
class UnknownMigrationVersionError < MigrationError; end
|
28
31
|
end
|
data/lib/neo4j/migration.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'benchmark'
|
2
|
+
require 'neo4j/migrations/helpers/id_property'
|
2
3
|
|
3
4
|
module Neo4j
|
4
5
|
class Migration
|
@@ -7,11 +8,11 @@ module Neo4j
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def output(string = '')
|
10
|
-
puts string unless !!ENV['
|
11
|
+
puts string unless !!ENV['MIGRATIONS_SILENCED']
|
11
12
|
end
|
12
13
|
|
13
14
|
def print_output(string)
|
14
|
-
print string unless !!ENV['
|
15
|
+
print string unless !!ENV['MIGRATIONS_SILENCED']
|
15
16
|
end
|
16
17
|
|
17
18
|
def default_path
|
@@ -22,7 +23,15 @@ module Neo4j
|
|
22
23
|
File.join(path.to_s, 'db', 'neo4j-migrate')
|
23
24
|
end
|
24
25
|
|
26
|
+
def setup
|
27
|
+
FileUtils.mkdir_p('db/neo4j-migrate')
|
28
|
+
end
|
29
|
+
|
30
|
+
delegate :query, to: Neo4j::Session
|
31
|
+
|
25
32
|
class AddIdProperty < Neo4j::Migration
|
33
|
+
include Neo4j::Migrations::Helpers::IdProperty
|
34
|
+
|
26
35
|
attr_reader :models_filename
|
27
36
|
|
28
37
|
def initialize(path = default_path)
|
@@ -30,6 +39,8 @@ module Neo4j
|
|
30
39
|
end
|
31
40
|
|
32
41
|
def migrate
|
42
|
+
ActiveSupport::Deprecation.warn '`AddIdProperty` task is deprecated and may be removed from future releases. '\
|
43
|
+
'Create a new migration and use the `populate_id_property` helper.', caller
|
33
44
|
models = ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(models_filename))[:models]
|
34
45
|
output 'This task will add an ID Property every node in the given file.'
|
35
46
|
output 'It may take a significant amount of time, please be patient.'
|
@@ -37,13 +48,20 @@ module Neo4j
|
|
37
48
|
output
|
38
49
|
output
|
39
50
|
output "Adding IDs to #{model}"
|
40
|
-
|
51
|
+
populate_id_property model
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
44
|
-
def
|
45
|
-
|
55
|
+
def query(*args)
|
56
|
+
ActiveBase.magic_query(*args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute(*args)
|
60
|
+
ActiveBase.query(*args)
|
61
|
+
end
|
46
62
|
|
63
|
+
def setup
|
64
|
+
super
|
47
65
|
return if File.file?(models_filename)
|
48
66
|
|
49
67
|
File.open(models_filename, 'w') do |file|
|
@@ -56,72 +74,73 @@ MESSAGE
|
|
56
74
|
end
|
57
75
|
end
|
58
76
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
77
|
+
# Might need some of this...
|
78
|
+
# private
|
79
|
+
|
80
|
+
# def add_ids_to(model)
|
81
|
+
# max_per_batch = (ENV['MAX_PER_BATCH'] || default_max_per_batch).to_i
|
82
|
+
|
83
|
+
# label = model.mapped_label_name
|
84
|
+
# last_time_taken = nil
|
85
|
+
|
86
|
+
# until (nodes_left = idless_count(label, model.primary_key)) == 0
|
87
|
+
# print_status(last_time_taken, max_per_batch, nodes_left)
|
88
|
+
|
89
|
+
# count = [nodes_left, max_per_batch].min
|
90
|
+
# last_time_taken = Benchmark.realtime do
|
91
|
+
# max_per_batch = id_batch_set(label, model.primary_key, Array.new(count) { new_id_for(model) }, count)
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
|
96
|
+
# def idless_count(label, id_property)
|
97
|
+
# Neo4j::ActiveBase.new_query.match(n: label).where("NOT EXISTS(n.#{id_property})").pluck('COUNT(n) AS ids').first
|
98
|
+
# end
|
99
|
+
|
100
|
+
# def print_status(last_time_taken, max_per_batch, nodes_left)
|
101
|
+
# time_per_node = last_time_taken / max_per_batch if last_time_taken
|
102
|
+
# message = if time_per_node
|
103
|
+
# eta_seconds = (nodes_left * time_per_node).round
|
104
|
+
# "#{nodes_left} nodes left. Last batch: #{(time_per_node * 1000.0).round(1)}ms / node (ETA: #{eta_seconds / 60} minutes)\r"
|
105
|
+
# else
|
106
|
+
# "Running first batch...\r"
|
107
|
+
# end
|
108
|
+
|
109
|
+
# print_output message
|
110
|
+
# end
|
111
|
+
|
112
|
+
|
113
|
+
# def id_batch_set(label, id_property, new_ids, count)
|
114
|
+
# tx = Neo4j::ActiveBase.new_transaction
|
115
|
+
|
116
|
+
# Neo4j::ActiveBase.current_session.query("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
117
|
+
# with COLLECT(n) as nodes, #{new_ids} as ids
|
118
|
+
# FOREACH(i in range(0,#{count - 1})|
|
119
|
+
# FOREACH(node in [nodes[i]]|
|
120
|
+
# SET node.#{id_property} = ids[i]))
|
121
|
+
# RETURN distinct(true)
|
122
|
+
# LIMIT #{count}")
|
123
|
+
|
124
|
+
# count
|
125
|
+
# rescue Neo4j::Server::CypherResponse::ResponseError, Faraday::TimeoutError
|
126
|
+
# new_max_per_batch = (max_per_batch * 0.8).round
|
127
|
+
# output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
128
|
+
# new_max_per_batch
|
129
|
+
# ensure
|
130
|
+
# tx.close
|
131
|
+
# end
|
132
|
+
|
133
|
+
# def default_max_per_batch
|
134
|
+
# 900
|
135
|
+
# end
|
136
|
+
|
137
|
+
# def new_id_for(model)
|
138
|
+
# if model.id_property_info[:type][:auto]
|
139
|
+
# SecureRandom.uuid
|
140
|
+
# else
|
141
|
+
# model.new.send(model.id_property_info[:type][:on])
|
142
|
+
# end
|
143
|
+
# end
|
125
144
|
end
|
126
145
|
end
|
127
146
|
end
|