neo4j 3.0.0.rc.3 → 3.0.0.rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/Gemfile +5 -4
- data/README.md +1 -1
- data/config/neo4j/add_classnames.yml +1 -0
- data/lib/neo4j.rb +2 -0
- data/lib/neo4j/active_node.rb +4 -1
- data/lib/neo4j/active_node/has_n.rb +75 -9
- data/lib/neo4j/active_node/has_n/association.rb +5 -4
- data/lib/neo4j/active_node/id_property.rb +50 -5
- data/lib/neo4j/active_node/initialize.rb +1 -0
- data/lib/neo4j/active_node/labels.rb +15 -3
- data/lib/neo4j/active_node/orm_adapter.rb +1 -1
- data/lib/neo4j/active_node/persistence.rb +39 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +29 -6
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +20 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +24 -12
- data/lib/neo4j/active_node/query_methods.rb +21 -10
- data/lib/neo4j/active_node/reflection.rb +85 -0
- data/lib/neo4j/active_rel/callbacks.rb +3 -1
- data/lib/neo4j/active_rel/initialize.rb +2 -2
- data/lib/neo4j/active_rel/persistence.rb +23 -14
- data/lib/neo4j/active_rel/property.rb +9 -10
- data/lib/neo4j/active_rel/query.rb +51 -29
- data/lib/neo4j/active_rel/related_node.rb +2 -6
- data/lib/neo4j/migration.rb +185 -0
- data/lib/neo4j/paginated.rb +2 -1
- data/lib/neo4j/railtie.rb +13 -8
- data/lib/neo4j/shared/persistence.rb +10 -56
- data/lib/neo4j/shared/property.rb +15 -6
- data/lib/neo4j/tasks/migration.rake +23 -0
- data/lib/neo4j/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd39f8916d1896f18ed9ce37f44137e383415b30
|
4
|
+
data.tar.gz: bbed25635d7e160853156a666b25589a35986b92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3e192aadf5611e0bfd472eb7253d2863298536bd595a9934e7c08ff3495c6685aa699335a0492f5df57d8d63bd2d84a85b1dc586237c97bf4d60cc353e894b0
|
7
|
+
data.tar.gz: 9dfd058c39bcf5ba5d5476d103b5449070609bc2c998250df343d3334477ef2f034d962abfad2301f7589c61af45e3ae2434d49830897c872716351e152b4829
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 3.0.0.rc.4
|
2
|
+
* UUIDs are now automatically specified on models as neo IDs won't be reliable
|
3
|
+
in future versions of neo4j
|
4
|
+
* Migrations now supported (including built-in migrations to migrate UUIDs and
|
5
|
+
insert the _classname property which is used for performance)
|
6
|
+
* Association reflection
|
7
|
+
* Model.find supports ids/node objects as well as arrays of id/node objects
|
8
|
+
* rake tasks now get automatically included into rails app
|
9
|
+
|
10
|
+
|
1
11
|
== 3.0.0.rc.3
|
2
12
|
* thread safety improvements
|
3
13
|
* scope and general refactoring
|
data/Gemfile
CHANGED
@@ -3,10 +3,11 @@ source 'http://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
#gem 'neo4j-core', path: '../neo4j-core'
|
6
|
-
|
6
|
+
gem 'neo4j-core', git: 'https://github.com/neo4jrb/neo4j-core'
|
7
7
|
#gem 'orm_adapter', :path => '../orm_adapter'
|
8
8
|
|
9
|
-
|
9
|
+
gem 'coveralls', require: false
|
10
|
+
|
10
11
|
|
11
12
|
group 'development' do
|
12
13
|
gem 'pry'
|
@@ -16,9 +17,9 @@ group 'development' do
|
|
16
17
|
end
|
17
18
|
|
18
19
|
group 'test' do
|
19
|
-
gem
|
20
|
+
gem 'simplecov', require: false
|
21
|
+
gem 'simplecov-html', require: false
|
20
22
|
gem "rspec", '~> 2.0'
|
21
|
-
# gem 'rspec-its' instead of its in rspec 3
|
22
23
|
gem "its"
|
23
24
|
gem "test-unit"
|
24
25
|
end
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Welcome to Neo4j.rb [](http://travis-ci.org/neo4jrb/neo4j) [](http://travis-ci.org/neo4jrb/neo4j) [](https://coveralls.io/r/neo4jrb/neo4j?branch=master) [](https://codeclimate.com/github/andreasronge/neo4j)
|
2
2
|
|
3
3
|
Neo4j.rb is an Active Model compliant Ruby/JRuby wrapper for [the Neo4j graph database](http://www.neo4j.org/). It uses the [neo4j-core](https://github.com/neo4jrb/neo4j-core) and [active_attr](https://github.com/cgriego/active_attr) gems.
|
4
4
|
|
@@ -0,0 +1 @@
|
|
1
|
+
# This file is used as the source for adding _classname properties to nodes and relationships.
|
data/lib/neo4j.rb
CHANGED
@@ -40,6 +40,7 @@ require 'neo4j/active_rel'
|
|
40
40
|
|
41
41
|
require 'neo4j/active_node/query_methods'
|
42
42
|
require 'neo4j/active_node/query/query_proxy_methods'
|
43
|
+
require 'neo4j/active_node/query/query_proxy_find_in_batches'
|
43
44
|
require 'neo4j/active_node/labels'
|
44
45
|
require 'neo4j/active_node/id_property'
|
45
46
|
require 'neo4j/active_node/callbacks'
|
@@ -48,6 +49,7 @@ require 'neo4j/active_node/property'
|
|
48
49
|
require 'neo4j/active_node/persistence'
|
49
50
|
require 'neo4j/active_node/validations'
|
50
51
|
require 'neo4j/active_node/rels'
|
52
|
+
require 'neo4j/active_node/reflection'
|
51
53
|
require 'neo4j/active_node/has_n'
|
52
54
|
require 'neo4j/active_node/has_n/association'
|
53
55
|
require 'neo4j/active_node/query/query_proxy'
|
data/lib/neo4j/active_node.rb
CHANGED
@@ -30,6 +30,7 @@ module Neo4j
|
|
30
30
|
include Neo4j::ActiveNode::IdProperty
|
31
31
|
include Neo4j::ActiveNode::SerializedProperties
|
32
32
|
include Neo4j::ActiveNode::Property
|
33
|
+
include Neo4j::ActiveNode::Reflection
|
33
34
|
include Neo4j::ActiveNode::Persistence
|
34
35
|
include Neo4j::ActiveNode::Validations
|
35
36
|
include Neo4j::ActiveNode::Callbacks
|
@@ -45,7 +46,7 @@ module Neo4j
|
|
45
46
|
|
46
47
|
included do
|
47
48
|
def self.inherited(other)
|
48
|
-
inherit_id_property(other) if self.
|
49
|
+
inherit_id_property(other) if self.has_id_property?
|
49
50
|
inherited_indexes(other) if self.respond_to?(:indexed_properties)
|
50
51
|
attributes.each_pair do |k,v|
|
51
52
|
other.attributes[k] = v
|
@@ -66,6 +67,8 @@ module Neo4j
|
|
66
67
|
end
|
67
68
|
|
68
69
|
Neo4j::Session.on_session_available do |_|
|
70
|
+
id_property :uuid, auto: :uuid
|
71
|
+
|
69
72
|
name = Neo4j::Config[:id_property]
|
70
73
|
type = Neo4j::Config[:id_property_type]
|
71
74
|
value = Neo4j::Config[:id_property_type_value]
|
@@ -4,8 +4,65 @@ module HasN
|
|
4
4
|
|
5
5
|
class NonPersistedNodeError < StandardError; end
|
6
6
|
|
7
|
-
|
7
|
+
# Clears out the association cache.
|
8
|
+
def clear_association_cache #:nodoc:
|
9
|
+
association_cache.clear if persisted?
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the current association cache. It is in the format
|
13
|
+
# { :association_name => { :hash_of_cypher_string => [collection] }}
|
14
|
+
def association_cache
|
15
|
+
@association_cache ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
|
19
|
+
# @param [String] cypher_string the cypher, with params, used for lookup
|
20
|
+
# @param [Enumerable] association_obj the HasN::Association object used to perform this query
|
21
|
+
def association_instance_get(cypher_string, association_obj)
|
22
|
+
return if association_cache.nil? || association_cache.empty?
|
23
|
+
lookup_obj = cypher_hash(cypher_string)
|
24
|
+
reflection = association_reflection(association_obj)
|
25
|
+
return if reflection.nil?
|
26
|
+
association_cache[reflection.name] ? association_cache[reflection.name][lookup_obj] : nil
|
27
|
+
end
|
8
28
|
|
29
|
+
# @return [Hash] A hash of all queries in @association_cache created from the association owning this reflection
|
30
|
+
def association_instance_get_by_reflection(reflection_name)
|
31
|
+
association_cache[reflection_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Caches an association result. Unlike ActiveRecord, which stores results in @association_cache using { :association_name => [collection_result] },
|
35
|
+
# ActiveNode stores it using { :association_name => { :hash_string_of_cypher => [collection_result] }}.
|
36
|
+
# This is necessary because an association name by itself does not take into account :where, :limit, :order, etc,... so it's prone to error.
|
37
|
+
# @param [Neo4j::ActiveNode::Query::QueryProxy] query_proxy The QueryProxy object that resulted in this result
|
38
|
+
# @param [Enumerable] collection_result The result of the query after calling :each
|
39
|
+
# @param [Neo4j::ActiveNode::HasN::Association] association_obj The association traversed to create the result
|
40
|
+
def association_instance_set(cypher_string, collection_result, association_obj)
|
41
|
+
return collection_result if Neo4j::Transaction.current
|
42
|
+
cache_key = cypher_hash(cypher_string)
|
43
|
+
reflection = association_reflection(association_obj)
|
44
|
+
return if reflection.nil?
|
45
|
+
if @association_cache[reflection.name]
|
46
|
+
@association_cache[reflection.name][cache_key] = collection_result
|
47
|
+
else
|
48
|
+
@association_cache[reflection.name] = { cache_key => collection_result }
|
49
|
+
end
|
50
|
+
collection_result
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_reflection(association_obj)
|
54
|
+
self.class.reflect_on_association(association_obj.name)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Uses the cypher generated by a QueryProxy object, complete with params, to generate a basic non-cryptographic hash
|
58
|
+
# for use in @association_cache.
|
59
|
+
# @param [String] the cypher used in the query
|
60
|
+
# @return [String] A basic hash of the query
|
61
|
+
def cypher_hash(cypher_string)
|
62
|
+
cypher_string.hash.abs
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
9
66
|
def has_association?(name)
|
10
67
|
!!associations[name.to_sym]
|
11
68
|
end
|
@@ -25,11 +82,11 @@ module HasN
|
|
25
82
|
name = name.to_sym
|
26
83
|
|
27
84
|
association = Neo4j::ActiveNode::HasN::Association.new(:has_many, direction, name, options)
|
28
|
-
|
29
85
|
@associations ||= {}
|
30
86
|
@associations[name] = association
|
31
87
|
|
32
88
|
target_class_name = association.target_class_name || 'nil'
|
89
|
+
create_reflection(:has_many, name, association)
|
33
90
|
|
34
91
|
# TODO: Make assignment more efficient? (don't delete nodes when they are being assigned)
|
35
92
|
module_eval(%Q{
|
@@ -42,13 +99,15 @@ module HasN
|
|
42
99
|
start_object: self,
|
43
100
|
node: node,
|
44
101
|
rel: rel,
|
45
|
-
context: '#{self.name}##{name}'
|
102
|
+
context: '#{self.name}##{name}',
|
103
|
+
caller: self
|
46
104
|
})
|
105
|
+
|
47
106
|
end
|
48
107
|
|
49
108
|
def #{name}=(other_nodes)
|
50
109
|
#{name}(nil, :r).query_as(:n).delete(:r).exec
|
51
|
-
|
110
|
+
clear_association_cache
|
52
111
|
other_nodes.each do |node|
|
53
112
|
#{name} << node
|
54
113
|
end
|
@@ -71,7 +130,8 @@ module HasN
|
|
71
130
|
query_proxy: query_proxy,
|
72
131
|
node: node,
|
73
132
|
rel: rel,
|
74
|
-
context: context
|
133
|
+
context: context,
|
134
|
+
caller: query_proxy.caller
|
75
135
|
})
|
76
136
|
end}, __FILE__, __LINE__)
|
77
137
|
end
|
@@ -80,15 +140,16 @@ module HasN
|
|
80
140
|
name = name.to_sym
|
81
141
|
|
82
142
|
association = Neo4j::ActiveNode::HasN::Association.new(:has_one, direction, name, options)
|
83
|
-
|
84
143
|
@associations ||= {}
|
85
144
|
@associations[name] = association
|
86
145
|
|
87
146
|
target_class_name = association.target_class_name || 'nil'
|
147
|
+
create_reflection(:has_one, name, association)
|
88
148
|
|
89
149
|
module_eval(%Q{
|
90
150
|
def #{name}=(other_node)
|
91
151
|
raise(Neo4j::ActiveNode::HasN::NonPersistedNodeError, 'Unable to create relationship with non-persisted nodes') unless self.persisted?
|
152
|
+
clear_association_cache
|
92
153
|
#{name}_query_proxy(rel: :r).query_as(:n).delete(:r).exec
|
93
154
|
#{name}_query_proxy << other_node
|
94
155
|
end
|
@@ -103,7 +164,14 @@ module HasN
|
|
103
164
|
|
104
165
|
def #{name}(node = nil, rel = nil)
|
105
166
|
return nil unless self.persisted?
|
106
|
-
#{name}_query_proxy(node: node, rel: rel, context: '#{self.name}##{name}')
|
167
|
+
result = #{name}_query_proxy(node: node, rel: rel, context: '#{self.name}##{name}')
|
168
|
+
association = self.class.reflect_on_association(__method__)
|
169
|
+
query_return = association_instance_get(result.to_cypher_with_params, association)
|
170
|
+
if query_return.nil?
|
171
|
+
association_instance_set(result.to_cypher_with_params, result.first, association)
|
172
|
+
else
|
173
|
+
query_return
|
174
|
+
end
|
107
175
|
end}, __FILE__, __LINE__)
|
108
176
|
|
109
177
|
instance_eval(%Q{
|
@@ -118,8 +186,6 @@ module HasN
|
|
118
186
|
#{name}_query_proxy(query_proxy: query_proxy, node: node, rel: rel, context: context)
|
119
187
|
end}, __FILE__, __LINE__)
|
120
188
|
end
|
121
|
-
|
122
|
-
|
123
189
|
end
|
124
190
|
end
|
125
191
|
|
@@ -12,6 +12,7 @@ module Neo4j
|
|
12
12
|
@name = name
|
13
13
|
@direction = direction.to_sym
|
14
14
|
@target_class_name_from_name = name.to_s.classify
|
15
|
+
|
15
16
|
set_vars_from_options(options)
|
16
17
|
end
|
17
18
|
|
@@ -64,6 +65,10 @@ module Neo4j
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
68
|
+
def relationship_class
|
69
|
+
@relationship_class
|
70
|
+
end
|
71
|
+
|
67
72
|
private
|
68
73
|
|
69
74
|
def get_direction(relationship_cypher, create)
|
@@ -94,10 +99,6 @@ module Neo4j
|
|
94
99
|
target_class.associations[@origin].relationship_type
|
95
100
|
end
|
96
101
|
|
97
|
-
def relationship_class
|
98
|
-
@relationship_class
|
99
|
-
end
|
100
|
-
|
101
102
|
private
|
102
103
|
|
103
104
|
def set_vars_from_options(options)
|
@@ -51,18 +51,24 @@ module Neo4j::ActiveNode
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def define_property_method(clazz, name)
|
54
|
+
clear_methods(clazz, name)
|
55
|
+
|
54
56
|
clazz.module_eval(%Q{
|
55
57
|
def id
|
56
58
|
persisted? ? #{name} : nil
|
57
59
|
end
|
58
60
|
|
59
|
-
property :#{name}
|
60
61
|
validates_uniqueness_of :#{name}
|
62
|
+
|
63
|
+
property :#{name}
|
61
64
|
}, __FILE__, __LINE__)
|
65
|
+
|
62
66
|
end
|
63
67
|
|
64
68
|
|
65
69
|
def define_uuid_method(clazz, name)
|
70
|
+
clear_methods(clazz, name)
|
71
|
+
|
66
72
|
clazz.module_eval(%Q{
|
67
73
|
default_property :#{name} do
|
68
74
|
::SecureRandom.uuid
|
@@ -77,6 +83,8 @@ module Neo4j::ActiveNode
|
|
77
83
|
end
|
78
84
|
|
79
85
|
def define_custom_method(clazz, name, on)
|
86
|
+
clear_methods(clazz, name)
|
87
|
+
|
80
88
|
clazz.module_eval(%Q{
|
81
89
|
default_property :#{name} do |instance|
|
82
90
|
raise "Specifying custom id_property #{name} on none existing method #{on}" unless instance.respond_to?(:#{on})
|
@@ -91,17 +99,44 @@ module Neo4j::ActiveNode
|
|
91
99
|
}, __FILE__, __LINE__)
|
92
100
|
end
|
93
101
|
|
102
|
+
def clear_methods(clazz, name)
|
103
|
+
if clazz.method_defined?(name)
|
104
|
+
clazz.module_eval(%Q{
|
105
|
+
undef_method :#{name}
|
106
|
+
}, __FILE__, __LINE__)
|
107
|
+
end
|
108
|
+
|
109
|
+
if clazz.attribute_names.include?(name.to_s)
|
110
|
+
clazz.module_eval(%Q{
|
111
|
+
undef_property :#{name}
|
112
|
+
}, __FILE__, __LINE__)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
94
116
|
extend self
|
95
117
|
end
|
96
118
|
|
97
119
|
|
98
120
|
module ClassMethods
|
99
121
|
|
100
|
-
def find_by_id(
|
101
|
-
|
122
|
+
def find_by_id(id)
|
123
|
+
self.where(id_property_name => id).first
|
124
|
+
end
|
125
|
+
|
126
|
+
def find_by_ids(ids)
|
127
|
+
self.where(id_property_name => ids).to_a
|
102
128
|
end
|
103
129
|
|
104
130
|
def id_property(name, conf = {})
|
131
|
+
begin
|
132
|
+
if has_id_property?
|
133
|
+
unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
|
134
|
+
drop_constraint(id_property_name, type: :unique)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
rescue Neo4j::Server::CypherResponse::ResponseError
|
138
|
+
end
|
139
|
+
|
105
140
|
@id_property_info = {name: name, type: conf}
|
106
141
|
TypeMethods.define_id_methods(self, name, conf)
|
107
142
|
constraint name, type: :unique
|
@@ -111,11 +146,21 @@ module Neo4j::ActiveNode
|
|
111
146
|
end
|
112
147
|
end
|
113
148
|
|
149
|
+
def has_id_property?
|
150
|
+
id_property_info && !id_property_info.empty?
|
151
|
+
end
|
152
|
+
|
114
153
|
def id_property_info
|
115
|
-
@id_property_info ||=
|
154
|
+
@id_property_info ||= {}
|
116
155
|
end
|
117
156
|
|
157
|
+
def id_property_name
|
158
|
+
id_property_info[:name]
|
159
|
+
end
|
160
|
+
|
161
|
+
alias_method :primary_key, :id_property_name
|
162
|
+
|
118
163
|
end
|
119
164
|
end
|
120
165
|
|
121
|
-
end
|
166
|
+
end
|
@@ -8,6 +8,7 @@ module Neo4j::ActiveNode::Initialize
|
|
8
8
|
def init_on_load(persisted_node, properties)
|
9
9
|
@_association_attributes = self.class.extract_association_attributes!(properties)
|
10
10
|
@_persisted_obj = persisted_node
|
11
|
+
@association_cache = {}
|
11
12
|
changed_attributes && changed_attributes.clear
|
12
13
|
@attributes = attributes.merge(properties.stringify_keys)
|
13
14
|
self.default_properties=properties
|
@@ -71,8 +71,13 @@ module Neo4j
|
|
71
71
|
# Returns the object with the specified neo4j id.
|
72
72
|
# @param [String,Fixnum] id of node to find
|
73
73
|
def find(id)
|
74
|
-
|
75
|
-
|
74
|
+
map_id = Proc.new {|object| object.respond_to?(:id) ? object.send(:id) : object }
|
75
|
+
|
76
|
+
if id.is_a?(Array)
|
77
|
+
find_by_ids(id.map {|o| map_id.call(o) })
|
78
|
+
else
|
79
|
+
find_by_id(map_id.call(id))
|
80
|
+
end
|
76
81
|
end
|
77
82
|
|
78
83
|
# Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
|
@@ -129,12 +134,19 @@ module Neo4j
|
|
129
134
|
# Person.constraint :name, type: :unique
|
130
135
|
#
|
131
136
|
def constraint(property, constraints, session = Neo4j::Session.current)
|
132
|
-
Neo4j::Session.on_session_available do |
|
137
|
+
Neo4j::Session.on_session_available do |session|
|
133
138
|
label = Neo4j::Label.create(mapped_label_name)
|
134
139
|
label.create_constraint(property, constraints, session)
|
135
140
|
end
|
136
141
|
end
|
137
142
|
|
143
|
+
def drop_constraint(property, constraint, session = Neo4j::Session.current)
|
144
|
+
Neo4j::Session.on_session_available do |session|
|
145
|
+
label = Neo4j::Label.create(mapped_label_name)
|
146
|
+
label.drop_constraint(property, constraint, session)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
138
150
|
def index?(index_def)
|
139
151
|
mapped_label.indexes[:property_keys].include?([index_def])
|
140
152
|
end
|