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,24 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Core
|
|
3
|
+
class Query
|
|
4
|
+
# Creates a Neo4j::ActiveNode::Query::QueryProxy object that builds off of a Core::Query object.
|
|
5
|
+
#
|
|
6
|
+
# @param [Class] model An ActiveNode model to be used as the start of a new QueryuProxy chain
|
|
7
|
+
# @param [Symbol] var The variable to be used to refer to the object from within the new QueryProxy
|
|
8
|
+
# @param [Boolean] optional Indicate whether the new QueryProxy will use MATCH or OPTIONAL MATCH.
|
|
9
|
+
# @return [Neo4j::ActiveNode::Query::QueryProxy] A QueryProxy object.
|
|
10
|
+
def proxy_as(model, var, optional = false)
|
|
11
|
+
# TODO: Discuss whether it's necessary to call `break` on the query or if this should be left to the user.
|
|
12
|
+
Neo4j::ActiveNode::Query::QueryProxy.new(model, nil, node: var, optional: optional, starting_query: self, chain_level: @proxy_chain_level)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Calls proxy_as with `optional` set true. This doesn't offer anything different from calling `proxy_as` directly but it may be more readable.
|
|
16
|
+
def proxy_as_optional(model, var)
|
|
17
|
+
proxy_as(model, var, true)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# For instances where you turn a QueryProxy into a Query and then back to a QueryProxy with `#proxy_as`
|
|
21
|
+
attr_accessor :proxy_chain_level
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Core
|
|
3
|
+
module QueryFindInBatches
|
|
4
|
+
def find_in_batches(node_var, prop_var, options = {})
|
|
5
|
+
validate_find_in_batches_options!(options)
|
|
6
|
+
|
|
7
|
+
batch_size = options.delete(:batch_size) || 1000
|
|
8
|
+
|
|
9
|
+
query = reorder(node_var => prop_var).limit(batch_size)
|
|
10
|
+
|
|
11
|
+
records = query.to_a
|
|
12
|
+
|
|
13
|
+
while records.any?
|
|
14
|
+
records_size = records.size
|
|
15
|
+
primary_key_offset = primary_key_offset(records.last, node_var, prop_var)
|
|
16
|
+
|
|
17
|
+
yield records
|
|
18
|
+
|
|
19
|
+
break if records_size < batch_size
|
|
20
|
+
|
|
21
|
+
primary_key_var = Neo4j::Core::QueryClauses::Clause.from_key_and_single_value(node_var, prop_var)
|
|
22
|
+
records = query.where("#{primary_key_var} > {primary_key_offset}")
|
|
23
|
+
.params(primary_key_offset: primary_key_offset).to_a
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_each(*args, &block)
|
|
28
|
+
find_in_batches(*args) { |batch| batch.each(&block) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def validate_find_in_batches_options!(options)
|
|
34
|
+
invalid_keys = options.keys.map(&:to_sym) - [:batch_size]
|
|
35
|
+
fail ArgumentError, "Invalid keys: #{invalid_keys.join(', ')}" if not invalid_keys.empty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def primary_key_offset(last_record, node_var, prop_var)
|
|
39
|
+
last_record.send(node_var).send(prop_var)
|
|
40
|
+
rescue NoMethodError
|
|
41
|
+
begin
|
|
42
|
+
last_record.send(node_var).properties[prop_var.to_sym]
|
|
43
|
+
rescue NoMethodError
|
|
44
|
+
last_record.send("#{node_var}.#{prop_var}") # In case we're explicitly returning it
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'neo4j/core/wrappable'
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Core
|
|
5
|
+
module Relationship
|
|
6
|
+
def props; properties; end
|
|
7
|
+
def neo_id; id; end
|
|
8
|
+
def start_node_neo_id; start_node_id; end
|
|
9
|
+
def end_node_neo_id; end_node_id; end
|
|
10
|
+
def rel_type; type; end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'neo4j/core/result'
|
|
2
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
|
3
|
+
|
|
4
|
+
module Neo4j
|
|
5
|
+
module Core
|
|
6
|
+
module Responses
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
mattr_accessor :wrap_level
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class_methods do
|
|
14
|
+
def result_from_data(entities_data)
|
|
15
|
+
rows = entities_data.map do |entity_data|
|
|
16
|
+
wrap(entity_data.values)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Neo4j::Core::Result.new(entities_data.keys, rows)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def wrap(value)
|
|
25
|
+
case value
|
|
26
|
+
when Neo4j::Driver::Types::Entity
|
|
27
|
+
wrap_by_level(value)
|
|
28
|
+
when Neo4j::Driver::Types::Path
|
|
29
|
+
value
|
|
30
|
+
when Hash
|
|
31
|
+
value.map { |key, val| [key, wrap(val)] }.to_h
|
|
32
|
+
when Enumerable
|
|
33
|
+
value.map(&method(:wrap))
|
|
34
|
+
else
|
|
35
|
+
value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def wrap_by_level(entity)
|
|
40
|
+
case wrap_level
|
|
41
|
+
when :core_entity
|
|
42
|
+
entity
|
|
43
|
+
else
|
|
44
|
+
entity.wrap
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Core
|
|
3
|
+
class Result
|
|
4
|
+
attr_reader :columns, :rows
|
|
5
|
+
|
|
6
|
+
def initialize(columns, rows)
|
|
7
|
+
@columns = columns.map(&:to_sym)
|
|
8
|
+
@rows = rows
|
|
9
|
+
@struct_class = Struct.new(:index, *@columns)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
include Enumerable
|
|
13
|
+
|
|
14
|
+
def each
|
|
15
|
+
structs.each do |struct|
|
|
16
|
+
yield struct
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def structs
|
|
21
|
+
@structs ||= rows.each_with_index.map do |row, index|
|
|
22
|
+
@struct_class.new(index, *row)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def hashes
|
|
27
|
+
@hashes ||= rows.map do |row|
|
|
28
|
+
Hash[@columns.zip(row)]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Core
|
|
3
|
+
module Schema
|
|
4
|
+
def version
|
|
5
|
+
result = query('CALL dbms.components()', {}, skip_instrumentation: true)
|
|
6
|
+
|
|
7
|
+
# BTW: community / enterprise could be retrieved via `result.first.edition`
|
|
8
|
+
result.first.versions[0]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def indexes
|
|
12
|
+
result = query('CALL db.indexes()', {}, skip_instrumentation: true)
|
|
13
|
+
|
|
14
|
+
result.map do |row|
|
|
15
|
+
label, property = row.description.match(/INDEX ON :([^\(]+)\(([^\)]+)\)/)[1, 2]
|
|
16
|
+
{ type: row.type.to_sym, label: label.to_sym, properties: [property.to_sym], state: row.state.to_sym }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def constraints
|
|
21
|
+
result = query('CALL db.indexes()', {}, skip_instrumentation: true)
|
|
22
|
+
|
|
23
|
+
result.select { |row| row.type == 'node_unique_property' }.map do |row|
|
|
24
|
+
label, property = row.description.match(/INDEX ON :([^\(]+)\(([^\)]+)\)/)[1, 2]
|
|
25
|
+
{ type: :uniqueness, label: label.to_sym, properties: [property.to_sym] }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Core
|
|
3
|
+
module Wrappable
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def wrap
|
|
7
|
+
self.class.wrap(self)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class_methods do
|
|
11
|
+
def wrapper_callback(proc)
|
|
12
|
+
fail 'Callback already specified!' if @wrapper_callback
|
|
13
|
+
@wrapper_callback = proc
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def clear_wrapper_callback
|
|
17
|
+
@wrapper_callback = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def wrap(node)
|
|
21
|
+
if @wrapper_callback
|
|
22
|
+
@wrapper_callback.call(node)
|
|
23
|
+
else
|
|
24
|
+
node
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/neo4j/errors.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
# Neo4j.rb Errors
|
|
3
|
+
# Generic Neo4j.rb exception class.
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Raised when Neo4j.rb cannot find record by given id.
|
|
8
|
+
class RecordNotFound < Error
|
|
9
|
+
attr_reader :model, :primary_key, :id
|
|
10
|
+
|
|
11
|
+
def initialize(message = nil, model = nil, primary_key = nil, id = nil)
|
|
12
|
+
@primary_key = primary_key
|
|
13
|
+
@model = model
|
|
14
|
+
@id = id
|
|
15
|
+
|
|
16
|
+
super(message)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class DeprecatedSchemaDefinitionError < Error; end
|
|
21
|
+
|
|
22
|
+
class InvalidPropertyOptionsError < Error; end
|
|
23
|
+
|
|
24
|
+
class InvalidParameterError < Error; end
|
|
25
|
+
|
|
26
|
+
class UnknownTypeConverterError < Error; end
|
|
27
|
+
|
|
28
|
+
class DangerousAttributeError < ScriptError; end
|
|
29
|
+
class UnknownAttributeError < NoMethodError; end
|
|
30
|
+
|
|
31
|
+
class MigrationError < Error; end
|
|
32
|
+
class IrreversibleMigration < MigrationError; end
|
|
33
|
+
class UnknownMigrationVersionError < MigrationError; end
|
|
34
|
+
|
|
35
|
+
# Inspired/taken from active_record/migration.rb
|
|
36
|
+
class PendingMigrationError < MigrationError
|
|
37
|
+
def initialize(migrations)
|
|
38
|
+
pending_migrations = migrations.join("\n")
|
|
39
|
+
if rails? && defined?(Rails.env)
|
|
40
|
+
super("Migrations are pending:\n#{pending_migrations}\n To resolve this issue, run:\n\n #{command_name} neo4j:migrate RAILS_ENV=#{::Rails.env}")
|
|
41
|
+
else
|
|
42
|
+
super("Migrations are pending:\n#{pending_migrations}\n To resolve this issue, run:\n\n #{command_name} neo4j:migrate")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def command_name
|
|
49
|
+
return 'rake' unless rails?
|
|
50
|
+
Rails.version.to_f >= 5 ? 'bin/rails' : 'bin/rake'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def rails?
|
|
54
|
+
defined?(Rails)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'neo4j/migrations/helpers/id_property'
|
|
3
|
+
|
|
4
|
+
module Neo4j
|
|
5
|
+
class Migration
|
|
6
|
+
def migrate
|
|
7
|
+
fail 'not implemented'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def output(string = '')
|
|
11
|
+
puts string unless !!ENV['MIGRATIONS_SILENCED']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def print_output(string)
|
|
15
|
+
print string unless !!ENV['MIGRATIONS_SILENCED']
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def default_path
|
|
19
|
+
Rails.root if defined? Rails
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def joined_path(path)
|
|
23
|
+
File.join(path.to_s, 'db', 'neo4j-migrate')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def setup
|
|
27
|
+
FileUtils.mkdir_p('db/neo4j-migrate')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def query(*args)
|
|
31
|
+
Neo4j::ActiveBase.current_driver.query(*args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class AddIdProperty < Neo4j::Migration
|
|
35
|
+
include Neo4j::Migrations::Helpers::IdProperty
|
|
36
|
+
|
|
37
|
+
attr_reader :models_filename
|
|
38
|
+
|
|
39
|
+
def initialize(path = default_path)
|
|
40
|
+
@models_filename = File.join(joined_path(path), 'add_id_property.yml')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def migrate
|
|
44
|
+
ActiveSupport::Deprecation.warn '`AddIdProperty` task is deprecated and may be removed from future releases. '\
|
|
45
|
+
'Create a new migration and use the `populate_id_property` helper.', caller
|
|
46
|
+
models = ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(models_filename))[:models]
|
|
47
|
+
output 'This task will add an ID Property every node in the given file.'
|
|
48
|
+
output 'It may take a significant amount of time, please be patient.'
|
|
49
|
+
models.each do |model|
|
|
50
|
+
output
|
|
51
|
+
output
|
|
52
|
+
output "Adding IDs to #{model}"
|
|
53
|
+
populate_id_property model
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def query(*args)
|
|
58
|
+
ActiveBase.magic_query(*args)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def execute(*args)
|
|
62
|
+
ActiveBase.query(*args)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def setup
|
|
66
|
+
super
|
|
67
|
+
return if File.file?(models_filename)
|
|
68
|
+
|
|
69
|
+
File.open(models_filename, 'w') do |file|
|
|
70
|
+
message = <<MESSAGE
|
|
71
|
+
# Provide models to which IDs should be added.
|
|
72
|
+
# # It will only modify nodes that do not have IDs. There is no danger of overwriting data.
|
|
73
|
+
# # models: [Student,Lesson,Teacher,Exam]\nmodels: []
|
|
74
|
+
MESSAGE
|
|
75
|
+
file.write(message)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Might need some of this...
|
|
80
|
+
# private
|
|
81
|
+
|
|
82
|
+
# def add_ids_to(model)
|
|
83
|
+
# max_per_batch = (ENV['MAX_PER_BATCH'] || default_max_per_batch).to_i
|
|
84
|
+
|
|
85
|
+
# label = model.mapped_label_name
|
|
86
|
+
# last_time_taken = nil
|
|
87
|
+
|
|
88
|
+
# until (nodes_left = idless_count(label, model.primary_key)) == 0
|
|
89
|
+
# print_status(last_time_taken, max_per_batch, nodes_left)
|
|
90
|
+
|
|
91
|
+
# count = [nodes_left, max_per_batch].min
|
|
92
|
+
# last_time_taken = Benchmark.realtime do
|
|
93
|
+
# max_per_batch = id_batch_set(label, model.primary_key, Array.new(count) { new_id_for(model) }, count)
|
|
94
|
+
# end
|
|
95
|
+
# end
|
|
96
|
+
# end
|
|
97
|
+
|
|
98
|
+
# def idless_count(label, id_property)
|
|
99
|
+
# Neo4j::ActiveBase.new_query.match(n: label).where("NOT EXISTS(n.#{id_property})").pluck('COUNT(n) AS ids').first
|
|
100
|
+
# end
|
|
101
|
+
|
|
102
|
+
# def print_status(last_time_taken, max_per_batch, nodes_left)
|
|
103
|
+
# time_per_node = last_time_taken / max_per_batch if last_time_taken
|
|
104
|
+
# message = if time_per_node
|
|
105
|
+
# eta_seconds = (nodes_left * time_per_node).round
|
|
106
|
+
# "#{nodes_left} nodes left. Last batch: #{(time_per_node * 1000.0).round(1)}ms / node (ETA: #{eta_seconds / 60} minutes)\r"
|
|
107
|
+
# else
|
|
108
|
+
# "Running first batch...\r"
|
|
109
|
+
# end
|
|
110
|
+
|
|
111
|
+
# print_output message
|
|
112
|
+
# end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# def id_batch_set(label, id_property, new_ids, count)
|
|
116
|
+
# tx = Neo4j::ActiveBase.new_transaction
|
|
117
|
+
|
|
118
|
+
# Neo4j::ActiveBase.current_driver.query("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
|
119
|
+
# with COLLECT(n) as nodes, #{new_ids} as ids
|
|
120
|
+
# FOREACH(i in range(0,#{count - 1})|
|
|
121
|
+
# FOREACH(node in [nodes[i]]|
|
|
122
|
+
# SET node.#{id_property} = ids[i]))
|
|
123
|
+
# RETURN distinct(true)
|
|
124
|
+
# LIMIT #{count}")
|
|
125
|
+
|
|
126
|
+
# count
|
|
127
|
+
# rescue Neo4j::Server::CypherResponse::ResponseError, Faraday::TimeoutError
|
|
128
|
+
# new_max_per_batch = (max_per_batch * 0.8).round
|
|
129
|
+
# output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
|
130
|
+
# new_max_per_batch
|
|
131
|
+
# ensure
|
|
132
|
+
# tx.close
|
|
133
|
+
# end
|
|
134
|
+
|
|
135
|
+
# def default_max_per_batch
|
|
136
|
+
# 900
|
|
137
|
+
# end
|
|
138
|
+
|
|
139
|
+
# def new_id_for(model)
|
|
140
|
+
# if model.id_property_info[:type][:auto]
|
|
141
|
+
# SecureRandom.uuid
|
|
142
|
+
# else
|
|
143
|
+
# model.new.send(model.id_property_info[:type][:on])
|
|
144
|
+
# end
|
|
145
|
+
# end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|