activegraph 11.0.0.beta.1-java
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 +2016 -0
- data/CONTRIBUTORS +12 -0
- data/Gemfile +24 -0
- data/README.md +111 -0
- data/activegraph.gemspec +52 -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 +35 -0
- data/lib/active_graph.rb +123 -0
- data/lib/active_graph/ansi.rb +14 -0
- data/lib/active_graph/attribute_set.rb +32 -0
- data/lib/active_graph/base.rb +77 -0
- data/lib/active_graph/class_arguments.rb +39 -0
- data/lib/active_graph/config.rb +135 -0
- data/lib/active_graph/core.rb +14 -0
- data/lib/active_graph/core/connection_failed_error.rb +6 -0
- data/lib/active_graph/core/cypher_error.rb +37 -0
- data/lib/active_graph/core/entity.rb +11 -0
- data/lib/active_graph/core/instrumentable.rb +37 -0
- data/lib/active_graph/core/label.rb +135 -0
- data/lib/active_graph/core/logging.rb +44 -0
- data/lib/active_graph/core/node.rb +15 -0
- data/lib/active_graph/core/querable.rb +41 -0
- data/lib/active_graph/core/query.rb +485 -0
- data/lib/active_graph/core/query_builder.rb +18 -0
- data/lib/active_graph/core/query_clauses.rb +727 -0
- data/lib/active_graph/core/query_ext.rb +24 -0
- data/lib/active_graph/core/query_find_in_batches.rb +46 -0
- data/lib/active_graph/core/record.rb +51 -0
- data/lib/active_graph/core/result.rb +31 -0
- data/lib/active_graph/core/schema.rb +65 -0
- data/lib/active_graph/core/schema_errors.rb +12 -0
- data/lib/active_graph/core/wrappable.rb +30 -0
- data/lib/active_graph/errors.rb +59 -0
- data/lib/active_graph/lazy_attribute_hash.rb +38 -0
- data/lib/active_graph/migration.rb +148 -0
- data/lib/active_graph/migrations.rb +27 -0
- data/lib/active_graph/migrations/base.rb +77 -0
- data/lib/active_graph/migrations/check_pending.rb +20 -0
- data/lib/active_graph/migrations/helpers.rb +105 -0
- data/lib/active_graph/migrations/helpers/id_property.rb +72 -0
- data/lib/active_graph/migrations/helpers/relationships.rb +66 -0
- data/lib/active_graph/migrations/helpers/schema.rb +63 -0
- data/lib/active_graph/migrations/migration_file.rb +24 -0
- data/lib/active_graph/migrations/runner.rb +195 -0
- data/lib/active_graph/migrations/schema.rb +64 -0
- data/lib/active_graph/migrations/schema_migration.rb +14 -0
- data/lib/active_graph/model_schema.rb +139 -0
- data/lib/active_graph/node.rb +110 -0
- data/lib/active_graph/node/callbacks.rb +8 -0
- data/lib/active_graph/node/dependent.rb +11 -0
- data/lib/active_graph/node/dependent/association_methods.rb +49 -0
- data/lib/active_graph/node/dependent/query_proxy_methods.rb +52 -0
- data/lib/active_graph/node/dependent_callbacks.rb +31 -0
- data/lib/active_graph/node/enum.rb +26 -0
- data/lib/active_graph/node/has_n.rb +602 -0
- data/lib/active_graph/node/has_n/association.rb +278 -0
- data/lib/active_graph/node/has_n/association/rel_factory.rb +61 -0
- data/lib/active_graph/node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/active_graph/node/has_n/association_cypher_methods.rb +108 -0
- data/lib/active_graph/node/id_property.rb +224 -0
- data/lib/active_graph/node/id_property/accessor.rb +62 -0
- data/lib/active_graph/node/initialize.rb +21 -0
- data/lib/active_graph/node/labels.rb +207 -0
- data/lib/active_graph/node/labels/index.rb +37 -0
- data/lib/active_graph/node/labels/reloading.rb +21 -0
- data/lib/active_graph/node/node_list_formatter.rb +13 -0
- data/lib/active_graph/node/node_wrapper.rb +54 -0
- data/lib/active_graph/node/orm_adapter.rb +82 -0
- data/lib/active_graph/node/persistence.rb +186 -0
- data/lib/active_graph/node/property.rb +60 -0
- data/lib/active_graph/node/query.rb +76 -0
- data/lib/active_graph/node/query/query_proxy.rb +367 -0
- data/lib/active_graph/node/query/query_proxy_eager_loading.rb +177 -0
- data/lib/active_graph/node/query/query_proxy_eager_loading/association_tree.rb +75 -0
- data/lib/active_graph/node/query/query_proxy_enumerable.rb +110 -0
- data/lib/active_graph/node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/active_graph/node/query/query_proxy_link.rb +139 -0
- data/lib/active_graph/node/query/query_proxy_methods.rb +303 -0
- data/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +99 -0
- data/lib/active_graph/node/query_methods.rb +68 -0
- data/lib/active_graph/node/reflection.rb +86 -0
- data/lib/active_graph/node/rels.rb +11 -0
- data/lib/active_graph/node/scope.rb +166 -0
- data/lib/active_graph/node/unpersisted.rb +48 -0
- data/lib/active_graph/node/validations.rb +59 -0
- data/lib/active_graph/paginated.rb +27 -0
- data/lib/active_graph/railtie.rb +108 -0
- data/lib/active_graph/relationship.rb +68 -0
- data/lib/active_graph/relationship/callbacks.rb +21 -0
- data/lib/active_graph/relationship/initialize.rb +28 -0
- data/lib/active_graph/relationship/persistence.rb +133 -0
- data/lib/active_graph/relationship/persistence/query_factory.rb +95 -0
- data/lib/active_graph/relationship/property.rb +92 -0
- data/lib/active_graph/relationship/query.rb +99 -0
- data/lib/active_graph/relationship/rel_wrapper.rb +31 -0
- data/lib/active_graph/relationship/related_node.rb +87 -0
- data/lib/active_graph/relationship/types.rb +80 -0
- data/lib/active_graph/relationship/validations.rb +8 -0
- data/lib/active_graph/schema/operation.rb +102 -0
- data/lib/active_graph/shared.rb +48 -0
- data/lib/active_graph/shared/attributes.rb +217 -0
- data/lib/active_graph/shared/callbacks.rb +66 -0
- data/lib/active_graph/shared/cypher.rb +37 -0
- data/lib/active_graph/shared/declared_properties.rb +204 -0
- data/lib/active_graph/shared/declared_property.rb +109 -0
- data/lib/active_graph/shared/declared_property/index.rb +37 -0
- data/lib/active_graph/shared/enum.rb +167 -0
- data/lib/active_graph/shared/filtered_hash.rb +79 -0
- data/lib/active_graph/shared/identity.rb +34 -0
- data/lib/active_graph/shared/initialize.rb +65 -0
- data/lib/active_graph/shared/marshal.rb +23 -0
- data/lib/active_graph/shared/mass_assignment.rb +63 -0
- data/lib/active_graph/shared/permitted_attributes.rb +28 -0
- data/lib/active_graph/shared/persistence.rb +272 -0
- data/lib/active_graph/shared/property.rb +249 -0
- data/lib/active_graph/shared/query_factory.rb +122 -0
- data/lib/active_graph/shared/rel_type_converters.rb +43 -0
- data/lib/active_graph/shared/serialized_properties.rb +30 -0
- data/lib/active_graph/shared/type_converters.rb +439 -0
- data/lib/active_graph/shared/typecasted_attributes.rb +99 -0
- data/lib/active_graph/shared/typecaster.rb +53 -0
- data/lib/active_graph/shared/validations.rb +44 -0
- data/lib/active_graph/tasks/migration.rake +204 -0
- data/lib/active_graph/timestamps.rb +11 -0
- data/lib/active_graph/timestamps/created.rb +9 -0
- data/lib/active_graph/timestamps/updated.rb +9 -0
- data/lib/active_graph/transaction.rb +22 -0
- data/lib/active_graph/transactions.rb +57 -0
- data/lib/active_graph/type_converters.rb +7 -0
- data/lib/active_graph/undeclared_properties.rb +53 -0
- data/lib/active_graph/version.rb +3 -0
- data/lib/active_graph/wrapper.rb +4 -0
- data/lib/rails/generators/active_graph/migration/migration_generator.rb +16 -0
- data/lib/rails/generators/active_graph/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/active_graph/model/model_generator.rb +89 -0
- data/lib/rails/generators/active_graph/model/templates/migration.erb +11 -0
- data/lib/rails/generators/active_graph/model/templates/model.erb +15 -0
- data/lib/rails/generators/active_graph/upgrade_v8/templates/migration.erb +17 -0
- data/lib/rails/generators/active_graph/upgrade_v8/upgrade_v8_generator.rb +34 -0
- data/lib/rails/generators/active_graph_generator.rb +121 -0
- metadata +423 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Core
|
|
3
|
+
class Query
|
|
4
|
+
# Creates a ActiveGraph::Node::Query::QueryProxy object that builds off of a Core::Query object.
|
|
5
|
+
#
|
|
6
|
+
# @param [Class] model An Node 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 [ActiveGraph::Node::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
|
+
ActiveGraph::Node::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,46 @@
|
|
|
1
|
+
module ActiveGraph
|
|
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 = ActiveGraph::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
|
+
node = last_record[node_var]
|
|
40
|
+
return node.send(prop_var) if node&.respond_to?(prop_var)
|
|
41
|
+
return node.properties[prop_var.to_sym] if node&.respond_to?(:properties)
|
|
42
|
+
last_record["#{node_var}.#{prop_var}"] # In case we're explicitly returning it
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_graph/core/result'
|
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
|
5
|
+
|
|
6
|
+
module ActiveGraph
|
|
7
|
+
module Core
|
|
8
|
+
module Record
|
|
9
|
+
attr_writer :wrap
|
|
10
|
+
|
|
11
|
+
def values
|
|
12
|
+
wrap(super)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first
|
|
16
|
+
wrap(super)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [](key)
|
|
20
|
+
wrap(super)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_h
|
|
24
|
+
wrap(super)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def wrap(value)
|
|
30
|
+
return value unless wrap?
|
|
31
|
+
|
|
32
|
+
case value
|
|
33
|
+
when Neo4j::Driver::Types::Entity
|
|
34
|
+
value.wrap
|
|
35
|
+
when Neo4j::Driver::Types::Path
|
|
36
|
+
value
|
|
37
|
+
when Hash
|
|
38
|
+
value.transform_values(&method(:wrap))
|
|
39
|
+
when Enumerable
|
|
40
|
+
value.map!(&method(:wrap))
|
|
41
|
+
else
|
|
42
|
+
value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def wrap?
|
|
47
|
+
@wrap
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Core
|
|
3
|
+
module Result
|
|
4
|
+
attr_writer :wrap
|
|
5
|
+
|
|
6
|
+
def keys
|
|
7
|
+
@keys ||= super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def wrap?
|
|
11
|
+
@wrap
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def each(&block)
|
|
15
|
+
store if wrap? # TODO: why? This is preventing streaming
|
|
16
|
+
@records&.each(&block) || super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def store
|
|
20
|
+
return if @records
|
|
21
|
+
keys
|
|
22
|
+
@records = []
|
|
23
|
+
# TODO: implement 'each' without block parameter
|
|
24
|
+
method(:each).super_method.call do |record|
|
|
25
|
+
record.wrap = wrap?
|
|
26
|
+
@records << record
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Core
|
|
3
|
+
module Schema
|
|
4
|
+
def version
|
|
5
|
+
read_transaction do
|
|
6
|
+
# BTW: community / enterprise could be retrieved via `result.first.edition`
|
|
7
|
+
query('CALL dbms.components()', {}, skip_instrumentation: true).first[:versions][0]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def indexes
|
|
12
|
+
read_transaction do
|
|
13
|
+
result = query('CALL db.indexes()', {}, skip_instrumentation: true)
|
|
14
|
+
|
|
15
|
+
result.map do |row|
|
|
16
|
+
{ type: row[:type].to_sym, label: label(result, row), properties: properties(row),
|
|
17
|
+
state: row[:state].to_sym }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def constraints
|
|
23
|
+
read_transaction do
|
|
24
|
+
result = query('CALL db.indexes()', {}, skip_instrumentation: true)
|
|
25
|
+
|
|
26
|
+
result.select(&method(v4?(result) ? :v4_filter : :v3_filter)).map do |row|
|
|
27
|
+
{ type: :uniqueness, label: label(result, row), properties: properties(row) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def v4_filter(row)
|
|
35
|
+
row[:uniqueness] == 'UNIQUE'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def v3_filter(row)
|
|
39
|
+
row[:type] == 'node_unique_property'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def label(result, row)
|
|
43
|
+
if v34?(result)
|
|
44
|
+
row[:label]
|
|
45
|
+
else
|
|
46
|
+
(v4?(result) ? row[:labelsOrTypes] : row[:tokenNames]).first
|
|
47
|
+
end.to_sym
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def v4?(result)
|
|
51
|
+
return @v4 unless @v4.nil?
|
|
52
|
+
@v4 = result.keys.include?(:labelsOrTypes)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def v34?(result)
|
|
56
|
+
return @v34 unless @v34.nil?
|
|
57
|
+
@v34 = result.keys.include?(:label)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def properties(row)
|
|
61
|
+
row[:properties].map(&:to_sym)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module ActiveGraph
|
|
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
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module ActiveGraph
|
|
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
|
+
|
|
58
|
+
class Rollback < Error; end
|
|
59
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'active_model/attribute_set'
|
|
2
|
+
|
|
3
|
+
module ActiveGraph
|
|
4
|
+
class LazyAttributeHash < ActiveModel::LazyAttributeHash
|
|
5
|
+
def initialize(values, attr_list)
|
|
6
|
+
@types = {}
|
|
7
|
+
@values = {}
|
|
8
|
+
@additional_types = {}
|
|
9
|
+
@materialized = false
|
|
10
|
+
@delegate_hash = values
|
|
11
|
+
|
|
12
|
+
@default_attributes = process_default_attributes(attr_list)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def marshal_load(values)
|
|
18
|
+
initialize(values[4], values[3])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process_default_attributes(attr_list)
|
|
22
|
+
if attr_list.is_a?(Hash)
|
|
23
|
+
attr_list
|
|
24
|
+
else
|
|
25
|
+
# initialize default attributes map with nil values
|
|
26
|
+
attr_list.each_with_object({}) do |name, map|
|
|
27
|
+
map[name] = nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# we are using with_cast_value here because at the moment casting is being managed by
|
|
33
|
+
# Neo4j and not in ActiveModel
|
|
34
|
+
def assign_default_value(name)
|
|
35
|
+
delegate_hash[name] = ActiveModel::Attribute.with_cast_value(name, default_attributes[name].dup, nil)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'active_graph/migrations/helpers/id_property'
|
|
3
|
+
|
|
4
|
+
module ActiveGraph
|
|
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
|
+
ActiveGraph::Base.query(*args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class AddIdProperty < ActiveGraph::Migration
|
|
35
|
+
include ActiveGraph::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
|
+
Base.magic_query(*args)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def execute(*args)
|
|
62
|
+
Base.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
|
+
# ActiveGraph::Base.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 = ActiveGraph::Base.new_transaction
|
|
117
|
+
|
|
118
|
+
# ActiveGraph::Base.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 ActiveGraph::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
|