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,39 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module ClassArguments
|
|
3
|
+
class << self
|
|
4
|
+
INVALID_CLASS_ARGUMENT_ERROR = 'option must be String, Symbol, false, nil, or an Array of Symbols/Strings'
|
|
5
|
+
|
|
6
|
+
def valid_argument?(class_argument)
|
|
7
|
+
[NilClass, String, Symbol, FalseClass].include?(class_argument.class) ||
|
|
8
|
+
(class_argument.is_a?(Array) && class_argument.all? { |c| [Symbol, String].include?(c.class) })
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def validate_argument!(class_argument, context)
|
|
12
|
+
return if valid_argument?(class_argument)
|
|
13
|
+
|
|
14
|
+
fail ArgumentError, "#{context} #{INVALID_CLASS_ARGUMENT_ERROR} (was #{class_argument.inspect})"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def node_model?(class_constant)
|
|
18
|
+
class_constant.included_modules.include?(ActiveGraph::Node)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def constantize_argument(class_argument)
|
|
22
|
+
case class_argument
|
|
23
|
+
when 'any', :any, false, nil
|
|
24
|
+
nil
|
|
25
|
+
when Array
|
|
26
|
+
class_argument.map(&method(:constantize_argument))
|
|
27
|
+
else
|
|
28
|
+
class_argument.to_s.constantize.tap do |class_constant|
|
|
29
|
+
if !node_model?(class_constant)
|
|
30
|
+
fail ArgumentError, "#{class_constant} is not an Node model"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
rescue NameError
|
|
35
|
+
raise ArgumentError, "Could not find class: #{class_argument}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
# == Keeps configuration for neo4j
|
|
3
|
+
#
|
|
4
|
+
# == Configurations keys
|
|
5
|
+
class Config
|
|
6
|
+
DEFAULT_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'config', 'neo4j', 'config.yml'))
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
# In keeping with the Rails convention, this class writer lets you globally configure
|
|
10
|
+
# the incluse of timestamps on your nodes and rels. It defaults to false, requiring manual
|
|
11
|
+
# timestamp inclusion.
|
|
12
|
+
# @return [Boolean] the true/false value specified.
|
|
13
|
+
|
|
14
|
+
# @return [Integer] The location of the default configuration file.
|
|
15
|
+
def default_file
|
|
16
|
+
@default_file ||= DEFAULT_FILE
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Sets the location of the configuration YAML file and old deletes configurations.
|
|
20
|
+
# @param [String] file_path represent the path to the file.
|
|
21
|
+
def default_file=(file_path)
|
|
22
|
+
delete_all
|
|
23
|
+
@defaults = nil
|
|
24
|
+
@default_file = File.expand_path(file_path)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Hash] the default file loaded by yaml
|
|
28
|
+
def defaults
|
|
29
|
+
require 'yaml'
|
|
30
|
+
@defaults ||= ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(default_file))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Reads from the default_file if configuration is not set already
|
|
34
|
+
# @return [Hash] the configuration
|
|
35
|
+
def configuration
|
|
36
|
+
return @configuration if @configuration
|
|
37
|
+
|
|
38
|
+
@configuration = ActiveSupport::HashWithIndifferentAccess.new
|
|
39
|
+
@configuration.merge!(defaults)
|
|
40
|
+
@configuration
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Yields the configuration
|
|
44
|
+
#
|
|
45
|
+
# @example
|
|
46
|
+
# ActiveGraph::Config.use do |config|
|
|
47
|
+
# config[:storage_path] = '/var/neo4j'
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# @return nil
|
|
51
|
+
# @yield config
|
|
52
|
+
# @yieldparam [ActiveGraph::Config] config - this configuration class
|
|
53
|
+
def use
|
|
54
|
+
@configuration ||= ActiveSupport::HashWithIndifferentAccess.new
|
|
55
|
+
yield @configuration
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Sets the value of a config entry.
|
|
60
|
+
#
|
|
61
|
+
# @param [Symbol] key the key to set the parameter for
|
|
62
|
+
# @param val the value of the parameter.
|
|
63
|
+
def []=(key, val)
|
|
64
|
+
configuration[key.to_s] = val
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param [Symbol] key The key of the config entry value we want
|
|
68
|
+
# @return the the value of a config entry
|
|
69
|
+
def [](key)
|
|
70
|
+
configuration[key.to_s]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def fetch(key, default)
|
|
74
|
+
configuration.fetch(key, default)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Remove the value of a config entry.
|
|
78
|
+
#
|
|
79
|
+
# @param [Symbol] key the key of the configuration entry to delete
|
|
80
|
+
# @return The value of the removed entry.
|
|
81
|
+
def delete(key)
|
|
82
|
+
configuration.delete(key)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Remove all configuration. This can be useful for testing purpose.
|
|
86
|
+
#
|
|
87
|
+
# @return nil
|
|
88
|
+
def delete_all
|
|
89
|
+
@configuration = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [Hash] The config as a hash.
|
|
93
|
+
def to_hash
|
|
94
|
+
configuration.to_hash
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [String] The config as a YAML
|
|
98
|
+
def to_yaml
|
|
99
|
+
configuration.to_yaml
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def fail_on_pending_migrations
|
|
103
|
+
ActiveGraph::Config[:fail_on_pending_migrations].nil? ? true : ActiveGraph::Config[:fail_on_pending_migrations]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def include_root_in_json
|
|
107
|
+
# we use ternary because a simple || will always evaluate true
|
|
108
|
+
ActiveGraph::Config[:include_root_in_json].nil? ? true : ActiveGraph::Config[:include_root_in_json]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def module_handling
|
|
112
|
+
ActiveGraph::Config[:module_handling] || :none
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @return [Class] The configured timestamps type (e.g. Integer) or the default DateTime.
|
|
116
|
+
def timestamp_type
|
|
117
|
+
ActiveGraph::Config[:timestamp_type] || DateTime
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def association_model_namespace
|
|
121
|
+
ActiveGraph::Config[:association_model_namespace] || nil
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def association_model_namespace_string
|
|
125
|
+
namespace = ActiveGraph::Config[:association_model_namespace]
|
|
126
|
+
return nil if namespace.nil?
|
|
127
|
+
"::#{namespace}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def enums_case_sensitive
|
|
131
|
+
ActiveGraph::Config[:enums_case_sensitive] || false
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'active_graph/core/instrumentable'
|
|
2
|
+
require 'active_graph/core/entity'
|
|
3
|
+
require 'active_graph/core/node'
|
|
4
|
+
require 'active_graph/core/query'
|
|
5
|
+
require 'active_graph/core/record'
|
|
6
|
+
require 'active_graph/core/wrappable'
|
|
7
|
+
require 'active_graph/transaction'
|
|
8
|
+
require 'neo4j_ruby_driver'
|
|
9
|
+
|
|
10
|
+
Neo4j::Driver::Types::Entity.include ActiveGraph::Core::Wrappable
|
|
11
|
+
Neo4j::Driver::Types::Entity.prepend ActiveGraph::Core::Entity
|
|
12
|
+
Neo4j::Driver::Types::Node.prepend ActiveGraph::Core::Node
|
|
13
|
+
Neo4j::Driver::Result.prepend ActiveGraph::Core::Result
|
|
14
|
+
Neo4j::Driver::Record.prepend ActiveGraph::Core::Record
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Core
|
|
3
|
+
class CypherError < StandardError
|
|
4
|
+
attr_reader :code, :original_message, :stack_trace
|
|
5
|
+
|
|
6
|
+
def initialize(code = nil, original_message = nil, stack_trace = nil)
|
|
7
|
+
@code = code
|
|
8
|
+
@original_message = original_message
|
|
9
|
+
@stack_trace = stack_trace
|
|
10
|
+
|
|
11
|
+
msg = <<-ERROR
|
|
12
|
+
Cypher error:
|
|
13
|
+
#{ANSI::CYAN}#{code}#{ANSI::CLEAR}: #{original_message}
|
|
14
|
+
#{stack_trace}
|
|
15
|
+
ERROR
|
|
16
|
+
super(msg)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.new_from(code, message, stack_trace = nil)
|
|
20
|
+
error_class_from(code).new(code, message, stack_trace)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.error_class_from(code)
|
|
24
|
+
case code
|
|
25
|
+
when /(ConstraintValidationFailed|ConstraintViolation)/
|
|
26
|
+
SchemaErrors::ConstraintValidationFailedError
|
|
27
|
+
when /IndexAlreadyExists/
|
|
28
|
+
SchemaErrors::IndexAlreadyExistsError
|
|
29
|
+
when /ConstraintAlreadyExists/ # ?????
|
|
30
|
+
SchemaErrors::ConstraintAlreadyExistsError
|
|
31
|
+
else
|
|
32
|
+
CypherError
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
require 'active_support/notifications'
|
|
3
|
+
require 'active_graph/ansi'
|
|
4
|
+
require 'active_graph/core/logging'
|
|
5
|
+
|
|
6
|
+
module ActiveGraph
|
|
7
|
+
module Core
|
|
8
|
+
module Instrumentable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
EMPTY = ''
|
|
12
|
+
NEWLINE_W_SPACES = "\n "
|
|
13
|
+
|
|
14
|
+
class_methods do
|
|
15
|
+
def subscribe_to_request
|
|
16
|
+
ActiveSupport::Notifications.subscribe('neo4j.core.bolt.request') do |_, start, finish, _id, _payload|
|
|
17
|
+
ms = (finish - start) * 1000
|
|
18
|
+
yield " #{ANSI::BLUE}BOLT:#{ANSI::CLEAR} #{ANSI::YELLOW}#{ms.round}ms#{ANSI::CLEAR}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def subscribe_to_query
|
|
23
|
+
ActiveSupport::Notifications.subscribe('neo4j.core.cypher_query') do |_, _start, _finish, _id, payload|
|
|
24
|
+
query = payload[:query]
|
|
25
|
+
params_string = (query.parameters && !query.parameters.empty? ? "| #{query.parameters.inspect}" : EMPTY)
|
|
26
|
+
cypher = query.pretty_cypher ? (NEWLINE_W_SPACES if query.pretty_cypher.include?("\n")).to_s + query.pretty_cypher.gsub(/\n/, NEWLINE_W_SPACES) : query.cypher
|
|
27
|
+
|
|
28
|
+
source_line, line_number = Logging.first_external_path_and_line(caller_locations)
|
|
29
|
+
|
|
30
|
+
yield " #{ANSI::CYAN}#{query.context || 'CYPHER'}#{ANSI::CLEAR} #{cypher} #{params_string}" +
|
|
31
|
+
("\n ↳ #{source_line}:#{line_number}" if ActiveGraph::Config.fetch(:verbose_query_logs, false) && source_line).to_s
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
module ActiveGraph
|
|
2
|
+
module Core
|
|
3
|
+
class Label
|
|
4
|
+
attr_reader :name
|
|
5
|
+
|
|
6
|
+
def initialize(name)
|
|
7
|
+
@name = name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create_index(property, options = {})
|
|
11
|
+
validate_index_options!(options)
|
|
12
|
+
properties = property.is_a?(Array) ? property.join(',') : property
|
|
13
|
+
schema_query("CREATE INDEX ON :`#{@name}`(#{properties})")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def drop_index(property, options = {})
|
|
17
|
+
validate_index_options!(options)
|
|
18
|
+
schema_query("DROP INDEX ON :`#{@name}`(#{property})")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates a neo4j constraint on a property
|
|
22
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
|
23
|
+
# @example
|
|
24
|
+
# label = ActiveGraph::Label.create(:person)
|
|
25
|
+
# label.create_constraint(:name, {type: :unique})
|
|
26
|
+
#
|
|
27
|
+
def create_constraint(property, constraints)
|
|
28
|
+
cypher = case constraints[:type]
|
|
29
|
+
when :unique, :uniqueness
|
|
30
|
+
"CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
|
31
|
+
else
|
|
32
|
+
fail "Not supported constraint #{constraints.inspect} for property #{property} (expected :type => :unique)"
|
|
33
|
+
end
|
|
34
|
+
schema_query(cypher)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_uniqueness_constraint(property, options = {})
|
|
38
|
+
create_constraint(property, options.merge(type: :unique))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Drops a neo4j constraint on a property
|
|
42
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
|
43
|
+
# @example
|
|
44
|
+
# label = ActiveGraph::Label.create(:person)
|
|
45
|
+
# label.create_constraint(:name, {type: :unique})
|
|
46
|
+
# label.drop_constraint(:name, {type: :unique})
|
|
47
|
+
#
|
|
48
|
+
def drop_constraint(property, constraint)
|
|
49
|
+
cypher = case constraint[:type]
|
|
50
|
+
when :unique, :uniqueness
|
|
51
|
+
"n.`#{property}` IS UNIQUE"
|
|
52
|
+
when :exists
|
|
53
|
+
"exists(n.`#{property}`)"
|
|
54
|
+
else
|
|
55
|
+
fail "Not supported constraint #{constraint.inspect}"
|
|
56
|
+
end
|
|
57
|
+
schema_query("DROP CONSTRAINT ON (n:`#{name}`) ASSERT #{cypher}")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def drop_uniqueness_constraint(property, options = {})
|
|
61
|
+
drop_constraint(property, options.merge(type: :unique))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def indexes
|
|
65
|
+
self.class.indexes.select do |definition|
|
|
66
|
+
definition[:label] == @name.to_sym
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def drop_indexes
|
|
71
|
+
self.class.drop_indexes
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def index?(property)
|
|
75
|
+
indexes.any? { |definition| definition[:properties] == [property.to_sym] }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def constraints(_options = {})
|
|
79
|
+
ActiveGraph::Base.constraints.select do |definition|
|
|
80
|
+
definition[:label] == @name.to_sym
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def uniqueness_constraints(_options = {})
|
|
85
|
+
constraints.select do |definition|
|
|
86
|
+
definition[:type] == :uniqueness
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def constraint?(property)
|
|
91
|
+
constraints.any? { |definition| definition[:properties] == [property.to_sym] }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def uniqueness_constraint?(property)
|
|
95
|
+
uniqueness_constraints.include?([property])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
class << self
|
|
101
|
+
def indexes
|
|
102
|
+
ActiveGraph::Base.indexes
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def drop_indexes
|
|
106
|
+
indexes.each do |definition|
|
|
107
|
+
begin
|
|
108
|
+
ActiveGraph::Base.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
|
109
|
+
rescue Neo4j::Driver::Exceptions::DatabaseException
|
|
110
|
+
# This will error on each constraint. Ignore and continue.
|
|
111
|
+
next
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def drop_constraints
|
|
117
|
+
ActiveGraph::Base.transaction do |tx|
|
|
118
|
+
tx.run('CALL db.constraints').each do |record|
|
|
119
|
+
tx.run("DROP #{record.keys.include?(:name) ? "CONSTRAINT #{record[:name]}" : record[:description]}")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def schema_query(cypher)
|
|
126
|
+
ActiveGraph::Base.query(cypher, {})
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def validate_index_options!(options)
|
|
130
|
+
return unless options[:type] && options[:type] != :exact
|
|
131
|
+
fail "Type #{options[:type]} is not supported"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copied largely from activerecord/lib/active_record/log_subscriber.rb
|
|
2
|
+
module ActiveGraph
|
|
3
|
+
module Core
|
|
4
|
+
module Logging
|
|
5
|
+
class << self
|
|
6
|
+
def first_external_path_and_line(callstack)
|
|
7
|
+
line = callstack.find do |frame|
|
|
8
|
+
frame.absolute_path && !ignored_callstack(frame.absolute_path)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
offending_line = line || callstack.first
|
|
12
|
+
|
|
13
|
+
[offending_line.path,
|
|
14
|
+
offending_line.lineno]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
NEO4J_CORE_GEM_ROOT = File.expand_path('../../..', __dir__) + '/'
|
|
18
|
+
|
|
19
|
+
def ignored_callstack(path)
|
|
20
|
+
paths_to_ignore.any?(&path.method(:start_with?))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def paths_to_ignore
|
|
24
|
+
@paths_to_ignore ||= [NEO4J_CORE_GEM_ROOT,
|
|
25
|
+
RbConfig::CONFIG['rubylibdir'],
|
|
26
|
+
neo4j_gem_path,
|
|
27
|
+
active_support_gem_path].compact
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def neo4j_gem_path
|
|
31
|
+
return if !defined?(::Rails.root)
|
|
32
|
+
|
|
33
|
+
@neo4j_gem_path ||= File.expand_path('../../..', ActiveGraph::Base.method(:driver).source_location[0])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def active_support_gem_path
|
|
37
|
+
return if !defined?(::ActiveSupport::Notifications)
|
|
38
|
+
|
|
39
|
+
@active_support_gem_path ||= File.expand_path('../../..', ActiveSupport::Notifications.method(:subscribe).source_location[0])
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|