neo4j 3.0.0.rc.2 → 3.0.0.rc.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://secure.travis-ci.org/
|
1
|
+
# Welcome to Neo4j.rb [![Build Status](https://secure.travis-ci.org/neo4jrb/neo4j.png?branch=master)](http://travis-ci.org/neo4jrb/neo4j) [![Test Coverage](https://codeclimate.com/github/neo4jrb/neo4j/badges/coverage.svg)](https://codeclimate.com/github/neo4jrb/neo4j) [![Code Climate](https://codeclimate.com/github/neo4jrb/neo4j.png)](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: {}
|