neo4j_legacy 7.2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1357 -0
- data/CONTRIBUTORS +8 -0
- data/Gemfile +38 -0
- data/README.md +103 -0
- data/bin/neo4j-jars +33 -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_support/per_thread_registry.rb +1 -0
- data/lib/backports/action_controller/metal/strong_parameters.rb +672 -0
- data/lib/backports/active_model/forbidden_attributes_protection.rb +30 -0
- data/lib/backports/active_support/concern.rb +13 -0
- data/lib/backports/active_support/core_ext/module/attribute_accessors.rb +10 -0
- data/lib/backports/active_support/logger.rb +99 -0
- data/lib/backports/active_support/logger_silence.rb +27 -0
- data/lib/backports/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/backports/active_support/per_thread_registry.rb +60 -0
- data/lib/backports.rb +4 -0
- data/lib/neo4j/active_node/callbacks.rb +8 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +48 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +50 -0
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/enum.rb +29 -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.rb +280 -0
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/has_n.rb +532 -0
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/id_property.rb +187 -0
- data/lib/neo4j/active_node/initialize.rb +21 -0
- data/lib/neo4j/active_node/labels/index.rb +87 -0
- data/lib/neo4j/active_node/labels/reloading.rb +21 -0
- data/lib/neo4j/active_node/labels.rb +198 -0
- data/lib/neo4j/active_node/node_wrapper.rb +52 -0
- data/lib/neo4j/active_node/orm_adapter.rb +82 -0
- data/lib/neo4j/active_node/persistence.rb +175 -0
- data/lib/neo4j/active_node/property.rb +60 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +361 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +61 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +90 -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 +117 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +210 -0
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +83 -0
- data/lib/neo4j/active_node/query.rb +76 -0
- data/lib/neo4j/active_node/query_methods.rb +65 -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 +146 -0
- data/lib/neo4j/active_node/unpersisted.rb +48 -0
- data/lib/neo4j/active_node/validations.rb +59 -0
- data/lib/neo4j/active_node.rb +105 -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/query_factory.rb +95 -0
- data/lib/neo4j/active_rel/persistence.rb +114 -0
- data/lib/neo4j/active_rel/property.rb +95 -0
- data/lib/neo4j/active_rel/query.rb +95 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +22 -0
- data/lib/neo4j/active_rel/related_node.rb +83 -0
- data/lib/neo4j/active_rel/types.rb +82 -0
- data/lib/neo4j/active_rel/validations.rb +8 -0
- data/lib/neo4j/active_rel.rb +67 -0
- data/lib/neo4j/class_arguments.rb +39 -0
- data/lib/neo4j/config.rb +124 -0
- data/lib/neo4j/core/query.rb +22 -0
- data/lib/neo4j/errors.rb +28 -0
- data/lib/neo4j/migration.rb +127 -0
- data/lib/neo4j/paginated.rb +27 -0
- data/lib/neo4j/railtie.rb +169 -0
- data/lib/neo4j/schema/operation.rb +91 -0
- data/lib/neo4j/shared/attributes.rb +220 -0
- data/lib/neo4j/shared/callbacks.rb +64 -0
- data/lib/neo4j/shared/cypher.rb +37 -0
- data/lib/neo4j/shared/declared_properties.rb +204 -0
- data/lib/neo4j/shared/declared_property/index.rb +37 -0
- data/lib/neo4j/shared/declared_property.rb +118 -0
- data/lib/neo4j/shared/enum.rb +148 -0
- data/lib/neo4j/shared/filtered_hash.rb +79 -0
- data/lib/neo4j/shared/identity.rb +28 -0
- data/lib/neo4j/shared/initialize.rb +28 -0
- data/lib/neo4j/shared/marshal.rb +23 -0
- data/lib/neo4j/shared/mass_assignment.rb +58 -0
- data/lib/neo4j/shared/permitted_attributes.rb +28 -0
- data/lib/neo4j/shared/persistence.rb +231 -0
- data/lib/neo4j/shared/property.rb +220 -0
- data/lib/neo4j/shared/query_factory.rb +101 -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 +418 -0
- data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
- data/lib/neo4j/shared/typecaster.rb +53 -0
- data/lib/neo4j/shared/validations.rb +48 -0
- data/lib/neo4j/shared.rb +51 -0
- data/lib/neo4j/tasks/migration.rake +24 -0
- data/lib/neo4j/timestamps/created.rb +9 -0
- data/lib/neo4j/timestamps/updated.rb +9 -0
- data/lib/neo4j/timestamps.rb +11 -0
- data/lib/neo4j/type_converters.rb +7 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/neo4j/wrapper.rb +4 -0
- data/lib/neo4j.rb +96 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +86 -0
- data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
- data/lib/rails/generators/neo4j_generator.rb +67 -0
- data/neo4j.gemspec +43 -0
- metadata +389 -0
data/lib/neo4j/config.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Neo4j
|
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
|
+
CLASS_NAME_PROPERTY_KEY = 'class_name_property'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# In keeping with the Rails convention, this class writer lets you globally configure
|
11
|
+
# the incluse of timestamps on your nodes and rels. It defaults to false, requiring manual
|
12
|
+
# timestamp inclusion.
|
13
|
+
# @return [Boolean] the true/false value specified.
|
14
|
+
|
15
|
+
# @return [Fixnum] The location of the default configuration file.
|
16
|
+
def default_file
|
17
|
+
@default_file ||= DEFAULT_FILE
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets the location of the configuration YAML file and old deletes configurations.
|
21
|
+
# @param [String] file_path represent the path to the file.
|
22
|
+
def default_file=(file_path)
|
23
|
+
delete_all
|
24
|
+
@defaults = nil
|
25
|
+
@default_file = File.expand_path(file_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Hash] the default file loaded by yaml
|
29
|
+
def defaults
|
30
|
+
require 'yaml'
|
31
|
+
@defaults ||= ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(default_file))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Reads from the default_file if configuration is not set already
|
35
|
+
# @return [Hash] the configuration
|
36
|
+
def configuration
|
37
|
+
return @configuration if @configuration
|
38
|
+
|
39
|
+
@configuration = ActiveSupport::HashWithIndifferentAccess.new
|
40
|
+
@configuration.merge!(defaults)
|
41
|
+
@configuration
|
42
|
+
end
|
43
|
+
|
44
|
+
# Yields the configuration
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# Neo4j::Config.use do |config|
|
48
|
+
# config[:storage_path] = '/var/neo4j'
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# @return nil
|
52
|
+
# @yield config
|
53
|
+
# @yieldparam [Neo4j::Config] config - this configuration class
|
54
|
+
def use
|
55
|
+
@configuration ||= ActiveSupport::HashWithIndifferentAccess.new
|
56
|
+
yield @configuration
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Sets the value of a config entry.
|
61
|
+
#
|
62
|
+
# @param [Symbol] key the key to set the parameter for
|
63
|
+
# @param val the value of the parameter.
|
64
|
+
def []=(key, val)
|
65
|
+
configuration[key.to_s] = val
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param [Symbol] key The key of the config entry value we want
|
69
|
+
# @return the the value of a config entry
|
70
|
+
def [](key)
|
71
|
+
configuration[key.to_s]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Remove the value of a config entry.
|
75
|
+
#
|
76
|
+
# @param [Symbol] key the key of the configuration entry to delete
|
77
|
+
# @return The value of the removed entry.
|
78
|
+
def delete(key)
|
79
|
+
configuration.delete(key)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Remove all configuration. This can be useful for testing purpose.
|
83
|
+
#
|
84
|
+
# @return nil
|
85
|
+
def delete_all
|
86
|
+
@configuration = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Hash] The config as a hash.
|
90
|
+
def to_hash
|
91
|
+
configuration.to_hash
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [String] The config as a YAML
|
95
|
+
def to_yaml
|
96
|
+
configuration.to_yaml
|
97
|
+
end
|
98
|
+
|
99
|
+
def include_root_in_json
|
100
|
+
# we use ternary because a simple || will always evaluate true
|
101
|
+
Neo4j::Config[:include_root_in_json].nil? ? true : Neo4j::Config[:include_root_in_json]
|
102
|
+
end
|
103
|
+
|
104
|
+
def module_handling
|
105
|
+
Neo4j::Config[:module_handling] || :none
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Class] The configured timestamps type (e.g. Integer) or the default DateTime.
|
109
|
+
def timestamp_type
|
110
|
+
Neo4j::Config[:timestamp_type] || DateTime
|
111
|
+
end
|
112
|
+
|
113
|
+
def association_model_namespace
|
114
|
+
Neo4j::Config[:association_model_namespace] || nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def association_model_namespace_string
|
118
|
+
namespace = Neo4j::Config[:association_model_namespace]
|
119
|
+
return nil if namespace.nil?
|
120
|
+
"::#{namespace}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Neo4j::Core
|
2
|
+
class Query
|
3
|
+
# Creates a Neo4j::ActiveNode::Query::QueryProxy object that builds off of a Core::Query object.
|
4
|
+
#
|
5
|
+
# @param [Class] model An ActiveNode model to be used as the start of a new QueryuProxy chain
|
6
|
+
# @param [Symbol] var The variable to be used to refer to the object from within the new QueryProxy
|
7
|
+
# @param [Boolean] optional Indicate whether the new QueryProxy will use MATCH or OPTIONAL MATCH.
|
8
|
+
# @return [Neo4j::ActiveNode::Query::QueryProxy] A QueryProxy object.
|
9
|
+
def proxy_as(model, var, optional = false)
|
10
|
+
# TODO: Discuss whether it's necessary to call `break` on the query or if this should be left to the user.
|
11
|
+
Neo4j::ActiveNode::Query::QueryProxy.new(model, nil, node: var, optional: optional, starting_query: self, chain_level: @proxy_chain_level)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Calls proxy_as with `optional` set true. This doesn't offer anything different from calling `proxy_as` directly but it may be more readable.
|
15
|
+
def proxy_as_optional(model, var)
|
16
|
+
proxy_as(model, var, true)
|
17
|
+
end
|
18
|
+
|
19
|
+
# For instances where you turn a QueryProxy into a Query and then back to a QueryProxy with `#proxy_as`
|
20
|
+
attr_accessor :proxy_chain_level
|
21
|
+
end
|
22
|
+
end
|
data/lib/neo4j/errors.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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 InvalidPropertyOptionsError < Error; end
|
21
|
+
|
22
|
+
class InvalidParameterError < Error; end
|
23
|
+
|
24
|
+
class UnknownTypeConverterError < Error; end
|
25
|
+
|
26
|
+
class DangerousAttributeError < ScriptError; end
|
27
|
+
class UnknownAttributeError < NoMethodError; end
|
28
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
class Migration
|
5
|
+
def migrate
|
6
|
+
fail 'not implemented'
|
7
|
+
end
|
8
|
+
|
9
|
+
def output(string = '')
|
10
|
+
puts string unless !!ENV['silenced']
|
11
|
+
end
|
12
|
+
|
13
|
+
def print_output(string)
|
14
|
+
print string unless !!ENV['silenced']
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_path
|
18
|
+
Rails.root if defined? Rails
|
19
|
+
end
|
20
|
+
|
21
|
+
def joined_path(path)
|
22
|
+
File.join(path.to_s, 'db', 'neo4j-migrate')
|
23
|
+
end
|
24
|
+
|
25
|
+
class AddIdProperty < Neo4j::Migration
|
26
|
+
attr_reader :models_filename
|
27
|
+
|
28
|
+
def initialize(path = default_path)
|
29
|
+
@models_filename = File.join(joined_path(path), 'add_id_property.yml')
|
30
|
+
end
|
31
|
+
|
32
|
+
def migrate
|
33
|
+
models = ActiveSupport::HashWithIndifferentAccess.new(YAML.load_file(models_filename))[:models]
|
34
|
+
output 'This task will add an ID Property every node in the given file.'
|
35
|
+
output 'It may take a significant amount of time, please be patient.'
|
36
|
+
models.each do |model|
|
37
|
+
output
|
38
|
+
output
|
39
|
+
output "Adding IDs to #{model}"
|
40
|
+
add_ids_to model.constantize
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup
|
45
|
+
FileUtils.mkdir_p('db/neo4j-migrate')
|
46
|
+
|
47
|
+
return if File.file?(models_filename)
|
48
|
+
|
49
|
+
File.open(models_filename, 'w') do |file|
|
50
|
+
message = <<MESSAGE
|
51
|
+
# Provide models to which IDs should be added.
|
52
|
+
# # It will only modify nodes that do not have IDs. There is no danger of overwriting data.
|
53
|
+
# # models: [Student,Lesson,Teacher,Exam]\nmodels: []
|
54
|
+
MESSAGE
|
55
|
+
file.write(message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def add_ids_to(model)
|
62
|
+
max_per_batch = (ENV['MAX_PER_BATCH'] || default_max_per_batch).to_i
|
63
|
+
|
64
|
+
label = model.mapped_label_name
|
65
|
+
last_time_taken = nil
|
66
|
+
|
67
|
+
until (nodes_left = idless_count(label, model.primary_key)) == 0
|
68
|
+
print_status(last_time_taken, max_per_batch, nodes_left)
|
69
|
+
|
70
|
+
count = [nodes_left, max_per_batch].min
|
71
|
+
last_time_taken = Benchmark.realtime do
|
72
|
+
max_per_batch = id_batch_set(label, model.primary_key, count.times.map { new_id_for(model) }, count)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def idless_count(label, id_property)
|
78
|
+
Neo4j::Session.query.match(n: label).where("NOT EXISTS(n.#{id_property})").pluck('COUNT(n) AS ids').first
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_status(last_time_taken, max_per_batch, nodes_left)
|
82
|
+
time_per_node = last_time_taken / max_per_batch if last_time_taken
|
83
|
+
message = if time_per_node
|
84
|
+
eta_seconds = (nodes_left * time_per_node).round
|
85
|
+
"#{nodes_left} nodes left. Last batch: #{(time_per_node * 1000.0).round(1)}ms / node (ETA: #{eta_seconds / 60} minutes)\r"
|
86
|
+
else
|
87
|
+
"Running first batch...\r"
|
88
|
+
end
|
89
|
+
|
90
|
+
print_output message
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def id_batch_set(label, id_property, new_ids, count)
|
95
|
+
tx = Neo4j::Transaction.new
|
96
|
+
|
97
|
+
Neo4j::Session.query("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
98
|
+
with COLLECT(n) as nodes, #{new_ids} as ids
|
99
|
+
FOREACH(i in range(0,#{count - 1})|
|
100
|
+
FOREACH(node in [nodes[i]]|
|
101
|
+
SET node.#{id_property} = ids[i]))
|
102
|
+
RETURN distinct(true)
|
103
|
+
LIMIT #{count}")
|
104
|
+
|
105
|
+
count
|
106
|
+
rescue Neo4j::Server::CypherResponse::ResponseError, Faraday::TimeoutError
|
107
|
+
new_max_per_batch = (max_per_batch * 0.8).round
|
108
|
+
output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
109
|
+
new_max_per_batch
|
110
|
+
ensure
|
111
|
+
tx.close
|
112
|
+
end
|
113
|
+
|
114
|
+
def default_max_per_batch
|
115
|
+
900
|
116
|
+
end
|
117
|
+
|
118
|
+
def new_id_for(model)
|
119
|
+
if model.id_property_info[:type][:auto]
|
120
|
+
SecureRandom.uuid
|
121
|
+
else
|
122
|
+
model.new.send(model.id_property_info[:type][:on])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Neo4j
|
2
|
+
class Paginated
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :items, :total, :current_page
|
5
|
+
|
6
|
+
def initialize(items, total, current_page)
|
7
|
+
@items = items
|
8
|
+
@total = total
|
9
|
+
@current_page = current_page
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.create_from(source, page, per_page, order = nil)
|
13
|
+
target = source.node_var || source.identity
|
14
|
+
partial = source.skip((page - 1) * per_page).limit(per_page)
|
15
|
+
ordered_partial, ordered_source = if order
|
16
|
+
[partial.order_by(order), source.query.with("#{target} as #{target}").pluck("COUNT(#{target})").first]
|
17
|
+
else
|
18
|
+
[partial, source.count]
|
19
|
+
end
|
20
|
+
Paginated.new(ordered_partial, ordered_source, page)
|
21
|
+
end
|
22
|
+
|
23
|
+
delegate :each, to: :items
|
24
|
+
delegate :pluck, to: :items
|
25
|
+
delegate :size, :[], to: :items
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'active_support/notifications'
|
2
|
+
require 'rails/railtie'
|
3
|
+
# Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
|
4
|
+
require 'action_dispatch/railtie'
|
5
|
+
|
6
|
+
module Neo4j
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
config.neo4j = ActiveSupport::OrderedOptions.new
|
9
|
+
|
10
|
+
if defined?(ActiveSupport::Reloader)
|
11
|
+
ActiveSupport::Reloader.to_prepare do
|
12
|
+
Neo4j::ActiveNode::Labels::Reloading.reload_models!
|
13
|
+
end
|
14
|
+
elsif const_defined?(:ActionDispatch)
|
15
|
+
ActionDispatch::Reloader.to_prepare do
|
16
|
+
Neo4j::ActiveNode::Labels::Reloading.reload_models!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Rescue responses similar to ActiveRecord.
|
21
|
+
# For rails 3.2 and 4.0
|
22
|
+
if config.action_dispatch.respond_to?(:rescue_responses)
|
23
|
+
config.action_dispatch.rescue_responses.merge!(
|
24
|
+
'Neo4j::RecordNotFound' => :not_found,
|
25
|
+
'Neo4j::ActiveNode::Labels::RecordNotFound' => :not_found
|
26
|
+
)
|
27
|
+
else
|
28
|
+
# For rails 3.0 and 3.1
|
29
|
+
ActionDispatch::ShowExceptions.rescue_responses['Neo4j::RecordNotFound'] = :not_found
|
30
|
+
ActionDispatch::ShowExceptions.rescue_responses['Neo4j::ActiveNode::Labels::RecordNotFound'] = :not_found
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add ActiveModel translations to the I18n load_path
|
34
|
+
initializer 'i18n' do
|
35
|
+
config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
|
36
|
+
end
|
37
|
+
|
38
|
+
rake_tasks do
|
39
|
+
load 'neo4j/tasks/migration.rake'
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def java_platform?
|
44
|
+
RUBY_PLATFORM =~ /java/
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_default_session(cfg)
|
48
|
+
setup_config_defaults!(cfg)
|
49
|
+
|
50
|
+
return if !cfg.sessions.empty?
|
51
|
+
|
52
|
+
cfg.sessions << {type: cfg.session_type, path: cfg.session_path, options: cfg.session_options.merge(default: true)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_config_defaults!(cfg)
|
56
|
+
cfg.session_type ||= default_session_type
|
57
|
+
cfg.session_path ||= default_session_path
|
58
|
+
cfg.session_options ||= {}
|
59
|
+
cfg.sessions ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
def config_data
|
63
|
+
@config_data ||= if yaml_path
|
64
|
+
HashWithIndifferentAccess.new(YAML.load(ERB.new(yaml_path.read).result)[Rails.env])
|
65
|
+
else
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def yaml_path
|
71
|
+
@yaml_path ||= %w(config/neo4j.yml config/neo4j.yaml).map do |path|
|
72
|
+
Rails.root.join(path)
|
73
|
+
end.detect(&:exist?)
|
74
|
+
end
|
75
|
+
|
76
|
+
def default_session_type
|
77
|
+
type = ENV['NEO4J_TYPE'] || config_data[:type] || :server_db
|
78
|
+
type.to_sym
|
79
|
+
end
|
80
|
+
|
81
|
+
def default_session_path
|
82
|
+
ENV['NEO4J_URL'] || ENV['NEO4J_PATH'] ||
|
83
|
+
config_data[:url] || config_data[:path] ||
|
84
|
+
'http://localhost:7474'
|
85
|
+
end
|
86
|
+
|
87
|
+
def start_embedded_session(session)
|
88
|
+
# See https://github.com/jruby/jruby/wiki/UnlimitedStrengthCrypto
|
89
|
+
security_class = java.lang.Class.for_name('javax.crypto.JceSecurity')
|
90
|
+
restricted_field = security_class.get_declared_field('isRestricted')
|
91
|
+
restricted_field.accessible = true
|
92
|
+
restricted_field.set nil, false
|
93
|
+
session.start
|
94
|
+
end
|
95
|
+
|
96
|
+
def open_neo4j_session(options, wait_for_connection = false)
|
97
|
+
type, name, default, path = options.values_at(:type, :name, :default, :path)
|
98
|
+
|
99
|
+
if !java_platform? && type == :embedded_db
|
100
|
+
fail "Tried to start embedded Neo4j db without using JRuby (got #{RUBY_PLATFORM}), please run `rvm jruby`"
|
101
|
+
end
|
102
|
+
|
103
|
+
session = wait_for_value(wait_for_connection) do
|
104
|
+
if options.key?(:name)
|
105
|
+
Neo4j::Session.open_named(type, name, default, path)
|
106
|
+
else
|
107
|
+
Neo4j::Session.open(type, path, options[:options])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
start_embedded_session(session) if type == :embedded_db
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def wait_for_value(wait)
|
116
|
+
session = nil
|
117
|
+
Timeout.timeout(60) do
|
118
|
+
until session
|
119
|
+
begin
|
120
|
+
if session = yield
|
121
|
+
puts
|
122
|
+
return session
|
123
|
+
end
|
124
|
+
rescue Faraday::ConnectionFailed => e
|
125
|
+
raise e if !wait
|
126
|
+
|
127
|
+
putc '.'
|
128
|
+
sleep(1)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def register_neo4j_cypher_logging
|
135
|
+
return if @neo4j_cypher_logging_registered
|
136
|
+
|
137
|
+
Neo4j::Core::Query.pretty_cypher = Neo4j::Config[:pretty_logged_cypher_queries]
|
138
|
+
|
139
|
+
Neo4j::Server::CypherSession.log_with do |message|
|
140
|
+
(Neo4j::Config[:logger] || Rails.logger).debug message
|
141
|
+
end
|
142
|
+
|
143
|
+
@neo4j_cypher_logging_registered = true
|
144
|
+
end
|
145
|
+
|
146
|
+
console do
|
147
|
+
Neo4j::Config[:logger] = ActiveSupport::Logger.new(STDOUT)
|
148
|
+
|
149
|
+
register_neo4j_cypher_logging
|
150
|
+
end
|
151
|
+
|
152
|
+
# Starting Neo after :load_config_initializers allows apps to
|
153
|
+
# register migrations in config/initializers
|
154
|
+
initializer 'neo4j.start', after: :load_config_initializers do |app|
|
155
|
+
cfg = app.config.neo4j
|
156
|
+
# Set Rails specific defaults
|
157
|
+
Neo4j::Railtie.setup_default_session(cfg)
|
158
|
+
|
159
|
+
cfg.sessions.each do |session_opts|
|
160
|
+
Neo4j::Railtie.open_neo4j_session(session_opts, cfg.wait_for_connection)
|
161
|
+
end
|
162
|
+
Neo4j::Config.configuration.merge!(cfg.to_hash)
|
163
|
+
|
164
|
+
Neo4j::Config[:logger] ||= Rails.logger
|
165
|
+
|
166
|
+
register_neo4j_cypher_logging
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Schema
|
3
|
+
class Operation
|
4
|
+
attr_reader :label_name, :property, :options
|
5
|
+
|
6
|
+
def initialize(label_name, property, options = default_options)
|
7
|
+
@label_name = label_name.to_sym
|
8
|
+
@property = property.to_sym
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.incompatible_operation_classes
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def create!
|
17
|
+
drop_incompatible!
|
18
|
+
return if exist?
|
19
|
+
label_object.send(:"create_#{type}", property, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def label_object
|
23
|
+
@label_object ||= Neo4j::Label.create(label_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def incompatible_operation_classes
|
27
|
+
self.class.incompatible_operation_classes
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop!
|
31
|
+
label_object.send(:"drop_#{type}", property, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def drop_incompatible!
|
35
|
+
incompatible_operation_classes.each do |clazz|
|
36
|
+
operation = clazz.new(label_name, property)
|
37
|
+
operation.drop! if operation.exist?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def exist?
|
42
|
+
fail 'Abstract class, not implemented'
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_options
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
|
49
|
+
def type
|
50
|
+
fail 'Abstract class, not implemented'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class ExactIndexOperation < Neo4j::Schema::Operation
|
55
|
+
def self.incompatible_operation_classes
|
56
|
+
[UniqueConstraintOperation]
|
57
|
+
end
|
58
|
+
|
59
|
+
def type
|
60
|
+
'index'
|
61
|
+
end
|
62
|
+
|
63
|
+
def exist?
|
64
|
+
label_object.indexes[:property_keys].include?([property])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class UniqueConstraintOperation < Neo4j::Schema::Operation
|
69
|
+
def self.incompatible_operation_classes
|
70
|
+
[ExactIndexOperation]
|
71
|
+
end
|
72
|
+
|
73
|
+
def type
|
74
|
+
'constraint'
|
75
|
+
end
|
76
|
+
|
77
|
+
def create!
|
78
|
+
return if exist?
|
79
|
+
super
|
80
|
+
end
|
81
|
+
|
82
|
+
def exist?
|
83
|
+
Neo4j::Label.constraint?(label_name, property)
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_options
|
87
|
+
{type: :unique}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|