neo4j 5.0.15 → 5.1.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -5
- data/Gemfile +1 -1
- data/README.md +8 -0
- data/lib/neo4j.rb +4 -0
- data/lib/neo4j/active_node.rb +3 -1
- data/lib/neo4j/active_node/dependent/association_methods.rb +4 -2
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +3 -3
- data/lib/neo4j/active_node/has_n.rb +103 -36
- data/lib/neo4j/active_node/has_n/association.rb +10 -33
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/id_property.rb +19 -11
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/labels.rb +13 -2
- data/lib/neo4j/active_node/persistence.rb +19 -4
- data/lib/neo4j/active_node/property.rb +4 -3
- data/lib/neo4j/active_node/query/query_proxy.rb +29 -13
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +8 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +7 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +16 -6
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +4 -0
- data/lib/neo4j/active_node/query/query_proxy_unpersisted.rb +17 -0
- data/lib/neo4j/active_node/unpersisted.rb +49 -0
- data/lib/neo4j/active_node/validations.rb +1 -1
- data/lib/neo4j/active_rel.rb +17 -0
- data/lib/neo4j/active_rel/persistence.rb +10 -5
- data/lib/neo4j/active_rel/property.rb +17 -5
- data/lib/neo4j/railtie.rb +2 -1
- data/lib/neo4j/shared/declared_property_manager.rb +10 -0
- data/lib/neo4j/shared/initialize.rb +3 -3
- data/lib/neo4j/shared/property.rb +7 -51
- data/lib/neo4j/shared/property/default_property.rb +0 -0
- data/lib/neo4j/shared/type_converters.rb +49 -6
- data/lib/neo4j/shared/typecaster.rb +22 -18
- data/lib/neo4j/shared/validations.rb +1 -1
- data/lib/neo4j/version.rb +1 -1
- data/neo4j.gemspec +1 -1
- metadata +12 -7
@@ -6,6 +6,7 @@ module Neo4j
|
|
6
6
|
class Association
|
7
7
|
include Neo4j::Shared::RelTypeConverters
|
8
8
|
include Neo4j::ActiveNode::Dependent::AssociationMethods
|
9
|
+
include Neo4j::ActiveNode::HasN::AssociationCypherMethods
|
9
10
|
|
10
11
|
attr_reader :type, :name, :relationship, :direction, :dependent, :model_class
|
11
12
|
|
@@ -29,7 +30,9 @@ module Neo4j
|
|
29
30
|
|
30
31
|
def refresh_model_class!
|
31
32
|
@pending_model_refresh = @target_classes_or_nil = nil
|
32
|
-
|
33
|
+
|
34
|
+
# Using #to_s on purpose here to take care of classes/strings/symbols
|
35
|
+
@model_class = @model_class.to_s.constantize if @model_class
|
33
36
|
end
|
34
37
|
|
35
38
|
def queue_model_refresh!
|
@@ -49,12 +52,6 @@ module Neo4j
|
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
52
|
-
# Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
|
53
|
-
def arrow_cypher(var = nil, properties = {}, create = false, reverse = false)
|
54
|
-
validate_origin!
|
55
|
-
direction_cypher(get_relationship_cypher(var, properties, create), create, reverse)
|
56
|
-
end
|
57
|
-
|
58
55
|
def pending_model_refresh?
|
59
56
|
!!@pending_model_refresh
|
60
57
|
end
|
@@ -110,9 +107,12 @@ module Neo4j
|
|
110
107
|
|
111
108
|
def relationship_type(create = false)
|
112
109
|
case
|
113
|
-
when relationship_class
|
114
|
-
|
115
|
-
when @
|
110
|
+
when relationship_class
|
111
|
+
relationship_class_type
|
112
|
+
when @relationship_type
|
113
|
+
@relationship_type
|
114
|
+
when @origin
|
115
|
+
origin_type
|
116
116
|
else
|
117
117
|
(create || exceptional_target_class?) && decorated_rel_type(@name)
|
118
118
|
end
|
@@ -148,14 +148,6 @@ module Neo4j
|
|
148
148
|
Neo4j::Config.association_model_namespace_string
|
149
149
|
end
|
150
150
|
|
151
|
-
def direction_cypher(relationship_cypher, create, reverse = false)
|
152
|
-
case get_direction(create, reverse)
|
153
|
-
when :out then "-#{relationship_cypher}->"
|
154
|
-
when :in then "<-#{relationship_cypher}-"
|
155
|
-
when :both then "-#{relationship_cypher}-"
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
151
|
def get_direction(create, reverse = false)
|
160
152
|
dir = (create && @direction == :both) ? :out : @direction
|
161
153
|
if reverse
|
@@ -169,21 +161,6 @@ module Neo4j
|
|
169
161
|
end
|
170
162
|
end
|
171
163
|
|
172
|
-
def get_relationship_cypher(var, properties, create)
|
173
|
-
relationship_type = relationship_type(create)
|
174
|
-
relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
|
175
|
-
properties_string = get_properties_string(properties)
|
176
|
-
|
177
|
-
"[#{var}#{relationship_name_cypher}#{properties_string}]"
|
178
|
-
end
|
179
|
-
|
180
|
-
def get_properties_string(properties)
|
181
|
-
p = properties.map do |key, value|
|
182
|
-
"#{key}: #{value.inspect}"
|
183
|
-
end.join(', ')
|
184
|
-
p.size == 0 ? '' : " {#{p}}"
|
185
|
-
end
|
186
|
-
|
187
164
|
def origin_association
|
188
165
|
target_class.associations[@origin]
|
189
166
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
module HasN
|
4
|
+
module AssociationCypherMethods
|
5
|
+
# Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
|
6
|
+
def arrow_cypher(var = nil, properties = {}, create = false, reverse = false, length = nil)
|
7
|
+
validate_origin!
|
8
|
+
|
9
|
+
if create && length.present?
|
10
|
+
fail(ArgumentError, 'rel_length option cannot be specified when creating a relationship')
|
11
|
+
end
|
12
|
+
|
13
|
+
direction_cypher(get_relationship_cypher(var, properties, create, length), create, reverse)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def direction_cypher(relationship_cypher, create, reverse = false)
|
19
|
+
case get_direction(create, reverse)
|
20
|
+
when :out
|
21
|
+
"-#{relationship_cypher}->"
|
22
|
+
when :in
|
23
|
+
"<-#{relationship_cypher}-"
|
24
|
+
when :both
|
25
|
+
"-#{relationship_cypher}-"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_relationship_cypher(var, properties, create, length)
|
30
|
+
relationship_type = relationship_type(create)
|
31
|
+
relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
|
32
|
+
rel_length_cypher = cypher_for_rel_length(length)
|
33
|
+
properties_string = get_properties_string(properties)
|
34
|
+
|
35
|
+
"[#{var}#{relationship_name_cypher}#{rel_length_cypher}#{properties_string}]"
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_properties_string(properties)
|
39
|
+
p = properties.map do |key, value|
|
40
|
+
"#{key}: #{value.inspect}"
|
41
|
+
end.join(', ')
|
42
|
+
p.size == 0 ? '' : " {#{p}}"
|
43
|
+
end
|
44
|
+
|
45
|
+
VALID_REL_LENGTH_SYMBOLS = {
|
46
|
+
any: '*'
|
47
|
+
}
|
48
|
+
|
49
|
+
def cypher_for_rel_length(length)
|
50
|
+
return nil if length.blank?
|
51
|
+
|
52
|
+
validate_rel_length!(length)
|
53
|
+
|
54
|
+
case length
|
55
|
+
when Symbol then VALID_REL_LENGTH_SYMBOLS[length]
|
56
|
+
when Fixnum then "*#{length}"
|
57
|
+
when Range then cypher_for_range_rel_length(length)
|
58
|
+
when Hash then cypher_for_hash_rel_length(length)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cypher_for_range_rel_length(length)
|
63
|
+
range_end = length.end
|
64
|
+
range_end = nil if range_end == Float::INFINITY
|
65
|
+
"*#{length.begin}..#{range_end}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def cypher_for_hash_rel_length(length)
|
69
|
+
range_end = length[:max]
|
70
|
+
range_end = nil if range_end == Float::INFINITY
|
71
|
+
"*#{length[:min]}..#{range_end}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_rel_length!(length)
|
75
|
+
message = rel_length_error_message(length)
|
76
|
+
fail(ArgumentError, "Invalid value for rel_length (#{length.inspect}): #{message}") if message
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def rel_length_error_message(length)
|
81
|
+
case length
|
82
|
+
when Fixnum then 'cannot be negative' if length < 0
|
83
|
+
when Symbol then rel_length_symbol_error_message(length)
|
84
|
+
when Range then rel_length_range_error_message(length)
|
85
|
+
when Hash then rel_length_hash_error_message(length)
|
86
|
+
else 'should be a Symbol, Fixnum, Range or Hash'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def rel_length_symbol_error_message(length)
|
91
|
+
"expecting one of #{VALID_REL_LENGTH_SYMBOLS.keys.inspect}" if !VALID_REL_LENGTH_SYMBOLS.key?(length)
|
92
|
+
end
|
93
|
+
|
94
|
+
def rel_length_range_error_message(length)
|
95
|
+
if length.begin > length.end
|
96
|
+
'cannot be a decreasing Range'
|
97
|
+
elsif length.begin < 0
|
98
|
+
'cannot include negative values'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def rel_length_hash_error_message(length)
|
103
|
+
'Hash keys should be a subset of [:min, :max]' if (length.keys & [:min, :max]) != length.keys
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -34,7 +34,7 @@ module Neo4j::ActiveNode
|
|
34
34
|
#
|
35
35
|
module IdProperty
|
36
36
|
extend ActiveSupport::Concern
|
37
|
-
|
37
|
+
include Accessor
|
38
38
|
|
39
39
|
module TypeMethods
|
40
40
|
def define_id_methods(clazz, name, conf)
|
@@ -90,7 +90,7 @@ module Neo4j::ActiveNode
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def #{name}
|
93
|
-
|
93
|
+
default_property_value
|
94
94
|
end
|
95
95
|
|
96
96
|
alias_method :id, :#{name}
|
@@ -102,12 +102,12 @@ module Neo4j::ActiveNode
|
|
102
102
|
|
103
103
|
clazz.module_eval(%{
|
104
104
|
default_property :#{name} do |instance|
|
105
|
-
|
106
|
-
|
105
|
+
raise "Specifying custom id_property #{name} on non-existent method #{on}" unless instance.respond_to?(:#{on})
|
106
|
+
instance.#{on}
|
107
107
|
end
|
108
108
|
|
109
109
|
def #{name}
|
110
|
-
|
110
|
+
default_property_value
|
111
111
|
end
|
112
112
|
|
113
113
|
alias_method :id, :#{name}
|
@@ -124,6 +124,8 @@ module Neo4j::ActiveNode
|
|
124
124
|
|
125
125
|
|
126
126
|
module ClassMethods
|
127
|
+
attr_accessor :manual_id_property
|
128
|
+
|
127
129
|
def find_by_neo_id(id)
|
128
130
|
Neo4j::Node.load(id)
|
129
131
|
end
|
@@ -137,12 +139,14 @@ module Neo4j::ActiveNode
|
|
137
139
|
end
|
138
140
|
|
139
141
|
def id_property(name, conf = {})
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
142
|
+
self.manual_id_property = true
|
143
|
+
Neo4j::Session.on_session_available do |_|
|
144
|
+
@id_property_info = {name: name, type: conf}
|
145
|
+
TypeMethods.define_id_methods(self, name, conf)
|
146
|
+
constraint(name, type: :unique) unless conf[:constraint] == false
|
144
147
|
|
145
|
-
|
148
|
+
self.define_singleton_method(:find_by_id) { |key| self.where(name => key).first }
|
149
|
+
end
|
146
150
|
end
|
147
151
|
|
148
152
|
# rubocop:disable Style/PredicateName
|
@@ -165,6 +169,10 @@ module Neo4j::ActiveNode
|
|
165
169
|
id_property_info[:name]
|
166
170
|
end
|
167
171
|
|
172
|
+
def manual_id_property?
|
173
|
+
!!manual_id_property
|
174
|
+
end
|
175
|
+
|
168
176
|
alias_method :primary_key, :id_property_name
|
169
177
|
|
170
178
|
private
|
@@ -173,7 +181,7 @@ module Neo4j::ActiveNode
|
|
173
181
|
if id_property?
|
174
182
|
unless mapped_label.uniqueness_constraints[:property_keys].include?([name])
|
175
183
|
# Neo4j Embedded throws a crazy error when a constraint can't be dropped
|
176
|
-
drop_constraint(id_property_name, type: :unique)
|
184
|
+
drop_constraint(id_property_name, type: :unique) if constraint?(mapped_label_name, id_property_name)
|
177
185
|
end
|
178
186
|
end
|
179
187
|
rescue Neo4j::Server::CypherResponse::ResponseError, Java::OrgNeo4jCypher::CypherExecutionException
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Neo4j::ActiveNode::IdProperty
|
2
|
+
# Provides get/set of the Id Property values.
|
3
|
+
# Some methods
|
4
|
+
module Accessor
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
attr_reader :default_property_value
|
8
|
+
|
9
|
+
def default_properties=(properties)
|
10
|
+
@default_property_value = properties[default_property_key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_property(key)
|
14
|
+
return nil unless key == default_property_key
|
15
|
+
default_property_value
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_property_key
|
19
|
+
self.class.default_property_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_properties
|
23
|
+
@default_properties ||= Hash.new(nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def default_property_key
|
28
|
+
@default_property_key ||= default_properties_keys.first
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: Move this to the DeclaredPropertyManager
|
32
|
+
def default_property(name, &block)
|
33
|
+
reset_default_properties(name) if default_properties.respond_to?(:size)
|
34
|
+
default_properties[name] = block
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Hash<Symbol,Proc>]
|
38
|
+
def default_properties
|
39
|
+
@default_property ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_properties_keys
|
43
|
+
@default_properties_keys ||= default_properties.keys
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset_default_properties(name_to_keep)
|
47
|
+
default_properties.each_key do |property|
|
48
|
+
@default_properties_keys = nil
|
49
|
+
undef_method(property) unless property == name_to_keep
|
50
|
+
end
|
51
|
+
@default_properties_keys = nil
|
52
|
+
@default_property = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_property_values(instance)
|
56
|
+
default_properties.each_with_object({}) do |(key, block), result|
|
57
|
+
result[key] = block.call(instance)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -103,7 +103,7 @@ module Neo4j
|
|
103
103
|
# Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
|
104
104
|
# @param Hash args of arguments to find
|
105
105
|
def find_by(values)
|
106
|
-
all.
|
106
|
+
all.where(values).limit(1).query_as(:n).pluck(:n).first
|
107
107
|
end
|
108
108
|
|
109
109
|
# Like find_by, except that if no record is found, raises a RecordNotFound error.
|
@@ -147,6 +147,7 @@ module Neo4j
|
|
147
147
|
# end
|
148
148
|
def index(property, conf = {})
|
149
149
|
Neo4j::Session.on_session_available do |_|
|
150
|
+
drop_constraint(property, type: :unique) if Neo4j::Label.constraint?(mapped_label_name, property)
|
150
151
|
_index(property, conf)
|
151
152
|
end
|
152
153
|
indexed_properties.push property unless indexed_properties.include? property
|
@@ -161,12 +162,22 @@ module Neo4j
|
|
161
162
|
Neo4j::Session.on_session_available do |session|
|
162
163
|
unless Neo4j::Label.constraint?(mapped_label_name, property)
|
163
164
|
label = Neo4j::Label.create(mapped_label_name)
|
165
|
+
drop_index(property, label) if index?(property)
|
164
166
|
label.create_constraint(property, constraints, session)
|
165
167
|
end
|
166
168
|
end
|
167
169
|
end
|
168
170
|
|
169
|
-
|
171
|
+
# @param [Symbol] property The name of the property index to be dropped
|
172
|
+
# @param [Neo4j::Label] label An instance of label from Neo4j::Core
|
173
|
+
def drop_index(property, label = nil)
|
174
|
+
label_obj = label || Neo4j::Label.create(mapped_label_name)
|
175
|
+
label_obj.drop_index(property)
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param [Symbol] property The name of the property constraint to be dropped
|
179
|
+
# @param [Hash] constraint The constraint type to be dropped.
|
180
|
+
def drop_constraint(property, constraint = {type: :unique})
|
170
181
|
Neo4j::Session.on_session_available do |session|
|
171
182
|
label = Neo4j::Label.create(mapped_label_name)
|
172
183
|
label.drop_constraint(property, constraint, session)
|
@@ -24,8 +24,10 @@ module Neo4j::ActiveNode
|
|
24
24
|
# If any of the before_* callbacks return false the action is cancelled and save returns false.
|
25
25
|
def save(*)
|
26
26
|
update_magic_properties
|
27
|
-
|
28
|
-
|
27
|
+
cascade_save do
|
28
|
+
association_proxy_cache.clear
|
29
|
+
create_or_update
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
31
33
|
# Persist the object to the database. Validations and Callbacks are included
|
@@ -53,7 +55,7 @@ module Neo4j::ActiveNode
|
|
53
55
|
node = _create_node(properties)
|
54
56
|
init_on_load(node, node.props)
|
55
57
|
send_props(@relationship_props) if @relationship_props
|
56
|
-
@relationship_props = nil
|
58
|
+
@relationship_props = @deferred_nodes = nil
|
57
59
|
true
|
58
60
|
end
|
59
61
|
|
@@ -66,12 +68,25 @@ module Neo4j::ActiveNode
|
|
66
68
|
session.create_node(props, labels)
|
67
69
|
end
|
68
70
|
|
71
|
+
private
|
72
|
+
|
73
|
+
# The pending associations are cleared during the save process, so it's necessary to
|
74
|
+
# build the processable hash before it begins. If there are nodes and associations that
|
75
|
+
# need to be created after the node is saved, a new transaction is started.
|
76
|
+
def cascade_save
|
77
|
+
deferred_nodes = pending_associations_with_nodes
|
78
|
+
Neo4j::Transaction.run(!deferred_nodes.blank?) do
|
79
|
+
result = yield
|
80
|
+
process_unpersisted_nodes!(deferred_nodes) if deferred_nodes
|
81
|
+
result
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
69
85
|
module ClassMethods
|
70
86
|
# Creates and saves a new node
|
71
87
|
# @param [Hash] props the properties the new node should have
|
72
88
|
def create(props = {})
|
73
89
|
association_props = extract_association_attributes!(props) || {}
|
74
|
-
|
75
90
|
new(props).tap do |obj|
|
76
91
|
yield obj if block_given?
|
77
92
|
obj.save
|
@@ -3,9 +3,9 @@ module Neo4j::ActiveNode
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
include Neo4j::Shared::Property
|
5
5
|
|
6
|
-
def initialize(attributes =
|
7
|
-
super(attributes
|
8
|
-
@attributes ||= self.class.attributes_nil_hash
|
6
|
+
def initialize(attributes = nil)
|
7
|
+
super(attributes)
|
8
|
+
@attributes ||= Hash[self.class.attributes_nil_hash]
|
9
9
|
send_props(@relationship_props) if _persisted_obj && !@relationship_props.nil?
|
10
10
|
end
|
11
11
|
|
@@ -22,6 +22,7 @@ module Neo4j::ActiveNode
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def contains_association?(attributes)
|
25
|
+
return false unless attributes
|
25
26
|
attributes.each_key { |key| return true if associations_keys.include?(key) }
|
26
27
|
false
|
27
28
|
end
|
@@ -6,6 +6,7 @@ module Neo4j
|
|
6
6
|
include Neo4j::ActiveNode::Query::QueryProxyMethods
|
7
7
|
include Neo4j::ActiveNode::Query::QueryProxyFindInBatches
|
8
8
|
include Neo4j::ActiveNode::Query::QueryProxyEagerLoading
|
9
|
+
include Neo4j::ActiveNode::Query::QueryProxyUnpersisted
|
9
10
|
include Neo4j::ActiveNode::Dependent::QueryProxyMethods
|
10
11
|
|
11
12
|
# The most recent node to start a QueryProxy chain.
|
@@ -26,11 +27,13 @@ module Neo4j
|
|
26
27
|
# @param [Neo4j::ActiveNode::HasN::Association] association The ActiveNode association (an object created by a <tt>has_one</tt> or
|
27
28
|
# <tt>has_many</tt>) that created this object.
|
28
29
|
# @param [Hash] options Additional options pertaining to the QueryProxy object. These may include:
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
30
|
+
# @option options [String, Symbol] :node_var A string or symbol to be used by Cypher within its query string as an identifier
|
31
|
+
# @option options [String, Symbol] :rel_var Same as above but pertaining to a relationship identifier
|
32
|
+
# @option options [Range, Fixnum, Symbol, Hash] :rel_length A Range, a Fixnum, a Hash or a Symbol to indicate the variable-length/fixed-length
|
33
|
+
# qualifier of the relationship. See http://neo4jrb.readthedocs.org/en/latest/Querying.html#variable-length-relationships.
|
34
|
+
# @option options [Neo4j::Session] :session The session to be used for this query
|
35
|
+
# @option options [Neo4j::ActiveNode] :source_object The node instance at the start of the QueryProxy chain
|
36
|
+
# @option options [QueryProxy] :query_proxy An existing QueryProxy chain upon which this new object should be built
|
34
37
|
#
|
35
38
|
# QueryProxy objects are evaluated lazily.
|
36
39
|
def initialize(model, association = nil, options = {})
|
@@ -51,7 +54,7 @@ module Neo4j
|
|
51
54
|
end
|
52
55
|
|
53
56
|
def inspect
|
54
|
-
"
|
57
|
+
"#<QueryProxy #{@context} CYPHER: #{self.to_cypher.inspect}>"
|
55
58
|
end
|
56
59
|
|
57
60
|
attr_reader :start_object, :query_proxy
|
@@ -84,6 +87,9 @@ module Neo4j
|
|
84
87
|
# Build a Neo4j::Core::Query object for the QueryProxy. This is necessary when you want to take an existing QueryProxy chain
|
85
88
|
# and work with it from the more powerful (but less friendly) Neo4j::Core::Query.
|
86
89
|
# @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.
|
90
|
+
#
|
91
|
+
# .. code-block:: ruby
|
92
|
+
#
|
87
93
|
# student.lessons.query_as(:l).with('your cypher here...')
|
88
94
|
def query_as(var, with_labels = true)
|
89
95
|
result_query = @chain.inject(base_query(var, with_labels).params(@params)) do |query, link|
|
@@ -115,6 +121,8 @@ module Neo4j
|
|
115
121
|
|
116
122
|
# Scope all queries to the current scope.
|
117
123
|
#
|
124
|
+
# .. code-block:: ruby
|
125
|
+
#
|
118
126
|
# Comment.where(post_id: 1).scoping do
|
119
127
|
# Comment.first
|
120
128
|
# end
|
@@ -141,8 +149,12 @@ module Neo4j
|
|
141
149
|
alias_method :order_by, :order
|
142
150
|
|
143
151
|
# Cypher string for the QueryProxy's query. This will not include params. For the full output, see <tt>to_cypher_with_params</tt>.
|
144
|
-
def to_cypher
|
145
|
-
query.to_cypher
|
152
|
+
def to_cypher(*args)
|
153
|
+
query.to_cypher(*args)
|
154
|
+
end
|
155
|
+
|
156
|
+
def print_cypher
|
157
|
+
query.print_cypher
|
146
158
|
end
|
147
159
|
|
148
160
|
# Returns a string of the cypher query with return objects and params
|
@@ -155,8 +167,7 @@ module Neo4j
|
|
155
167
|
|
156
168
|
# To add a relationship for the node for the association on this QueryProxy
|
157
169
|
def <<(other_node)
|
158
|
-
create(other_node, {})
|
159
|
-
|
170
|
+
@start_object._persisted_obj ? create(other_node, {}) : defer_create(other_node, {}, :<<)
|
160
171
|
self
|
161
172
|
end
|
162
173
|
|
@@ -278,7 +289,7 @@ module Neo4j
|
|
278
289
|
end
|
279
290
|
|
280
291
|
def _association_arrow(properties = {}, create = false)
|
281
|
-
@association && @association.arrow_cypher(@rel_var, properties, create)
|
292
|
+
@association && @association.arrow_cypher(@rel_var, properties, create, false, @rel_length)
|
282
293
|
end
|
283
294
|
|
284
295
|
def _chain_level
|
@@ -312,8 +323,13 @@ module Neo4j
|
|
312
323
|
private
|
313
324
|
|
314
325
|
def instance_vars_from_options!(options)
|
315
|
-
@node_var, @session, @source_object, @starting_query, @optional,
|
316
|
-
|
326
|
+
@node_var, @session, @source_object, @starting_query, @optional,
|
327
|
+
@start_object, @query_proxy, @chain_level, @association_labels,
|
328
|
+
@rel_length = options.values_at(:node, :session, :source_object,
|
329
|
+
:starting_query, :optional,
|
330
|
+
:start_object, :query_proxy,
|
331
|
+
:chain_level, :association_labels,
|
332
|
+
:rel_length)
|
317
333
|
end
|
318
334
|
|
319
335
|
def build_deeper_query_proxy(method, args)
|