neo4j 4.0.0.rc.1 → 4.0.0.rc.3
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 +13 -0
- data/lib/neo4j.rb +1 -0
- data/lib/neo4j/active_node/has_n.rb +3 -6
- data/lib/neo4j/active_node/query/query_proxy.rb +39 -18
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +63 -15
- data/lib/neo4j/core/query.rb +19 -0
- data/lib/neo4j/shared/serialized_properties.rb +4 -0
- data/lib/neo4j/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28fe8a8c4370fbe08721a8ebd9f88665439a71f2
|
4
|
+
data.tar.gz: 802a04febb895b8a561e4c82b9324b159c4eb68a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37cbe5595a4991d3f13f44411c71e2c2ec4e3587489cdaec51f639668d94e3819dae84d4fc1213573fb76bac3e6f592ca3919617ea5fa1b9be840b767bf2fd41
|
7
|
+
data.tar.gz: 61781492746a92e307d2bc5769ce7d0db389c06a16864c750cec5981067594fc553d0cd0330984b9fd009b76817380cf9f6cd4224334f4c8b60572472d67b241
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
== 4.0.0.rc.3
|
2
|
+
Released minutes after rc.2 to catch one late addition!
|
3
|
+
* Adds serialization support for QueryProxy.
|
4
|
+
|
5
|
+
== 4.0.0.rc.2
|
6
|
+
This release builds on features introduced in the first RC. We are releasing this as another RC because the API may be tweaked before release.
|
7
|
+
* New `proxy_as` for Core::Query to build QueryProxy chains onto Core::Query objects!
|
8
|
+
* Using `proxy_as`, new `optional` method in QueryProxy to use the `OPTIONAL MATCH` Cypher function.
|
9
|
+
* `match_to` and methods that depend on it now support arrays of nodes or IDs.
|
10
|
+
* New `rels_to`/`all_rels_to` methods.
|
11
|
+
* New `delete` and `destroy` methods in QueryProxy to easily remove relationships.
|
12
|
+
* Serialized objects will include IDs by default.
|
13
|
+
|
1
14
|
== 4.0.0.rc.1
|
2
15
|
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
16
|
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).
|
data/lib/neo4j.rb
CHANGED
@@ -119,7 +119,7 @@ module HasN
|
|
119
119
|
|
120
120
|
instance_eval(%Q{
|
121
121
|
def #{name}(node = nil, rel = nil, proxy_obj = nil)
|
122
|
-
query_proxy = proxy_obj || Neo4j::ActiveNode::Query::QueryProxy.new(#{self.name}, nil, {
|
122
|
+
query_proxy = proxy_obj || Neo4j::ActiveNode::Query::QueryProxy.new(#{self.name}, nil, {
|
123
123
|
session: self.neo4j_session, query_proxy: nil, context: '#{self.name}' + '##{name}'
|
124
124
|
})
|
125
125
|
context = (query_proxy && query_proxy.context ? query_proxy.context : '#{self.name}') + '##{name}'
|
@@ -131,6 +131,7 @@ module HasN
|
|
131
131
|
node: node,
|
132
132
|
rel: rel,
|
133
133
|
context: context,
|
134
|
+
optional: query_proxy.optional?,
|
134
135
|
caller: query_proxy.caller
|
135
136
|
})
|
136
137
|
end}, __FILE__, __LINE__)
|
@@ -167,11 +168,7 @@ module HasN
|
|
167
168
|
result = #{name}_query_proxy(node: node, rel: rel, context: '#{self.name}##{name}')
|
168
169
|
association = self.class.reflect_on_association(__method__)
|
169
170
|
query_return = association_instance_get(result.to_cypher_with_params, association)
|
170
|
-
|
171
|
-
association_instance_set(result.to_cypher_with_params, result.first, association)
|
172
|
-
else
|
173
|
-
query_return
|
174
|
-
end
|
171
|
+
query_return || association_instance_set(result.to_cypher_with_params, result.first, association)
|
175
172
|
end}, __FILE__, __LINE__)
|
176
173
|
|
177
174
|
instance_eval(%Q{
|
@@ -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, :association, :model
|
12
|
+
attr_reader :caller, :association, :model, :starting_query
|
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.
|
@@ -42,13 +42,15 @@ module Neo4j
|
|
42
42
|
@session = options[:session]
|
43
43
|
@caller = options[:caller]
|
44
44
|
@chain = []
|
45
|
+
@starting_query = options[:starting_query]
|
46
|
+
@optional = options[:optional]
|
45
47
|
@params = options[:query_proxy] ? options[:query_proxy].instance_variable_get('@params') : {}
|
46
48
|
end
|
47
49
|
|
48
50
|
# The current node identifier on deck, so to speak. It is the object that will be returned by calling `each` and the last node link
|
49
51
|
# in the QueryProxy chain.
|
50
52
|
def identity
|
51
|
-
@node_var ||
|
53
|
+
@node_var || _result_string
|
52
54
|
end
|
53
55
|
alias_method :node_identity, :identity
|
54
56
|
|
@@ -142,20 +144,18 @@ module Neo4j
|
|
142
144
|
# student.lessons.query_as(:l).with('your cypher here...')
|
143
145
|
def query_as(var)
|
144
146
|
query = if @association
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
147
|
+
chain_var = _association_chain_var
|
148
|
+
label_string = @model && ":`#{@model.mapped_label_name}`"
|
149
|
+
(_association_query_start(chain_var) & _query_model_as(var)).send(_match_type, "#{chain_var}#{_association_arrow}(#{var}#{label_string})")
|
150
|
+
else
|
151
|
+
starting_query ? (starting_query & _query_model_as(var)) : _query_model_as(var)
|
152
|
+
end
|
152
153
|
# Build a query chain via the chain, return the result
|
153
154
|
@chain.inject(query.params(@params)) do |query, (method, arg)|
|
154
155
|
query.send(method, arg.respond_to?(:call) ? arg.call(var) : arg)
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
158
|
-
|
159
159
|
# Scope all queries to the current scope.
|
160
160
|
#
|
161
161
|
# Comment.where(post_id: 1).scoping do
|
@@ -182,7 +182,7 @@ module Neo4j
|
|
182
182
|
# Returns a string of the cypher query with return objects and params
|
183
183
|
# @param [Array] columns array containing symbols of identifiers used in the query
|
184
184
|
# @return [String]
|
185
|
-
def to_cypher_with_params(columns = [
|
185
|
+
def to_cypher_with_params(columns = [self.identity])
|
186
186
|
final_query = query.return_query(columns)
|
187
187
|
"#{final_query.to_cypher} | params: #{final_query.send(:merge_params)}"
|
188
188
|
end
|
@@ -232,6 +232,10 @@ module Neo4j
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
def read_attribute_for_serialization(*args)
|
236
|
+
to_a.map {|o| o.read_attribute_for_serialization(*args) }
|
237
|
+
end
|
238
|
+
|
235
239
|
# QueryProxy objects act as a representation of a model at the class level so we pass through calls
|
236
240
|
# This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
|
237
241
|
def method_missing(method_name, *args, &block)
|
@@ -243,6 +247,10 @@ module Neo4j
|
|
243
247
|
end
|
244
248
|
end
|
245
249
|
|
250
|
+
def optional?
|
251
|
+
@optional == true
|
252
|
+
end
|
253
|
+
|
246
254
|
attr_reader :context
|
247
255
|
attr_reader :node_var
|
248
256
|
|
@@ -259,12 +267,23 @@ module Neo4j
|
|
259
267
|
|
260
268
|
def _query_model_as(var)
|
261
269
|
match_arg = if @model
|
262
|
-
|
263
|
-
|
270
|
+
label = @model.respond_to?(:mapped_label_name) ? @model.mapped_label_name : @model
|
271
|
+
{ var => label }
|
272
|
+
else
|
273
|
+
var
|
274
|
+
end
|
275
|
+
_session.query(context: @context).send(_match_type, match_arg)
|
276
|
+
end
|
277
|
+
|
278
|
+
# TODO: Refactor this. Too much happening here.
|
279
|
+
def _result_string
|
280
|
+
if self.association
|
281
|
+
"result_#{self.association.name}".to_sym
|
282
|
+
elsif self.model
|
283
|
+
"result_#{self.model.name.tr!(':', '')}".to_sym
|
264
284
|
else
|
265
|
-
|
285
|
+
:result
|
266
286
|
end
|
267
|
-
_session.query(context: @context).match(match_arg)
|
268
287
|
end
|
269
288
|
|
270
289
|
def _session
|
@@ -276,9 +295,7 @@ module Neo4j
|
|
276
295
|
end
|
277
296
|
|
278
297
|
def _chain_level
|
279
|
-
if @options[:
|
280
|
-
1
|
281
|
-
elsif query_proxy = @options[:query_proxy]
|
298
|
+
if query_proxy = @options[:query_proxy]
|
282
299
|
query_proxy._chain_level + 1
|
283
300
|
else
|
284
301
|
1
|
@@ -309,6 +326,10 @@ module Neo4j
|
|
309
326
|
:"rel#{_chain_level - 1}"
|
310
327
|
end
|
311
328
|
|
329
|
+
def _match_type
|
330
|
+
@optional ? :optional_match : :match
|
331
|
+
end
|
332
|
+
|
312
333
|
attr_writer :context
|
313
334
|
|
314
335
|
private
|
@@ -59,7 +59,7 @@ module Neo4j
|
|
59
59
|
rescue Neo4j::Session::CypherError
|
60
60
|
self.query.delete(target).exec
|
61
61
|
end
|
62
|
-
|
62
|
+
clear_caller_cache
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -68,36 +68,84 @@ module Neo4j
|
|
68
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
69
|
# primary key of that model. When nil, it uses `1 = 2` to prevent matching all records, which is the default
|
70
70
|
# behavior when nil is passed to `where` in QueryProxy.
|
71
|
+
# @param [#neo_id, String, Enumerable] node A node, a string representing a node's ID, or an enumerable of nodes or IDs.
|
71
72
|
# @return [Neo4j::ActiveNode::Query::QueryProxy] A QueryProxy object upon which you can build.
|
72
73
|
def match_to(node)
|
73
|
-
if node.respond_to?(:neo_id)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
74
|
+
where_arg = if node.respond_to?(:neo_id)
|
75
|
+
{ neo_id: node.neo_id }
|
76
|
+
elsif !node.nil?
|
77
|
+
id_key = association_id_key
|
78
|
+
node = ids_array(node) if node.is_a?(Array)
|
79
|
+
{ id_key => node }
|
80
|
+
else
|
81
|
+
# support for null object pattern
|
82
|
+
'1 = 2'
|
83
|
+
end
|
84
|
+
self.where(where_arg)
|
82
85
|
end
|
83
86
|
|
84
87
|
# Gives you the first relationship between the last link of a QueryProxy chain and a given node
|
85
88
|
# Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r`
|
89
|
+
# @param [#neo_id, String, Enumerable] node An object to be sent to `match_to`. See params for that method.
|
90
|
+
# @return A relationship (ActiveRel, CypherRelationship, EmbeddedRelationship) or nil.
|
86
91
|
def first_rel_to(node)
|
87
92
|
self.match_to(node).limit(1).pluck(rel_identity).first
|
88
93
|
end
|
89
94
|
|
95
|
+
# Returns all relationships across a QueryProxy chain between a given node or array of nodes and the preceeding link.
|
96
|
+
# @param [#neo_id, String, Enumerable] node An object to be sent to `match_to`. See params for that method.
|
97
|
+
# @return An enumerable of relationship objects.
|
98
|
+
def rels_to(node)
|
99
|
+
self.match_to(node).pluck(rel_identity)
|
100
|
+
end
|
101
|
+
alias_method :all_rels_to, :rels_to
|
102
|
+
|
103
|
+
# Deletes the relationship between a node and its last link in the QueryProxy chain. Executed in the database, callbacks will not run.
|
104
|
+
def delete(node)
|
105
|
+
self.match_to(node).query.delete(rel_identity).exec
|
106
|
+
clear_caller_cache
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns all relationships between a node and its last link in the QueryProxy chain, destroys them in Ruby. Callbacks will be run.
|
110
|
+
def destroy(node)
|
111
|
+
self.rels_to(node).map!(&:destroy)
|
112
|
+
clear_caller_cache
|
113
|
+
end
|
114
|
+
|
115
|
+
# A shortcut for attaching a new, optional match to the end of a QueryProxy chain.
|
116
|
+
# TODO: It's silly that we have to call constantize here. There should be a better way of finding the target class of the destination.
|
117
|
+
def optional(association, node_id = nil)
|
118
|
+
target_qp = self.send(association)
|
119
|
+
model = target_qp.name.constantize
|
120
|
+
var = node_id || target_qp.identity
|
121
|
+
self.query.proxy_as(model, var, true)
|
122
|
+
end
|
123
|
+
|
90
124
|
private
|
91
125
|
|
92
|
-
def
|
93
|
-
|
126
|
+
def clear_caller_cache
|
127
|
+
self.caller.clear_association_cache if self.caller.respond_to?(:clear_association_cache)
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [String] The primary key of a the current QueryProxy's model or target class
|
131
|
+
def association_id_key
|
132
|
+
self.association.nil? ? model.primary_key : self.association.target_class.primary_key
|
133
|
+
end
|
134
|
+
|
135
|
+
# @param [Enumerable] node An enumerable of nodes or ids.
|
136
|
+
# @return [Array] An array after having `id` called on each object
|
137
|
+
def ids_array(node)
|
138
|
+
node.first.respond_to?(:id) ? node.map!(&:id) : node
|
139
|
+
end
|
140
|
+
|
141
|
+
def query_with_target(target)
|
142
|
+
yield(target || identity)
|
94
143
|
end
|
95
144
|
|
96
145
|
def exists_query_start(origin, condition, target)
|
97
|
-
|
98
|
-
when condition.class == Fixnum
|
146
|
+
if condition.class == Fixnum
|
99
147
|
self.where("ID(#{target}) = {exists_condition}").params(exists_condition: condition)
|
100
|
-
|
148
|
+
elsif condition.class == Hash
|
101
149
|
self.where(condition.keys.first => condition.values.first)
|
102
150
|
else
|
103
151
|
self
|
@@ -0,0 +1,19 @@
|
|
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, { starting_query: self.break, node: var, optional: optional })
|
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
|
+
end
|
19
|
+
end
|
data/lib/neo4j/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neo4j
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0.rc.
|
4
|
+
version: 4.0.0.rc.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Ronge
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: orm_adapter
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- lib/neo4j/active_rel/types.rb
|
144
144
|
- lib/neo4j/active_rel/validations.rb
|
145
145
|
- lib/neo4j/config.rb
|
146
|
+
- lib/neo4j/core/query.rb
|
146
147
|
- lib/neo4j/migration.rb
|
147
148
|
- lib/neo4j/paginated.rb
|
148
149
|
- lib/neo4j/railtie.rb
|