neo4j 3.0.4 → 4.0.0.rc.1
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 +22 -0
- data/README.md +4 -0
- data/config/neo4j/config.yml +3 -1
- data/lib/neo4j/active_node/has_n/association.rb +4 -3
- data/lib/neo4j/active_node/id_property.rb +1 -1
- data/lib/neo4j/active_node/initialize.rb +1 -1
- data/lib/neo4j/active_node/labels.rb +20 -13
- data/lib/neo4j/active_node/query/query_proxy.rb +41 -16
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +15 -3
- data/lib/neo4j/active_node/scope.rb +59 -2
- data/lib/neo4j/active_node.rb +13 -11
- data/lib/neo4j/active_rel/initialize.rb +1 -1
- data/lib/neo4j/active_rel/persistence.rb +3 -2
- data/lib/neo4j/active_rel/property.rb +0 -10
- data/lib/neo4j/active_rel/query.rb +0 -4
- data/lib/neo4j/active_rel/rel_wrapper.rb +15 -4
- data/lib/neo4j/active_rel/types.rb +54 -0
- data/lib/neo4j/active_rel.rb +5 -2
- data/lib/neo4j/paginated.rb +1 -1
- data/lib/neo4j/shared/persistence.rb +43 -7
- data/lib/neo4j/shared/property.rb +2 -6
- data/lib/neo4j/shared/rel_type_converters.rb +44 -0
- data/lib/neo4j/shared/serialized_properties.rb +29 -0
- data/lib/neo4j/shared/type_converters.rb +177 -0
- data/lib/neo4j/type_converters.rb +3 -166
- data/lib/neo4j/version.rb +1 -1
- data/lib/neo4j.rb +8 -4
- data/neo4j.gemspec +1 -1
- metadata +11 -8
- data/lib/neo4j/active_node/serialized_properties.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 122872ebd58702ee7d868124092285e8907cfbfd
|
4
|
+
data.tar.gz: 11989c0af301ccb1305df5deb26e9c55a20313f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9433059f7d5dd25d4bd22be97af295f98cbd1340f5bdf1c1869c01111b22d61bf8cf6e260d08de528c3cb90735984d069234fafe32556cd6b200c415b0d7243
|
7
|
+
data.tar.gz: c894497b7e00362c8145bdbf61bdd8b50f772f108b839ae068971bd32e888f502897ecfc03c4284bfd1f838815b75fe8dc41d3222ea61b0d1157d371a3ff325e
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
== 4.0.0.rc.1
|
2
|
+
This release introduces API changes that may be considered breaking under certain conditions. See See https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction.
|
3
|
+
Please use https://github.com/neo4jrb/neo4j/issues for support regarding this update! You can also reach us on Twitter: @neo4jrb (Brian) and @subvertallmedia (Chris).
|
4
|
+
* Default behavior changed: relationship types default to all caps, no prepending of "#". This behavior can be changed.
|
5
|
+
* ActiveRel models no longer require explicit calling of `type`. When missing, the model will infer a type using the class name following the same rules used to determine automatic relationship types from ActiveNode models.
|
6
|
+
* _classname properties will not be added automatically if you are using a version Neo4j >= 2.1.5. Instead, models are found using labels or relationship type. This is a potentially breaking change, particularly where ActiveRel is concerned. See the link at the beginning of this message for the steps required to work around this.
|
7
|
+
* Query scopes are now chainable! Call `all` at the start of your scope or method to take advantage of this.
|
8
|
+
* Changes required for Neo4j 2.2.
|
9
|
+
* Support for custom typecasters.
|
10
|
+
* New method `rel_where`, expanded behavior of `match_to` and `first_rel_to`
|
11
|
+
* Implemented ActiveSupport load hooks.
|
12
|
+
* Assorted performance improvements and refactoring.
|
13
|
+
|
14
|
+
== 3.0.4
|
15
|
+
* Gemspec requires the latest neo4j-core.
|
16
|
+
* Fixed a pagination bug — thanks, @chrisgogreen!
|
17
|
+
* New QueryProxy methods `match_to` and `first_rel_to` are pretty cool.
|
18
|
+
* include_root_in_json is now configurable through config.neo4j.include_root_in_json or Neo4j::Config[:include_root_in_json]. Also cool.
|
19
|
+
* There's a new `delete_all` method for QueryProxy, too.
|
20
|
+
* @codebeige removed the `include?` class method, which was smart.
|
21
|
+
* Did I mention we won an award from Neo Tech? Check it out. https://github.com/neo4jrb/neo4j#welcome-to-neo4jrb
|
22
|
+
|
1
23
|
== 3.0.3
|
2
24
|
* Gemspec has been updated to require neo4j-core 3.0.5
|
3
25
|
* Added `find_in_batches`
|
data/README.md
CHANGED
@@ -6,6 +6,10 @@ Neo4j.rb is an Active Model compliant Ruby/JRuby wrapper for [the Neo4j graph da
|
|
6
6
|
Winner of a 2014 Graphie for "Best Community Contribution" at Neo4j's [Graph Connect](http://graphconnect.com) conference!
|
7
7
|

|
8
8
|
|
9
|
+
## Coming Soon: v4!
|
10
|
+
|
11
|
+
The master branch of this repo now holds stable builds of the v4 version of the gem. Its changes are outlined at [here](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v4-Introduction). Unless otherwise noted, all 3.X documentation is totally valid. v4 discussion will happen [https://github.com/neo4jrb/neo4j/issues/596](https://github.com/neo4jrb/neo4j/issues/596) but if you have any concerns, please contact us directly.
|
12
|
+
|
9
13
|
## Modern (3.X) Documentation
|
10
14
|
|
11
15
|
* [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v3-Introduction)
|
data/config/neo4j/config.yml
CHANGED
@@ -30,4 +30,6 @@ timestamps: true
|
|
30
30
|
# Alternatively, call class method Neo4j::ActiveNode:cache_class to set this on specific models.
|
31
31
|
# By default, this property is called _classname, set as symbol to override.
|
32
32
|
cache_class_names: true
|
33
|
-
# class_name_property: :_classname
|
33
|
+
# class_name_property: :_classname
|
34
|
+
|
35
|
+
transform_rel_type: :upcase
|
@@ -4,6 +4,7 @@ module Neo4j
|
|
4
4
|
module ActiveNode
|
5
5
|
module HasN
|
6
6
|
class Association
|
7
|
+
include Neo4j::Shared::RelTypeConverters
|
7
8
|
attr_reader :type, :name, :relationship, :direction
|
8
9
|
|
9
10
|
def initialize(type, direction, name, options = {})
|
@@ -60,7 +61,7 @@ module Neo4j
|
|
60
61
|
when @origin
|
61
62
|
origin_type
|
62
63
|
else
|
63
|
-
(create || exceptional_target_class?) &&
|
64
|
+
(create || exceptional_target_class?) && decorated_rel_type(@name)
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -136,8 +137,8 @@ module Neo4j
|
|
136
137
|
|
137
138
|
def validate_option_combinations(options)
|
138
139
|
raise ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
|
139
|
-
|
140
|
-
|
140
|
+
raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
|
141
|
+
raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
|
141
142
|
end
|
142
143
|
|
143
144
|
# Determine if model class as derived from the association name would be different than the one specified via the model_class key
|
@@ -13,17 +13,13 @@ module Neo4j
|
|
13
13
|
# @return the labels
|
14
14
|
# @see Neo4j-core
|
15
15
|
def labels
|
16
|
-
|
17
|
-
r = queried_labels
|
18
|
-
@_persisted_obj.labels = r
|
19
|
-
else
|
20
|
-
@_persisted_obj.labels
|
21
|
-
end
|
16
|
+
@_persisted_obj.labels
|
22
17
|
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
# this is handled by core, leaving it now for posterity
|
20
|
+
# def queried_labels
|
21
|
+
# self.class.query_as(:result).where("ID(result)" => self.neo_id).return("LABELS(result) as result_labels").first.result_labels.map(&:to_sym)
|
22
|
+
# end
|
27
23
|
|
28
24
|
# adds one or more labels
|
29
25
|
# @see Neo4j-core
|
@@ -72,7 +68,7 @@ module Neo4j
|
|
72
68
|
|
73
69
|
# Find all nodes/objects of this class
|
74
70
|
def all
|
75
|
-
|
71
|
+
Neo4j::ActiveNode::Query::QueryProxy.new(self, nil, {})
|
76
72
|
end
|
77
73
|
|
78
74
|
# Returns the object with the specified neo4j id.
|
@@ -131,8 +127,7 @@ module Neo4j
|
|
131
127
|
Neo4j::Session.on_session_available do |_|
|
132
128
|
_index(property, conf)
|
133
129
|
end
|
134
|
-
|
135
|
-
@_indexed_properties.push property unless @_indexed_properties.include? property
|
130
|
+
indexed_properties.push property unless indexed_properties.include? property
|
136
131
|
end
|
137
132
|
|
138
133
|
# Creates a neo4j constraint on this class for given property
|
@@ -174,7 +169,19 @@ module Neo4j
|
|
174
169
|
end
|
175
170
|
|
176
171
|
def indexed_properties
|
177
|
-
@_indexed_properties
|
172
|
+
@_indexed_properties ||= []
|
173
|
+
end
|
174
|
+
|
175
|
+
def base_class
|
176
|
+
unless self < Neo4j::ActiveNode
|
177
|
+
raise "#{name} doesn't belong in a hierarchy descending from ActiveNode"
|
178
|
+
end
|
179
|
+
|
180
|
+
if superclass == Object
|
181
|
+
self
|
182
|
+
else
|
183
|
+
superclass.base_class
|
184
|
+
end
|
178
185
|
end
|
179
186
|
|
180
187
|
|
@@ -9,7 +9,7 @@ module Neo4j
|
|
9
9
|
|
10
10
|
# The most recent node to start a QueryProxy chain.
|
11
11
|
# Will be nil when using QueryProxy chains on class methods.
|
12
|
-
attr_reader :caller
|
12
|
+
attr_reader :caller, :association, :model
|
13
13
|
|
14
14
|
# QueryProxy is ActiveNode's Cypher DSL. While the name might imply that it creates queries in a general sense,
|
15
15
|
# it is actually referring to <tt>Neo4j::Core::Query</tt>, which is a pure Ruby Cypher DSL provided by the <tt>neo4j-core</tt> gem.
|
@@ -50,6 +50,7 @@ module Neo4j
|
|
50
50
|
def identity
|
51
51
|
@node_var || :result
|
52
52
|
end
|
53
|
+
alias_method :node_identity, :identity
|
53
54
|
|
54
55
|
# The relationship identifier most recently used by the QueryProxy chain.
|
55
56
|
def rel_identity
|
@@ -106,7 +107,7 @@ module Neo4j
|
|
106
107
|
self.to_a == value
|
107
108
|
end
|
108
109
|
|
109
|
-
METHODS = %w[where order skip limit]
|
110
|
+
METHODS = %w[where rel_where order skip limit]
|
110
111
|
|
111
112
|
METHODS.each do |method|
|
112
113
|
module_eval(%Q{
|
@@ -114,7 +115,8 @@ module Neo4j
|
|
114
115
|
build_deeper_query_proxy(:#{method}, args)
|
115
116
|
end}, __FILE__, __LINE__)
|
116
117
|
end
|
117
|
-
|
118
|
+
# Since there is a rel_where method, it seems only natural for there to be node_where
|
119
|
+
alias_method :node_where, :where
|
118
120
|
alias_method :offset, :skip
|
119
121
|
alias_method :order_by, :order
|
120
122
|
|
@@ -139,7 +141,6 @@ module Neo4j
|
|
139
141
|
# @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
|
140
142
|
# student.lessons.query_as(:l).with('your cypher here...')
|
141
143
|
def query_as(var)
|
142
|
-
var = @node_var if @node_var
|
143
144
|
query = if @association
|
144
145
|
chain_var = _association_chain_var
|
145
146
|
label_string = @model && ":`#{@model.mapped_label_name}`"
|
@@ -154,6 +155,25 @@ module Neo4j
|
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
158
|
+
|
159
|
+
# Scope all queries to the current scope.
|
160
|
+
#
|
161
|
+
# Comment.where(post_id: 1).scoping do
|
162
|
+
# Comment.first
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# TODO: unscoped
|
166
|
+
# Please check unscoped if you want to remove all previous scopes (including
|
167
|
+
# the default_scope) during the execution of a block.
|
168
|
+
def scoping
|
169
|
+
previous, @model.current_scope = @model.current_scope, self
|
170
|
+
yield
|
171
|
+
ensure
|
172
|
+
@model.current_scope = previous
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
|
157
177
|
# Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
|
158
178
|
def to_cypher
|
159
179
|
query.to_cypher
|
@@ -203,7 +223,8 @@ module Neo4j
|
|
203
223
|
start_object = @options[:start_object]
|
204
224
|
start_object.clear_association_cache
|
205
225
|
_session.query(context: @options[:context])
|
206
|
-
.
|
226
|
+
.match("(start#{match_string(start_object)}), (end#{match_string(other_node)})").where("ID(start) = {start_id} AND ID(end) = {end_id}")
|
227
|
+
.params(start_id: start_object.neo_id, end_id: other_node.neo_id)
|
207
228
|
.create("start#{_association_arrow(properties, true)}end").exec
|
208
229
|
|
209
230
|
@association.perform_callback(@options[:start_object], other_node, :after)
|
@@ -213,9 +234,10 @@ module Neo4j
|
|
213
234
|
|
214
235
|
# QueryProxy objects act as a representation of a model at the class level so we pass through calls
|
215
236
|
# This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
|
216
|
-
def method_missing(method_name, *args)
|
237
|
+
def method_missing(method_name, *args, &block)
|
217
238
|
if @model && @model.respond_to?(method_name)
|
218
|
-
|
239
|
+
args[2] = self if @model.has_association?(method_name) || @model.has_scope?(method_name)
|
240
|
+
scoping { @model.public_send(method_name, *args, &block) }
|
219
241
|
else
|
220
242
|
super
|
221
243
|
end
|
@@ -291,12 +313,6 @@ module Neo4j
|
|
291
313
|
|
292
314
|
private
|
293
315
|
|
294
|
-
def call_class_method(method_name, *args)
|
295
|
-
args[2] = self
|
296
|
-
result = @model.send(method_name, *args)
|
297
|
-
result
|
298
|
-
end
|
299
|
-
|
300
316
|
def build_deeper_query_proxy(method, args)
|
301
317
|
self.dup.tap do |new_query|
|
302
318
|
args.each do |arg|
|
@@ -319,7 +335,7 @@ module Neo4j
|
|
319
335
|
node_num = 1
|
320
336
|
result = []
|
321
337
|
if arg.is_a?(Hash)
|
322
|
-
arg.
|
338
|
+
arg.each do |key, value|
|
323
339
|
if @model && @model.has_association?(key)
|
324
340
|
|
325
341
|
neo_id = value.try(:neo_id) || value
|
@@ -341,14 +357,23 @@ module Neo4j
|
|
341
357
|
end
|
342
358
|
result
|
343
359
|
end
|
360
|
+
alias_method :links_for_node_where_arg, :links_for_where_arg
|
361
|
+
|
362
|
+
# We don't accept strings here. If you want to use a string, just use where.
|
363
|
+
def links_for_rel_where_arg(arg)
|
364
|
+
arg.each_with_object([]) do |(key, value), result|
|
365
|
+
result << [:where, ->(v) {{ rel_identity => { key => value }}}]
|
366
|
+
end
|
367
|
+
end
|
344
368
|
|
345
369
|
def links_for_order_arg(arg)
|
346
370
|
[[:order, ->(v) { arg.is_a?(String) ? arg : {v => arg} }]]
|
347
371
|
end
|
348
372
|
|
349
|
-
|
373
|
+
def match_string(node)
|
374
|
+
":`#{node.class.mapped_label_name}`" if node.class.respond_to?(:mapped_label_name)
|
375
|
+
end
|
350
376
|
end
|
351
|
-
|
352
377
|
end
|
353
378
|
end
|
354
379
|
end
|
@@ -21,7 +21,7 @@ module Neo4j
|
|
21
21
|
raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
|
22
22
|
query_with_target(target) do |target|
|
23
23
|
q = distinct.nil? ? target : "DISTINCT #{target}"
|
24
|
-
self.query.pluck("count(#{q}) AS #{target}").first
|
24
|
+
self.query.reorder.pluck("count(#{q}) AS #{target}").first
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -64,15 +64,27 @@ module Neo4j
|
|
64
64
|
end
|
65
65
|
|
66
66
|
# Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id}`
|
67
|
+
# The `node` param can be a persisted ActiveNode instance, any string or integer, or nil.
|
68
|
+
# When it's a node, it'll use the object's neo_id, which is fastest. When not nil, it'll figure out the
|
69
|
+
# primary key of that model. When nil, it uses `1 = 2` to prevent matching all records, which is the default
|
70
|
+
# behavior when nil is passed to `where` in QueryProxy.
|
67
71
|
# @return [Neo4j::ActiveNode::Query::QueryProxy] A QueryProxy object upon which you can build.
|
68
72
|
def match_to(node)
|
69
|
-
|
73
|
+
if node.respond_to?(:neo_id)
|
74
|
+
self.where(neo_id: node.neo_id)
|
75
|
+
elsif !node.nil?
|
76
|
+
id_key = self.association.nil? ? model.primary_key : self.association.target_class.primary_key
|
77
|
+
self.where(id_key => node)
|
78
|
+
else
|
79
|
+
# support for null object pattern
|
80
|
+
self.where('1 = 2')
|
81
|
+
end
|
70
82
|
end
|
71
83
|
|
72
84
|
# Gives you the first relationship between the last link of a QueryProxy chain and a given node
|
73
85
|
# Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r`
|
74
86
|
def first_rel_to(node)
|
75
|
-
self.
|
87
|
+
self.match_to(node).limit(1).pluck(rel_identity).first
|
76
88
|
end
|
77
89
|
|
78
90
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
|
1
3
|
module Neo4j::ActiveNode
|
2
4
|
module Scope
|
3
5
|
extend ActiveSupport::Concern
|
@@ -53,7 +55,9 @@ module Neo4j::ActiveNode
|
|
53
55
|
}, __FILE__, __LINE__)
|
54
56
|
end
|
55
57
|
|
56
|
-
|
58
|
+
def has_scope?(name)
|
59
|
+
_scope.has_key?(name.to_sym)
|
60
|
+
end
|
57
61
|
|
58
62
|
def _scope
|
59
63
|
@_scope ||= {}
|
@@ -68,6 +72,23 @@ module Neo4j::ActiveNode
|
|
68
72
|
end
|
69
73
|
|
70
74
|
|
75
|
+
def current_scope #:nodoc:
|
76
|
+
ScopeRegistry.value_for(:current_scope, base_class.to_s)
|
77
|
+
end
|
78
|
+
|
79
|
+
def current_scope=(scope) #:nodoc:
|
80
|
+
ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def all
|
85
|
+
if current_scope
|
86
|
+
current_scope.clone
|
87
|
+
else
|
88
|
+
self.as(:n)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
71
92
|
end
|
72
93
|
|
73
94
|
class ScopeEvalContext
|
@@ -79,9 +100,45 @@ module Neo4j::ActiveNode
|
|
79
100
|
Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
|
80
101
|
module_eval(%Q{
|
81
102
|
def #{method}(params={})
|
82
|
-
|
103
|
+
@target.all.scoping do
|
104
|
+
(@query_proxy || @target).#{method}(params)
|
105
|
+
end
|
83
106
|
end}, __FILE__, __LINE__)
|
84
107
|
end
|
85
108
|
end
|
109
|
+
|
110
|
+
|
111
|
+
# Stolen from ActiveRecord
|
112
|
+
# https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57
|
113
|
+
class ScopeRegistry # :nodoc:
|
114
|
+
extend ActiveSupport::PerThreadRegistry
|
115
|
+
|
116
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
117
|
+
|
118
|
+
def initialize
|
119
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Obtains the value for a given +scope_name+ and +variable_name+.
|
123
|
+
def value_for(scope_type, variable_name)
|
124
|
+
raise_invalid_scope_type!(scope_type)
|
125
|
+
@registry[scope_type][variable_name]
|
126
|
+
end
|
127
|
+
|
128
|
+
# Sets the +value+ for a given +scope_type+ and +variable_name+.
|
129
|
+
def set_value_for(scope_type, variable_name, value)
|
130
|
+
raise_invalid_scope_type!(scope_type)
|
131
|
+
@registry[scope_type][variable_name] = value
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def raise_invalid_scope_type!(scope_type)
|
137
|
+
if !VALID_SCOPE_TYPES.include?(scope_type)
|
138
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
86
143
|
end
|
87
144
|
end
|
data/lib/neo4j/active_node.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Neo4j
|
2
2
|
|
3
|
-
# Makes Neo4j nodes and relationships behave like
|
3
|
+
# Makes Neo4j nodes and relationships behave like ActiveRecord objects.
|
4
4
|
# By including this module in your class it will create a mapping for the node to your ruby class
|
5
5
|
# by using a Neo4j Label with the same name as the class. When the node is loaded from the database it
|
6
6
|
# will check if there is a ruby class for the labels it has.
|
@@ -28,7 +28,7 @@ module Neo4j
|
|
28
28
|
include Neo4j::Shared::Identity
|
29
29
|
include Neo4j::ActiveNode::Initialize
|
30
30
|
include Neo4j::ActiveNode::IdProperty
|
31
|
-
include Neo4j::
|
31
|
+
include Neo4j::Shared::SerializedProperties
|
32
32
|
include Neo4j::ActiveNode::Property
|
33
33
|
include Neo4j::ActiveNode::Reflection
|
34
34
|
include Neo4j::ActiveNode::Persistence
|
@@ -48,21 +48,24 @@ module Neo4j
|
|
48
48
|
def self.inherited(other)
|
49
49
|
inherit_id_property(other) if self.has_id_property?
|
50
50
|
inherited_indexes(other) if self.respond_to?(:indexed_properties)
|
51
|
-
attributes.each_pair
|
52
|
-
|
53
|
-
end
|
51
|
+
attributes.each_pair { |k,v| other.attributes[k] = v }
|
52
|
+
inherit_serialized_properties(other) if self.respond_to?(:serialized_properties)
|
54
53
|
Neo4j::ActiveNode::Labels.add_wrapped_class(other)
|
55
54
|
super
|
56
55
|
end
|
57
56
|
|
58
57
|
def self.inherited_indexes(other)
|
59
58
|
return if indexed_properties.nil?
|
60
|
-
self.indexed_properties.each {|property| other.index property }
|
59
|
+
self.indexed_properties.each { |property| other.index property }
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.inherit_serialized_properties(other)
|
63
|
+
other.serialized_properties = self.serialized_properties
|
61
64
|
end
|
62
65
|
|
63
66
|
def self.inherit_id_property(other)
|
64
67
|
id_prop = self.id_property_info
|
65
|
-
conf = id_prop[:type].empty? ? {auto: :uuid} : id_prop[:type]
|
68
|
+
conf = id_prop[:type].empty? ? { auto: :uuid } : id_prop[:type]
|
66
69
|
other.id_property id_prop[:name], conf
|
67
70
|
end
|
68
71
|
|
@@ -72,11 +75,10 @@ module Neo4j
|
|
72
75
|
name = Neo4j::Config[:id_property]
|
73
76
|
type = Neo4j::Config[:id_property_type]
|
74
77
|
value = Neo4j::Config[:id_property_type_value]
|
75
|
-
if (name && type && value)
|
76
|
-
id_property(name, type => value)
|
77
|
-
end
|
78
|
+
id_property(name, type => value) if (name && type && value)
|
78
79
|
end
|
79
|
-
|
80
80
|
end
|
81
|
+
|
82
|
+
ActiveSupport.run_load_hooks(:active_node, self)
|
81
83
|
end
|
82
84
|
end
|
@@ -24,6 +24,7 @@ module Neo4j::ActiveRel
|
|
24
24
|
set_timestamps
|
25
25
|
properties = convert_properties_to :db, props
|
26
26
|
rel = _create_rel(from_node, to_node, properties)
|
27
|
+
return self unless rel.respond_to?(:_persisted_obj)
|
27
28
|
init_on_load(rel._persisted_obj, from_node, to_node, @rel_type)
|
28
29
|
true
|
29
30
|
end
|
@@ -61,7 +62,7 @@ module Neo4j::ActiveRel
|
|
61
62
|
def _create_rel(from_node, to_node, *args)
|
62
63
|
props = self.class.default_property_values(self)
|
63
64
|
props.merge!(args[0]) if args[0].is_a?(Hash)
|
64
|
-
set_classname(props)
|
65
|
+
set_classname(props, true)
|
65
66
|
_rel_creation_query(from_node, to_node, props)
|
66
67
|
end
|
67
68
|
|
@@ -99,4 +100,4 @@ module Neo4j::ActiveRel
|
|
99
100
|
end
|
100
101
|
|
101
102
|
end
|
102
|
-
end
|
103
|
+
end
|
@@ -42,16 +42,6 @@ module Neo4j::ActiveRel
|
|
42
42
|
alias_method :start_class, :from_class
|
43
43
|
alias_method :end_class, :to_class
|
44
44
|
|
45
|
-
# @param type [String] sets the relationship type when creating relationships via this class
|
46
|
-
def type(type = nil)
|
47
|
-
@rel_type = type
|
48
|
-
end
|
49
|
-
|
50
|
-
# @return [String] a string representing the relationship type that will be created
|
51
|
-
def _type
|
52
|
-
@rel_type
|
53
|
-
end
|
54
|
-
|
55
45
|
def load_entity(id)
|
56
46
|
Neo4j::Node.load(id)
|
57
47
|
end
|
@@ -26,10 +26,6 @@ module Neo4j::ActiveRel
|
|
26
26
|
where_query.where(where_string(args)).pluck(:r1)
|
27
27
|
end
|
28
28
|
|
29
|
-
def array_load(ids_array)
|
30
|
-
where_query.where("ID(r1) IN {ids_array}").params(ids_array: ids_array).pluck(:r1)
|
31
|
-
end
|
32
|
-
|
33
29
|
# Performs a basic match on the relationship, returning all results.
|
34
30
|
# This is not executed lazily, it will immediately return matching objects.
|
35
31
|
def all
|
@@ -3,16 +3,27 @@ class Neo4j::Relationship
|
|
3
3
|
module Wrapper
|
4
4
|
def wrapper
|
5
5
|
props.symbolize_keys!
|
6
|
-
return self unless props.is_a?(Hash)
|
6
|
+
# return self unless props.is_a?(Hash)
|
7
7
|
begin
|
8
|
-
|
8
|
+
most_concrete_class = sorted_wrapper_classes
|
9
|
+
wrapped_rel = most_concrete_class.constantize.new
|
9
10
|
rescue NameError
|
10
11
|
return self
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
wrapped_rel.init_on_load(self, self._start_node_id, self._end_node_id, self.rel_type)
|
14
15
|
wrapped_rel
|
15
16
|
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def sorted_wrapper_classes
|
21
|
+
props[Neo4j::Config.class_name_property] || class_from_type
|
22
|
+
end
|
23
|
+
|
24
|
+
def class_from_type
|
25
|
+
Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] || rel_type.camelize
|
26
|
+
end
|
16
27
|
end
|
17
28
|
|
18
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveRel
|
3
|
+
|
4
|
+
# provides mapping of type to model name
|
5
|
+
module Types
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# WRAPPED_CLASSES maps relationship types to ActiveRel models.
|
9
|
+
#
|
10
|
+
# Typically, it's a 1:1 relationship, with a type having a model of the same name. Sometimes, someone needs to be a precious
|
11
|
+
# snowflake and have a model name that doesn't match the rel type, so this comes in handy.
|
12
|
+
#
|
13
|
+
# As an example, Chris often finds it easier to name models after the classes that use the relationship: `StudentLesson` instead of
|
14
|
+
# `EnrolledIn`, because it's easier to remember "A student has a relationship to lesson" than "the type of relationship between Student
|
15
|
+
# and Lesson is 'EnrolledIn'." After all, that is a big part of why we have models, right? To make our lives easier?
|
16
|
+
#
|
17
|
+
# A model is added to WRAPPED_CLASSES when it is initalized AND when the `type` class method is called within a model. This means that
|
18
|
+
# it's possible a model will be added twice: once with the rel_type version of the model name, again with the custom type. deal_with_it.gif.
|
19
|
+
#
|
20
|
+
# As an alternative to this, you can call the `set_classname` class method to insert a `_classname` property into your relationship,
|
21
|
+
# which will completely bypass this whole process.
|
22
|
+
WRAPPED_CLASSES = {}
|
23
|
+
|
24
|
+
included do |klazz|
|
25
|
+
type self.name, true
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
include Neo4j::Shared::RelTypeConverters
|
30
|
+
|
31
|
+
# @param type [String] sets the relationship type when creating relationships via this class
|
32
|
+
def type(given_type = self.name, auto = false)
|
33
|
+
use_type = auto ? decorated_rel_type(given_type) : given_type
|
34
|
+
add_wrapped_class use_type
|
35
|
+
@rel_type = use_type
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [String] a string representing the relationship type that will be created
|
39
|
+
def _type
|
40
|
+
@rel_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_wrapped_class(type)
|
44
|
+
#_wrapped_classes[type.to_sym.downcase] = self.name
|
45
|
+
_wrapped_classes[type.to_sym] = self.name
|
46
|
+
end
|
47
|
+
|
48
|
+
def _wrapped_classes
|
49
|
+
Neo4j::ActiveRel::Types::WRAPPED_CLASSES
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|