neo4j 3.0.0.rc.2 → 3.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 +5 -0
- data/Gemfile +2 -1
- data/README.md +5 -6
- data/lib/neo4j.rb +3 -0
- data/lib/neo4j/active_node.rb +1 -0
- data/lib/neo4j/active_node/has_n.rb +9 -6
- data/lib/neo4j/active_node/initialize.rb +1 -2
- data/lib/neo4j/active_node/labels.rb +2 -35
- data/lib/neo4j/active_node/persistence.rb +2 -0
- data/lib/neo4j/active_node/property.rb +7 -3
- data/lib/neo4j/active_node/query.rb +2 -2
- data/lib/neo4j/active_node/query/query_proxy.rb +11 -44
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +60 -0
- data/lib/neo4j/active_node/query_methods.rb +47 -0
- data/lib/neo4j/active_node/scope.rb +87 -0
- data/lib/neo4j/active_rel/initialize.rb +1 -1
- data/lib/neo4j/active_rel/persistence.rb +2 -3
- data/lib/neo4j/active_rel/property.rb +8 -4
- data/lib/neo4j/shared/property.rb +5 -2
- data/lib/neo4j/version.rb +1 -1
- data/neo4j.gemspec +3 -6
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2a5f927c44fc68a95721d7feca406c21a6f71d8
|
4
|
+
data.tar.gz: e53647f9c20b8d48040bcab9bc28ce300287f5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 542621a0655dbae24993c9a36c5e39a37930c08fc023acb16bb907622bcd953f0a02662a4750174f851e6144e4fef62b9c4abc4326f3a954b93c4811e13402d7
|
7
|
+
data.tar.gz: 0ebd9085b56e5a50a01f0d01994dff9f2219f1744f5e6e053717c12d787243c3d3e94a60dcd7b9623f24c08f3ba886acd454e8551518f22b083d19b387ef2738
|
data/CHANGELOG
CHANGED
data/Gemfile
CHANGED
@@ -6,7 +6,7 @@ gemspec
|
|
6
6
|
#gem 'neo4j-core', :git => 'https://github.com/andreasronge/neo4j-core.git'
|
7
7
|
#gem 'orm_adapter', :path => '../orm_adapter'
|
8
8
|
|
9
|
-
gem 'coveralls', require: false
|
9
|
+
#gem 'coveralls', require: false
|
10
10
|
|
11
11
|
group 'development' do
|
12
12
|
gem 'pry'
|
@@ -16,6 +16,7 @@ group 'development' do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
group 'test' do
|
19
|
+
gem "codeclimate-test-reporter", require: nil
|
19
20
|
gem "rspec", '~> 2.0'
|
20
21
|
# gem 'rspec-its' instead of its in rspec 3
|
21
22
|
gem "its"
|
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
# Welcome to Neo4j.rb [](http://travis-ci.org/neo4jrb/neo4j) [](https://codeclimate.com/github/neo4jrb/neo4j) [](https://codeclimate.com/github/andreasronge/neo4j)
|
2
2
|
|
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/
|
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
|
|
5
5
|
## Documentation version 3.0.0.rc
|
6
6
|
|
7
|
-
* [Wiki](https://github.com/
|
7
|
+
* [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v3-Introduction)
|
8
8
|
|
9
9
|
## Documentation Old stable version 2.x
|
10
10
|
|
11
|
-
* [README](https://github.com/
|
12
|
-
* [Wiki](https://github.com/
|
11
|
+
* [README](https://github.com/neo4jrb/neo4j/tree/2.x)
|
12
|
+
* [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3ARails-Introduction)
|
13
13
|
|
14
14
|
## Support
|
15
15
|
|
@@ -31,7 +31,6 @@ Pull request with high test coverage and good [code climate](https://codeclimate
|
|
31
31
|
## License
|
32
32
|
|
33
33
|
* Neo4j.rb - MIT, see the [LICENSE](http://github.com/andreasronge/neo4j/tree/master/LICENSE).
|
34
|
-
* Lucene - Apache, see the [Lucene Documentation](http://lucene.apache.org/java/docs/features.html).
|
35
34
|
* Neo4j - Dual free software/commercial license, see [Licensing Guide](http://www.neo4j.org/learn/licensing).
|
36
35
|
|
37
36
|
**Notice:** There are different licenses for the `neo4j-community`, `neo4j-advanced`, and `neo4j-enterprise` jar gems. Only the `neo4j-community` gem is required by default.
|
data/lib/neo4j.rb
CHANGED
@@ -38,6 +38,8 @@ require 'neo4j/active_rel/query'
|
|
38
38
|
require 'neo4j/active_rel/related_node'
|
39
39
|
require 'neo4j/active_rel'
|
40
40
|
|
41
|
+
require 'neo4j/active_node/query_methods'
|
42
|
+
require 'neo4j/active_node/query/query_proxy_methods'
|
41
43
|
require 'neo4j/active_node/labels'
|
42
44
|
require 'neo4j/active_node/id_property'
|
43
45
|
require 'neo4j/active_node/callbacks'
|
@@ -51,6 +53,7 @@ require 'neo4j/active_node/has_n/association'
|
|
51
53
|
require 'neo4j/active_node/query/query_proxy'
|
52
54
|
require 'neo4j/active_node/query'
|
53
55
|
require 'neo4j/active_node/serialized_properties'
|
56
|
+
require 'neo4j/active_node/scope'
|
54
57
|
require 'neo4j/active_node'
|
55
58
|
|
56
59
|
require 'neo4j/active_node/orm_adapter'
|
data/lib/neo4j/active_node.rb
CHANGED
@@ -37,6 +37,7 @@ module Neo4j
|
|
37
37
|
include Neo4j::ActiveNode::Labels
|
38
38
|
include Neo4j::ActiveNode::Rels
|
39
39
|
include Neo4j::ActiveNode::HasN
|
40
|
+
include Neo4j::ActiveNode::Scope
|
40
41
|
|
41
42
|
def neo4j_obj
|
42
43
|
_persisted_obj || raise("Tried to access native neo4j object on a non persisted object")
|
@@ -59,13 +59,16 @@ module HasN
|
|
59
59
|
end}, __FILE__, __LINE__)
|
60
60
|
|
61
61
|
instance_eval(%Q{
|
62
|
-
def #{name}(node = nil, rel = nil)
|
63
|
-
|
62
|
+
def #{name}(node = nil, rel = nil, proxy_obj = nil)
|
63
|
+
query_proxy = proxy_obj || Neo4j::ActiveNode::Query::QueryProxy.new(#{self.name}, nil, {
|
64
|
+
session: self.neo4j_session, query_proxy: nil, context: '#{self.name}' + '##{name}'
|
65
|
+
})
|
66
|
+
context = (query_proxy && query_proxy.context ? query_proxy.context : '#{self.name}') + '##{name}'
|
64
67
|
Neo4j::ActiveNode::Query::QueryProxy.new(#{target_class_name},
|
65
68
|
@associations[#{name.inspect}],
|
66
69
|
{
|
67
70
|
session: self.neo4j_session,
|
68
|
-
query_proxy:
|
71
|
+
query_proxy: query_proxy,
|
69
72
|
node: node,
|
70
73
|
rel: rel,
|
71
74
|
context: context
|
@@ -110,9 +113,9 @@ module HasN
|
|
110
113
|
{session: self.neo4j_session}.merge(options))
|
111
114
|
end
|
112
115
|
|
113
|
-
def #{name}(node = nil, rel = nil)
|
114
|
-
context = (
|
115
|
-
#{name}_query_proxy(query_proxy:
|
116
|
+
def #{name}(node = nil, rel = nil, query_proxy = nil)
|
117
|
+
context = (query_proxy && query_proxy.context ? query_proxy.context : '#{self.name}') + '##{name}'
|
118
|
+
#{name}_query_proxy(query_proxy: query_proxy, node: node, rel: rel, context: context)
|
116
119
|
end}, __FILE__, __LINE__)
|
117
120
|
end
|
118
121
|
|
@@ -2,12 +2,11 @@ module Neo4j::ActiveNode::Initialize
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
include Neo4j::TypeConverters
|
4
4
|
|
5
|
-
attr_reader :_persisted_obj
|
6
|
-
|
7
5
|
# called when loading the node from the database
|
8
6
|
# @param [Neo4j::Node] persisted_node the node this class wraps
|
9
7
|
# @param [Hash] properties of the persisted node.
|
10
8
|
def init_on_load(persisted_node, properties)
|
9
|
+
@_association_attributes = self.class.extract_association_attributes!(properties)
|
11
10
|
@_persisted_obj = persisted_node
|
12
11
|
changed_attributes && changed_attributes.clear
|
13
12
|
@attributes = attributes.merge(properties.stringify_keys)
|
@@ -9,7 +9,6 @@ module Neo4j
|
|
9
9
|
WRAPPED_CLASSES = []
|
10
10
|
class InvalidQueryError < StandardError; end
|
11
11
|
class RecordNotFound < StandardError; end
|
12
|
-
class InvalidParameterError < StandardError; end
|
13
12
|
|
14
13
|
# @return the labels
|
15
14
|
# @see Neo4j-core
|
@@ -60,46 +59,14 @@ module Neo4j
|
|
60
59
|
end
|
61
60
|
|
62
61
|
module ClassMethods
|
62
|
+
include Neo4j::ActiveNode::QueryMethods
|
63
|
+
|
63
64
|
# Find all nodes/objects of this class
|
64
65
|
def all
|
65
66
|
self.query_as(:n).pluck(:n)
|
66
67
|
end
|
67
68
|
|
68
|
-
# Returns the first node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
69
|
-
def first
|
70
|
-
self.query_as(:n).limit(1).order('ID(n)').pluck(:n).first
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
74
|
-
def last
|
75
|
-
self.query_as(:n).limit(1).order('ID(n) DESC').pluck(:n).first
|
76
|
-
end
|
77
|
-
|
78
|
-
# @return [Fixnum] number of nodes of this class
|
79
|
-
def count(distinct = nil)
|
80
|
-
raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
|
81
|
-
q = distinct.nil? ? "n" : "DISTINCT n"
|
82
|
-
self.query_as(:n).return("count(#{q}) AS count").first.count
|
83
|
-
end
|
84
|
-
alias_method :size, :count
|
85
|
-
alias_method :length, :count
|
86
69
|
|
87
|
-
def empty?
|
88
|
-
!self.exists?
|
89
|
-
end
|
90
|
-
alias_method :blank?, :empty?
|
91
|
-
|
92
|
-
def include?(other)
|
93
|
-
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
94
|
-
self.query_as(:n).where("ID(n) = #{other.neo_id}").return("count(n) AS count").first.count > 0
|
95
|
-
end
|
96
|
-
|
97
|
-
def exists?(node_id=nil)
|
98
|
-
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless node_id.is_a?(Integer) || node_id.nil?
|
99
|
-
start_q = self.query_as(:n)
|
100
|
-
end_q = node_id.nil? ? start_q : start_q.where("ID(n) = #{node_id}")
|
101
|
-
end_q.return("COUNT(n) AS count").first.count > 0
|
102
|
-
end
|
103
70
|
|
104
71
|
# Returns the object with the specified neo4j id.
|
105
72
|
# @param [String,Fixnum] id of node to find
|
@@ -13,6 +13,8 @@ module Neo4j::ActiveNode
|
|
13
13
|
properties = convert_properties_to :db, props
|
14
14
|
node = _create_node(properties)
|
15
15
|
init_on_load(node, node.props)
|
16
|
+
send_props(@relationship_props) if @relationship_props
|
17
|
+
@relationship_props = nil
|
16
18
|
# Neo4j::IdentityMap.add(node, self)
|
17
19
|
# write_changed_relationships
|
18
20
|
true
|
@@ -3,15 +3,19 @@ module Neo4j::ActiveNode
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
include Neo4j::Shared::Property
|
5
5
|
|
6
|
+
def initialize(attributes={}, options={})
|
7
|
+
super(attributes, options)
|
8
|
+
|
9
|
+
send_props(@relationship_props) if persisted? and not @relationship_props.nil?
|
10
|
+
end
|
11
|
+
|
6
12
|
module ClassMethods
|
7
13
|
|
8
14
|
# Extracts keys from attributes hash which are relationships of the model
|
9
15
|
# TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
|
10
16
|
def extract_association_attributes!(attributes)
|
11
|
-
attributes.keys.
|
17
|
+
attributes.keys.each_with_object({}) do |key, association_props|
|
12
18
|
association_props[key] = attributes.delete(key) if self.has_association?(key)
|
13
|
-
|
14
|
-
association_props
|
15
19
|
end
|
16
20
|
end
|
17
21
|
end
|
@@ -23,7 +23,7 @@ module Neo4j
|
|
23
23
|
module ClassMethods
|
24
24
|
include Enumerable
|
25
25
|
|
26
|
-
attr_writer :query_proxy
|
26
|
+
#attr_writer :query_proxy
|
27
27
|
|
28
28
|
def each
|
29
29
|
self.query_as(:n).pluck(:n).each {|o| yield o }
|
@@ -49,7 +49,7 @@ module Neo4j
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def query_proxy(options = {})
|
52
|
-
|
52
|
+
Neo4j::ActiveNode::Query::QueryProxy.new(self, nil, options)
|
53
53
|
end
|
54
54
|
|
55
55
|
def as(node_var)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Neo4j
|
2
2
|
module ActiveNode
|
3
3
|
module Query
|
4
|
-
class InvalidParameterError < StandardError; end
|
5
4
|
class QueryProxy
|
6
5
|
|
7
6
|
include Enumerable
|
7
|
+
include Neo4j::ActiveNode::Query::QueryProxyMethods
|
8
8
|
|
9
9
|
def initialize(model, association = nil, options = {})
|
10
10
|
@model = model
|
@@ -18,13 +18,17 @@ module Neo4j
|
|
18
18
|
@params = options[:query_proxy] ? options[:query_proxy].instance_variable_get('@params') : {}
|
19
19
|
end
|
20
20
|
|
21
|
+
def identity
|
22
|
+
@node_var || :result
|
23
|
+
end
|
24
|
+
|
21
25
|
def each(node = true, rel = nil, &block)
|
22
26
|
if node && rel
|
23
|
-
self.pluck(
|
27
|
+
self.pluck(identity, @rel_var).each do |obj, rel|
|
24
28
|
yield obj, rel
|
25
29
|
end
|
26
30
|
else
|
27
|
-
pluck_this = !rel ?
|
31
|
+
pluck_this = !rel ? identity : @rel_var
|
28
32
|
self.pluck(pluck_this).each do |obj|
|
29
33
|
yield obj
|
30
34
|
end
|
@@ -68,13 +72,12 @@ module Neo4j
|
|
68
72
|
|
69
73
|
# Like calling #query_as, but for when you don't care about the variable name
|
70
74
|
def query
|
71
|
-
query_as(
|
75
|
+
query_as(identity)
|
72
76
|
end
|
73
77
|
|
74
78
|
# Build a Neo4j::Core::Query object for the QueryProxy
|
75
79
|
def query_as(var)
|
76
80
|
var = @node_var if @node_var
|
77
|
-
|
78
81
|
query = if @association
|
79
82
|
chain_var = _association_chain_var
|
80
83
|
label_string = @model && ":`#{@model.mapped_label_name}`"
|
@@ -111,14 +114,14 @@ module Neo4j
|
|
111
114
|
raise "Can only create associations on associations" unless @association
|
112
115
|
other_nodes = [other_nodes].flatten
|
113
116
|
|
114
|
-
other_nodes.map
|
117
|
+
other_nodes = other_nodes.map do |other_node|
|
115
118
|
case other_node
|
116
119
|
when Integer, String
|
117
120
|
@model.find(other_node)
|
118
121
|
else
|
119
122
|
other_node
|
120
123
|
end
|
121
|
-
end
|
124
|
+
end.compact
|
122
125
|
|
123
126
|
raise ArgumentError, "Node must be of the association's class when model is specified" if @model && other_nodes.any? {|other_node| other_node.class != @model }
|
124
127
|
other_nodes.each do |other_node|
|
@@ -137,41 +140,6 @@ module Neo4j
|
|
137
140
|
end
|
138
141
|
end
|
139
142
|
|
140
|
-
#TODO: Get these out of here
|
141
|
-
def first
|
142
|
-
self.query_as(:n).limit(1).order('ID(n)').pluck(:n).first
|
143
|
-
end
|
144
|
-
|
145
|
-
def last
|
146
|
-
self.query_as(:n).limit(1).order('ID(n) DESC').pluck(:n).first
|
147
|
-
end
|
148
|
-
|
149
|
-
# @return [Fixnum] number of nodes of this class
|
150
|
-
def count(distinct = nil)
|
151
|
-
raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
|
152
|
-
q = distinct.nil? ? "n" : "DISTINCT n"
|
153
|
-
self.query_as(:n).return("count(#{q}) AS count").first.count
|
154
|
-
end
|
155
|
-
alias_method :size, :count
|
156
|
-
alias_method :length, :count
|
157
|
-
|
158
|
-
def empty?
|
159
|
-
!self.exists?
|
160
|
-
end
|
161
|
-
alias_method :blank?, :empty?
|
162
|
-
|
163
|
-
def include?(other)
|
164
|
-
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
165
|
-
self.query_as(:n).where("ID(n) = #{other.neo_id}").return("count(n) AS count").first.count > 0
|
166
|
-
end
|
167
|
-
|
168
|
-
def exists?(node_id=nil)
|
169
|
-
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless node_id.is_a?(Integer) || node_id.nil?
|
170
|
-
start_q = self.query_as(:n)
|
171
|
-
end_q = node_id.nil? ? start_q : start_q.where("ID(n) = #{node_id}")
|
172
|
-
end_q.return("COUNT(n) AS count").first.count > 0
|
173
|
-
end
|
174
|
-
|
175
143
|
# QueryProxy objects act as a representation of a model at the class level so we pass through calls
|
176
144
|
# This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing
|
177
145
|
def method_missing(method_name, *args)
|
@@ -254,9 +222,8 @@ module Neo4j
|
|
254
222
|
private
|
255
223
|
|
256
224
|
def call_class_method(method_name, *args)
|
257
|
-
|
225
|
+
args[2] = self
|
258
226
|
result = @model.send(method_name, *args)
|
259
|
-
@model.query_proxy = nil
|
260
227
|
result
|
261
228
|
end
|
262
229
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
module Query
|
4
|
+
module QueryProxyMethods
|
5
|
+
class InvalidParameterError < StandardError; end
|
6
|
+
|
7
|
+
def query_with_target(target, &block)
|
8
|
+
target = target.nil? ? identity : target
|
9
|
+
block.yield(target)
|
10
|
+
end
|
11
|
+
|
12
|
+
def first(target=nil)
|
13
|
+
query_with_target(target) { |target| first_and_last("ID(#{target})", target) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def last(target=nil)
|
17
|
+
query_with_target(target) { |target| first_and_last("ID(#{target}) DESC", target) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def first_and_last(order, target)
|
21
|
+
self.order(order).limit(1).pluck(target).first
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Fixnum] number of nodes of this class
|
25
|
+
def count(distinct=nil, target=nil)
|
26
|
+
raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
|
27
|
+
query_with_target(target) do |target|
|
28
|
+
q = distinct.nil? ? target : "DISTINCT #{target}"
|
29
|
+
self.query.return("count(#{q}) AS count").first.count
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :size, :count
|
34
|
+
alias_method :length, :count
|
35
|
+
|
36
|
+
def empty?(target=nil)
|
37
|
+
query_with_target(target) { |target| !self.exists?(nil, target) }
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :blank?, :empty?
|
41
|
+
|
42
|
+
def include?(other, target=nil)
|
43
|
+
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
44
|
+
query_with_target(target) do |target|
|
45
|
+
self.where("ID(#{target}) = {other_node_id}").params(other_node_id: other.neo_id).query.return("count(#{target}) AS count").first.count > 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def exists?(node_id=nil, target=nil)
|
50
|
+
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless node_id.is_a?(Integer) || node_id.nil?
|
51
|
+
query_with_target(target) do |target|
|
52
|
+
start_q = self.query
|
53
|
+
end_q = node_id.nil? ? start_q : start_q.where("ID(#{target}) = #{node_id}")
|
54
|
+
end_q.return("COUNT(#{target}) AS count").first.count > 0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
module QueryMethods
|
4
|
+
class InvalidParameterError < StandardError; end
|
5
|
+
|
6
|
+
def exists?(node_id=nil)
|
7
|
+
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless node_id.is_a?(Integer) || node_id.nil?
|
8
|
+
start_q = self.query_as(:n)
|
9
|
+
end_q = node_id.nil? ? start_q : start_q.where("ID(n) = #{node_id}")
|
10
|
+
end_q.return("COUNT(n) AS count").first.count > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the first node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
14
|
+
def first
|
15
|
+
self.query_as(:n).limit(1).order('ID(n)').pluck(:n).first
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
19
|
+
def last
|
20
|
+
self.query_as(:n).limit(1).order('ID(n) DESC').pluck(:n).first
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Fixnum] number of nodes of this class
|
24
|
+
def count(distinct = nil)
|
25
|
+
raise(InvalidParameterError, ':count accepts `distinct` or nil as a parameter') unless distinct.nil? || distinct == :distinct
|
26
|
+
q = distinct.nil? ? "n" : "DISTINCT n"
|
27
|
+
self.query_as(:n).return("count(#{q}) AS count").first.count
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_method :size, :count
|
31
|
+
alias_method :length, :count
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
!self.exists?
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :blank?, :empty?
|
38
|
+
|
39
|
+
def include?(other)
|
40
|
+
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
41
|
+
self.query_as(:n).where("ID(n) = #{other.neo_id}").return("count(n) AS count").first.count > 0
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Neo4j::ActiveNode
|
2
|
+
module Scope
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
# Similar to ActiveRecord scope
|
8
|
+
#
|
9
|
+
# @example without argument
|
10
|
+
# class Person
|
11
|
+
# include Neo4j::ActiveNode
|
12
|
+
# property :name
|
13
|
+
# property :score
|
14
|
+
# has_many :out, :friends, model_class: self
|
15
|
+
# scope :top_students, -> { where(score: 42)}") }
|
16
|
+
# end
|
17
|
+
# Person.top_students.to_a
|
18
|
+
# a_person.friends.top_students.to_a
|
19
|
+
# a_person.friends.friends.top_students.to_a
|
20
|
+
# a_person.friends.top_students.friends.to_a
|
21
|
+
#
|
22
|
+
# @example Argument for scopes
|
23
|
+
# Person.scope :level, ->(num) { where(level_num: num)}
|
24
|
+
#
|
25
|
+
# @example Argument as a cypher identifier
|
26
|
+
# class Person
|
27
|
+
# include Neo4j::ActiveNode
|
28
|
+
# property :name
|
29
|
+
# property :score
|
30
|
+
# has_many :out, :friends, model_class: self
|
31
|
+
# scope :great_students, ->(identifier) { where("#{identifier}.score > 41") }
|
32
|
+
# end
|
33
|
+
# Person.as(:all_people).great_students(:all_people).to_a
|
34
|
+
#
|
35
|
+
# @see http://guides.rubyonrails.org/active_record_querying.html#scopes
|
36
|
+
def scope(name, proc)
|
37
|
+
_scope[name.to_sym] = proc
|
38
|
+
|
39
|
+
module_eval(%Q{
|
40
|
+
def #{name}(query_params=nil, _=nil, query_proxy=nil)
|
41
|
+
eval_context = ScopeEvalContext.new(self, query_proxy || self.class.query_proxy)
|
42
|
+
proc = self.class._scope[:"#{name}"]
|
43
|
+
self.class._call_scope_context(eval_context, query_params, proc)
|
44
|
+
end
|
45
|
+
}, __FILE__, __LINE__)
|
46
|
+
|
47
|
+
instance_eval(%Q{
|
48
|
+
def #{name}(query_params=nil, _=nil, query_proxy=nil)
|
49
|
+
eval_context = ScopeEvalContext.new(self, query_proxy || self.query_proxy)
|
50
|
+
proc = _scope[:"#{name}"]
|
51
|
+
_call_scope_context(eval_context, query_params, proc)
|
52
|
+
end
|
53
|
+
}, __FILE__, __LINE__)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
def _scope
|
59
|
+
@_scope ||= {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def _call_scope_context(eval_context, query_params, proc)
|
63
|
+
if proc.arity == 1
|
64
|
+
eval_context.instance_exec(query_params,&proc)
|
65
|
+
else
|
66
|
+
eval_context.instance_exec(&proc)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class ScopeEvalContext
|
74
|
+
def initialize(target, query_proxy)
|
75
|
+
@query_proxy = query_proxy
|
76
|
+
@target = target
|
77
|
+
end
|
78
|
+
|
79
|
+
Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
|
80
|
+
module_eval(%Q{
|
81
|
+
def #{method}(params={})
|
82
|
+
(@query_proxy || @target).#{method}(params)
|
83
|
+
end}, __FILE__, __LINE__)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -30,7 +30,7 @@ module Neo4j::ActiveRel
|
|
30
30
|
create_magic_properties
|
31
31
|
set_timestamps
|
32
32
|
properties = convert_properties_to :db, props
|
33
|
-
rel = _create_rel(properties)
|
33
|
+
rel = _create_rel(from_node, to_node, properties)
|
34
34
|
init_on_load(rel, to_node, from_node, @rel_type)
|
35
35
|
true
|
36
36
|
end
|
@@ -69,8 +69,7 @@ module Neo4j::ActiveRel
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
def _create_rel(*args)
|
73
|
-
session = self.class.neo4j_session
|
72
|
+
def _create_rel(from_node, to_node, *args)
|
74
73
|
props = self.class.default_property_values(self)
|
75
74
|
props.merge!(args[0]) if args[0].is_a?(Hash)
|
76
75
|
set_classname(props)
|
@@ -19,15 +19,19 @@ module Neo4j::ActiveRel
|
|
19
19
|
self.class._type
|
20
20
|
end
|
21
21
|
|
22
|
+
def initialize(attributes={}, options={})
|
23
|
+
super(attributes, options)
|
24
|
+
|
25
|
+
send_props(@relationship_props) unless @relationship_props.nil?
|
26
|
+
end
|
27
|
+
|
22
28
|
module ClassMethods
|
23
29
|
|
24
30
|
# Extracts keys from attributes hash which are relationships of the model
|
25
31
|
# TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
|
26
32
|
def extract_association_attributes!(attributes)
|
27
|
-
attributes.keys.
|
28
|
-
relationship_props[key] = attributes.delete(key) if
|
29
|
-
|
30
|
-
relationship_props
|
33
|
+
attributes.keys.each_with_object({}) do |key, relationship_props|
|
34
|
+
relationship_props[key] = attributes.delete(key) if [:from_node, :to_node].include?(key)
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
@@ -15,13 +15,16 @@ module Neo4j::Shared
|
|
15
15
|
|
16
16
|
ILLEGAL_PROPS = %w[from_node to_node start_node end_node]
|
17
17
|
|
18
|
+
attr_reader :_persisted_obj
|
19
|
+
|
18
20
|
def initialize(attributes={}, options={})
|
19
21
|
attributes = process_attributes(attributes)
|
20
|
-
relationship_props = self.class.extract_association_attributes!(attributes)
|
22
|
+
@relationship_props = self.class.extract_association_attributes!(attributes)
|
21
23
|
writer_method_props = extract_writer_methods!(attributes)
|
22
24
|
validate_attributes!(attributes)
|
23
25
|
send_props(writer_method_props) unless writer_method_props.nil?
|
24
|
-
|
26
|
+
|
27
|
+
@_persisted_obj = nil
|
25
28
|
|
26
29
|
super(attributes, options)
|
27
30
|
end
|
data/lib/neo4j/version.rb
CHANGED
data/neo4j.gemspec
CHANGED
@@ -11,15 +11,12 @@ Gem::Specification.new do |s|
|
|
11
11
|
|
12
12
|
s.authors = "Andreas Ronge"
|
13
13
|
s.email = 'andreas.ronge@gmail.com'
|
14
|
-
s.homepage = "
|
14
|
+
s.homepage = "https://github.com/andreasronge/neo4j/"
|
15
15
|
s.rubyforge_project = 'neo4j'
|
16
16
|
s.summary = "A graph database for Ruby"
|
17
17
|
s.license = 'MIT'
|
18
18
|
s.description = <<-EOF
|
19
|
-
|
20
|
-
The programmer works with an object-oriented, flexible network structure rather than with strict and static tables
|
21
|
-
yet enjoys all the benefits of a fully transactional, enterprise-strength database.
|
22
|
-
It comes included with the Apache Lucene document database.
|
19
|
+
A Neo4j OGM for use in Ruby on Rails and Rack frameworks, intended as a complete replacement for ActiveRecord.
|
23
20
|
EOF
|
24
21
|
|
25
22
|
s.require_path = 'lib'
|
@@ -34,7 +31,7 @@ It comes included with the Apache Lucene document database.
|
|
34
31
|
s.add_dependency("activesupport", "~> 4")
|
35
32
|
s.add_dependency("railties", "~> 4")
|
36
33
|
s.add_dependency('active_attr', "~> 0.8")
|
37
|
-
s.add_dependency("neo4j-core", "
|
34
|
+
s.add_dependency("neo4j-core", "= 3.0.0.rc.4")
|
38
35
|
|
39
36
|
if RUBY_PLATFORM =~ /java/
|
40
37
|
s.add_dependency("neo4j-community", '~> 2.0')
|
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: 3.0.0.rc.
|
4
|
+
version: 3.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-08-
|
11
|
+
date: 2014-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: orm_adapter
|
@@ -84,21 +84,18 @@ dependencies:
|
|
84
84
|
name: neo4j-core
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 3.0.0.rc.4
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 3.0.0.rc.4
|
97
|
-
description:
|
98
|
-
|
99
|
-
flexible network structure rather than with strict and static tables \nyet enjoys
|
100
|
-
all the benefits of a fully transactional, enterprise-strength database.\nIt comes
|
101
|
-
included with the Apache Lucene document database.\n"
|
97
|
+
description: |
|
98
|
+
A Neo4j OGM for use in Ruby on Rails and Rack frameworks, intended as a complete replacement for ActiveRecord.
|
102
99
|
email: andreas.ronge@gmail.com
|
103
100
|
executables:
|
104
101
|
- neo4j-jars
|
@@ -127,7 +124,10 @@ files:
|
|
127
124
|
- lib/neo4j/active_node/property.rb
|
128
125
|
- lib/neo4j/active_node/query.rb
|
129
126
|
- lib/neo4j/active_node/query/query_proxy.rb
|
127
|
+
- lib/neo4j/active_node/query/query_proxy_methods.rb
|
128
|
+
- lib/neo4j/active_node/query_methods.rb
|
130
129
|
- lib/neo4j/active_node/rels.rb
|
130
|
+
- lib/neo4j/active_node/scope.rb
|
131
131
|
- lib/neo4j/active_node/serialized_properties.rb
|
132
132
|
- lib/neo4j/active_node/validations.rb
|
133
133
|
- lib/neo4j/active_rel.rb
|
@@ -155,7 +155,7 @@ files:
|
|
155
155
|
- lib/rails/generators/neo4j/model/templates/model.erb
|
156
156
|
- lib/rails/generators/neo4j_generator.rb
|
157
157
|
- neo4j.gemspec
|
158
|
-
homepage:
|
158
|
+
homepage: https://github.com/andreasronge/neo4j/
|
159
159
|
licenses:
|
160
160
|
- MIT
|
161
161
|
metadata: {}
|