neo4r 0.0.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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +130 -0
- data/Rakefile +2 -0
- data/lib/neo4r.rb +20 -0
- data/lib/neo4r/attributes.rb +68 -0
- data/lib/neo4r/finder.rb +68 -0
- data/lib/neo4r/index_config_loader.rb +35 -0
- data/lib/neo4r/node.rb +54 -0
- data/lib/neo4r/node_relationship.rb +39 -0
- data/lib/neo4r/node_traverser.rb +154 -0
- data/lib/neo4r/paginated.rb +25 -0
- data/lib/neo4r/property_container.rb +38 -0
- data/lib/neo4r/relation.rb +174 -0
- data/lib/neo4r/relationship.rb +51 -0
- data/lib/neo4r/relationship_traverser.rb +86 -0
- data/lib/neo4r/rest_wrapper.rb +10 -0
- data/lib/neo4r/type_converters.rb +336 -0
- data/lib/neo4r/version.rb +3 -0
- data/lib/neo4r/will_paginate.rb +20 -0
- data/neo4r.gemspec +29 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 38c41f26322670c74f7c715c4a6ebb420f7d2280
|
4
|
+
data.tar.gz: a5d6264214419449d7b4162df24701add7b6b915
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 457d03a1975c88bf9a44ab54c599ff41e4f63f64ddbba892516dccd981537b740f18f4c5ab55fb5a683f14e198bbf9099c9e80053f779fe512c922cc6a2d143f
|
7
|
+
data.tar.gz: f4da9b851ca5d6e2b8e4ffbcad5d9dbe91dbdb8d62cc2b495b535895cf4af7b59639b638a524f1754f7282ca794cd590f45ea35c043b8e8cf9b95524702c6103
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Hiroyuki Sato
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Neo4r
|
2
|
+
|
3
|
+
Neo4r is an Active Model compliant Ruby wrapper for [the Neo4j graph database](http://neo4j.com/). It uses [the Neography](https://github.com/maxdemarzi/neography) gems.
|
4
|
+
|
5
|
+
|
6
|
+
## Requirement
|
7
|
+
|
8
|
+
- ruby ~> 2.1
|
9
|
+
- neography ~> 1.6
|
10
|
+
|
11
|
+
## Neo4j version support
|
12
|
+
|
13
|
+
- 2.0.x
|
14
|
+
- 2.1.x
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'neo4r'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install neo4r
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
### Configuration
|
35
|
+
|
36
|
+
Neo4r uses Neography to handle Neo4j REST API. So the configuration is same with Neography.
|
37
|
+
Please see [the Neography document](https://github.com/maxdemarzi/neography/tree/v1.6.0#configuration-and-initialization).
|
38
|
+
|
39
|
+
### Modeling
|
40
|
+
|
41
|
+
A sample Node model.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class User < Neo4r::Node
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Create a new node.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
user = User.new
|
52
|
+
user.name = "Hiroyuki Sato"
|
53
|
+
user.mail = "hiroyuki@example.com"
|
54
|
+
user.age = 35
|
55
|
+
user.save # => true
|
56
|
+
```
|
57
|
+
|
58
|
+
Search nodes.
|
59
|
+
**The syntax of an exact match is only implemented.**
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
users = User.where(name: "Hiroyuki Sato")
|
63
|
+
puts users.first.name # => "Hiroyuki Sato"
|
64
|
+
```
|
65
|
+
|
66
|
+
Update nodes.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
user = User.where(name: "Hiroyuki Sato").first
|
70
|
+
user.age = 36
|
71
|
+
user.save # => true
|
72
|
+
```
|
73
|
+
|
74
|
+
Delete nodes.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
user = User.where(name: "Hiroyuki Sato").first
|
78
|
+
user.destroy => true
|
79
|
+
```
|
80
|
+
|
81
|
+
Add connection.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
ozawa = User.new
|
85
|
+
ozawa.name = "Kunio Ozawa"
|
86
|
+
ozawa.save
|
87
|
+
|
88
|
+
sato = User.new
|
89
|
+
sato.name = "Hiroyuki Sato"
|
90
|
+
sato.save
|
91
|
+
|
92
|
+
nakamura = User.new
|
93
|
+
nakamura.name = "Hiroyuki Nakamura"
|
94
|
+
nakamura.save
|
95
|
+
|
96
|
+
sugahara = User.new
|
97
|
+
sugahara.name = "Junichi Sugahara"
|
98
|
+
sugahara.save
|
99
|
+
|
100
|
+
nakamura.incoming(:boss) << ozawa
|
101
|
+
nakamura.incoming(:boss) << sato
|
102
|
+
sugahara.incoming(:boss) << nakamura
|
103
|
+
```
|
104
|
+
|
105
|
+
Traverse connection.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
sugahara.incoming(:boss).each { |user| puts user.name }
|
109
|
+
# output
|
110
|
+
# => "Hiroyuki Nakamura"
|
111
|
+
sugahara.incoming(:boss).depth(:all).each { |user| puts user.name }
|
112
|
+
# output
|
113
|
+
# => "Hiroyuki Nakamura"
|
114
|
+
# => "Hiroyuki Sato"
|
115
|
+
# => "Kunio Ozawa"
|
116
|
+
```
|
117
|
+
|
118
|
+
Delete connection.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
nakamura.rels(:boss).to_other(sato).destroy
|
122
|
+
```
|
123
|
+
|
124
|
+
## Contributing
|
125
|
+
|
126
|
+
1. Fork it ( https://github.com/hiroponz/neo4r/fork )
|
127
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
128
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
129
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
130
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/neo4r.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "active_model"
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/core_ext"
|
4
|
+
require "neography"
|
5
|
+
|
6
|
+
require "neo4r/attributes"
|
7
|
+
require "neo4r/index_config_loader"
|
8
|
+
require "neo4r/node"
|
9
|
+
require "neo4r/node_traverser"
|
10
|
+
require "neo4r/paginated"
|
11
|
+
require "neo4r/relation"
|
12
|
+
require "neo4r/relationship"
|
13
|
+
require "neo4r/relationship_traverser"
|
14
|
+
require "neo4r/rest_wrapper"
|
15
|
+
require "neo4r/version"
|
16
|
+
require "neo4r/will_paginate"
|
17
|
+
|
18
|
+
module Neo4r
|
19
|
+
# Your code goes here...
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "neo4r/type_converters"
|
2
|
+
|
3
|
+
module Neo4r
|
4
|
+
# Neo4r node attributes module
|
5
|
+
module Attributes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# class methods
|
9
|
+
module ClassMethods
|
10
|
+
# @return [Hash] a hash of all properties and its configuration defined by
|
11
|
+
# property class method
|
12
|
+
def decl_props
|
13
|
+
@decl_props ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# make sure the inherited classes inherit the <tt>_decl_props</tt> hash
|
17
|
+
def inherited(klass)
|
18
|
+
klass.instance_variable_set(:@decl_props, decl_props.clone)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def property(*args)
|
23
|
+
options = args.extract_options!
|
24
|
+
args.each do |property_sym|
|
25
|
+
property_setup(property_sym, options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_ruby(hash)
|
30
|
+
ret = {}
|
31
|
+
hash.each do |k, v|
|
32
|
+
if decl_props.key?(k.to_sym)
|
33
|
+
ret[k] = decl_props[k.to_sym][:converter].to_ruby(v)
|
34
|
+
else
|
35
|
+
ret[k] = v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_java(hash)
|
42
|
+
ret = {}
|
43
|
+
hash.each do |k, v|
|
44
|
+
if decl_props.key?(k.to_sym)
|
45
|
+
ret[k] = decl_props[k.to_sym][:converter].to_java(v)
|
46
|
+
elsif v
|
47
|
+
ret[k] = v
|
48
|
+
end
|
49
|
+
end
|
50
|
+
ret
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def property_setup(property, options)
|
56
|
+
decl_props[property] = options
|
57
|
+
handle_property_options_for(property, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_property_options_for(property, options)
|
61
|
+
converter = options[:converter] ||
|
62
|
+
Neo4r::TypeConverters
|
63
|
+
.converter(decl_props[property][:type])
|
64
|
+
decl_props[property][:converter] = converter
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/neo4r/finder.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require "neo4r/relation"
|
2
|
+
|
3
|
+
module Neo4r
|
4
|
+
# Neo4r Finder module
|
5
|
+
module Finder
|
6
|
+
def find(neo_id)
|
7
|
+
rest = RestWrapper.new
|
8
|
+
node = rest.get_node(neo_id)
|
9
|
+
new(node)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_query(query)
|
13
|
+
rest = RestWrapper.new
|
14
|
+
ret = rest.execute_query(query)
|
15
|
+
ret["data"].map do |data|
|
16
|
+
cols = []
|
17
|
+
data.each do |col|
|
18
|
+
cols << new(col)
|
19
|
+
end
|
20
|
+
if cols.size == 1
|
21
|
+
cols.first
|
22
|
+
else
|
23
|
+
cols
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def labels(*labels)
|
29
|
+
relation.labels(*labels)
|
30
|
+
end
|
31
|
+
|
32
|
+
def where(hash)
|
33
|
+
relation.where(hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
def asc(*props)
|
37
|
+
relation.asc(*props)
|
38
|
+
end
|
39
|
+
|
40
|
+
def desc(*props)
|
41
|
+
relation.desc(*props)
|
42
|
+
end
|
43
|
+
|
44
|
+
def limit(n)
|
45
|
+
relation.limit(n)
|
46
|
+
end
|
47
|
+
|
48
|
+
def offset(n)
|
49
|
+
relation.offset(n)
|
50
|
+
end
|
51
|
+
|
52
|
+
def all
|
53
|
+
relation.all
|
54
|
+
end
|
55
|
+
|
56
|
+
def first
|
57
|
+
relation.first
|
58
|
+
end
|
59
|
+
|
60
|
+
def count
|
61
|
+
relation.count
|
62
|
+
end
|
63
|
+
|
64
|
+
def relation
|
65
|
+
Relation.new(self)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Neo4r
|
3
|
+
module IndexConfigLoader
|
4
|
+
#
|
5
|
+
# Load index config from database.
|
6
|
+
#
|
7
|
+
class << self
|
8
|
+
# entry point.
|
9
|
+
def load_index_config(mod)
|
10
|
+
if ExperimentKlass.table_exists?
|
11
|
+
ExperimentKlass.all.each do |klass|
|
12
|
+
klass.props.each do |prop|
|
13
|
+
prop_key = prop.mnemonic.to_sym
|
14
|
+
if prop.is_a?(Property::String)
|
15
|
+
mod.index prop_key, field_type: String
|
16
|
+
elsif prop.is_a?(Property::Number)
|
17
|
+
mod.index prop_key, field_type: Float
|
18
|
+
elsif prop.is_a?(Property::Date)
|
19
|
+
mod.index prop_key, field_type: Date
|
20
|
+
elsif prop.is_a?(Property::DateTime)
|
21
|
+
mod.index prop_key, field_type: DateTime
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def included(mod)
|
29
|
+
load_index_config(mod)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/neo4r/node.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "neo4r/finder"
|
2
|
+
require "neo4r/property_container"
|
3
|
+
require "neo4r/node_relationship"
|
4
|
+
|
5
|
+
module Neo4r
|
6
|
+
# Neo4r Node class
|
7
|
+
class Node < PropertyContainer
|
8
|
+
extend Finder
|
9
|
+
include ActiveModel::Conversion
|
10
|
+
include NodeRelationship
|
11
|
+
|
12
|
+
property :created_at, type: DateTime
|
13
|
+
property :updated_at, type: DateTime
|
14
|
+
|
15
|
+
def self.create(args)
|
16
|
+
node = new(args)
|
17
|
+
if node.save
|
18
|
+
node
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def labels
|
25
|
+
self.class.name.split("::")
|
26
|
+
end
|
27
|
+
|
28
|
+
def labels=(labels)
|
29
|
+
fail "Can not change label!"
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
time = DateTime.now
|
34
|
+
@table[:created_at] = time if is_new?
|
35
|
+
@table[:updated_at] = time
|
36
|
+
data = self.class.to_java(@table)
|
37
|
+
if is_new?
|
38
|
+
node = rest.create_node(data)
|
39
|
+
@neo_id = node["self"].split("/").last.to_i
|
40
|
+
else
|
41
|
+
rest.reset_node_properties(@neo_id, data)
|
42
|
+
end
|
43
|
+
rest.set_label(@neo_id, labels)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def destroy
|
48
|
+
rest.delete_node!(@neo_id)
|
49
|
+
@neo_id = nil
|
50
|
+
@table = {}
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "neo4r/node_traverser"
|
2
|
+
require "neo4r/relationship_traverser"
|
3
|
+
|
4
|
+
module Neo4r
|
5
|
+
# Node relationship module
|
6
|
+
module NodeRelationship
|
7
|
+
DIRECTIONS = %W(incoming, in, outgoing, out, all, both)
|
8
|
+
|
9
|
+
def outgoing(types = nil)
|
10
|
+
NodeTraverser.new(self).outgoing(types)
|
11
|
+
end
|
12
|
+
|
13
|
+
def incoming(types = nil)
|
14
|
+
NodeTraverser.new(self).incoming(types)
|
15
|
+
end
|
16
|
+
|
17
|
+
def both(types = nil)
|
18
|
+
NodeTraverser.new(self).both(types)
|
19
|
+
end
|
20
|
+
|
21
|
+
def rels(*types)
|
22
|
+
RelationshipTraverser.new(self, types, :both)
|
23
|
+
end
|
24
|
+
|
25
|
+
def rel(dir, type)
|
26
|
+
rel = RelationshipTraverser.new(self, type, dir)
|
27
|
+
rel = rel.first unless rel.empty?
|
28
|
+
rel
|
29
|
+
end
|
30
|
+
|
31
|
+
def rel?(dir = nil, type = nil)
|
32
|
+
if DIRECTIONS.include?(dir.to_s)
|
33
|
+
!rest.get_node_relationships(self, dir, type).empty?
|
34
|
+
else
|
35
|
+
!rest.get_node_relationships(self, type, dir).empty?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|