agama 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/agama.gemspec +65 -0
- data/lib/agama.rb +4 -0
- data/lib/agama/adapters/tokyocabinet.rb +160 -0
- data/lib/agama/config.rb +7 -0
- data/lib/agama/graph.rb +267 -0
- data/lib/agama/keyify.rb +62 -0
- data/lib/agama/traverser.rb +78 -0
- metadata +9 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.1
|
data/agama.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{agama}
|
8
|
+
s.version = "0.5.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Aditya Rachakonda"]
|
12
|
+
s.date = %q{2011-05-10}
|
13
|
+
s.description = %q{A simple Graph DB on a key-value store}
|
14
|
+
s.email = %q{aditya.rachakonda@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"agama.gemspec",
|
28
|
+
"lib/agama.rb",
|
29
|
+
"lib/agama/adapters/tokyocabinet.rb",
|
30
|
+
"lib/agama/config.rb",
|
31
|
+
"lib/agama/graph.rb",
|
32
|
+
"lib/agama/keyify.rb",
|
33
|
+
"lib/agama/traverser.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_agama.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/arrac/agama}
|
38
|
+
s.licenses = ["MIT"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
41
|
+
s.summary = %q{A simple Graph DB on a key-value store}
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
50
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
|
51
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
54
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
55
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
56
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
60
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
61
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
62
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/agama.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'tokyocabinet'
|
2
|
+
include TokyoCabinet
|
3
|
+
|
4
|
+
module Agama
|
5
|
+
module Adapters
|
6
|
+
class TC
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def open (path)
|
11
|
+
@meta = HDB::new
|
12
|
+
# open the meta database
|
13
|
+
if !@meta.open(path + "/meta.tch", HDB::OWRITER | HDB::OCREAT)
|
14
|
+
ecode = @meta.ecode
|
15
|
+
raise "Error opening meta_db: #{@meta.errmsg(ecode)}"
|
16
|
+
end
|
17
|
+
|
18
|
+
@nodes = HDB::new
|
19
|
+
# open the nodes database
|
20
|
+
if !@nodes.open(path + "/nodes.tch", HDB::OWRITER | HDB::OCREAT)
|
21
|
+
ecode = @nodes.ecode
|
22
|
+
raise "Error opening nodes_db: #{@nodes.errmsg(ecode)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
@edges = BDB::new
|
26
|
+
# open the edges database
|
27
|
+
if !@edges.open(path + "/edges.tcb", BDB::OWRITER | BDB::OCREAT)
|
28
|
+
ecode = @edges.ecode
|
29
|
+
raise "Error opening edges_db: #{@edges.errmsg(ecode)}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
def close
|
36
|
+
if !@meta.close
|
37
|
+
ecode = @meta.ecode
|
38
|
+
raise "Error closing meta_db: #{@meta.errmsg(ecode)}"
|
39
|
+
end
|
40
|
+
if !@nodes.close
|
41
|
+
ecode = @nodes.ecode
|
42
|
+
raise "Error closing nodes_db: #{@nodes.errmsg(ecode)}"
|
43
|
+
end
|
44
|
+
if !@edges.close
|
45
|
+
ecode = @edges.ecode
|
46
|
+
raise "Error closing edges_db: #{@edges.errmsg(ecode)}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def m_put (key, value)
|
54
|
+
unless @meta.put(key, value)
|
55
|
+
ecode = @meta.ecode
|
56
|
+
raise "Error inserting meta_db: #{@meta.errmsg(ecode)}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def m_get (key)
|
61
|
+
return @meta.get(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def m_del (key)
|
65
|
+
unless @meta.out(key)
|
66
|
+
ecode = @meta.ecode
|
67
|
+
raise "Error deleting from meta_db: #{@meta.errmsg(ecode)}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
def n_put (key, value)
|
75
|
+
unless @nodes.put(key, value)
|
76
|
+
ecode = @nodes.ecode
|
77
|
+
raise "Error inserting into nodes_db: #{@nodes.errmsg(ecode)}"
|
78
|
+
end
|
79
|
+
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def n_get (key)
|
84
|
+
return @nodes.get(key)
|
85
|
+
end
|
86
|
+
|
87
|
+
def n_del (key)
|
88
|
+
unless @nodes.out(key)
|
89
|
+
ecode = @nodes.ecode
|
90
|
+
raise "Error deleting from nodes_db: #{@nodes.errmsg(ecode)}"
|
91
|
+
end
|
92
|
+
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def e_put (key, value)
|
98
|
+
unless @edges.put(key, value)
|
99
|
+
ecode = @edges.ecode
|
100
|
+
raise "Error inserting into edges_db: #{@edges.errmsg(ecode)}"
|
101
|
+
end
|
102
|
+
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def e_get (key)
|
107
|
+
return @edges.get(key)
|
108
|
+
end
|
109
|
+
|
110
|
+
def e_del (key)
|
111
|
+
unless @edges.out(key, value)
|
112
|
+
ecode = @edges.ecode
|
113
|
+
raise "Error deleting from edges_db: #{@edges.errmsg(ecode)}"
|
114
|
+
end
|
115
|
+
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def e_cursor
|
120
|
+
return TCCursor.new(@edges, "edge")
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class TCCursor
|
126
|
+
def initialize(bptree, type)
|
127
|
+
@c = BDBCUR::new(bptree)
|
128
|
+
@t = type
|
129
|
+
end
|
130
|
+
|
131
|
+
def first
|
132
|
+
@c.first
|
133
|
+
end
|
134
|
+
|
135
|
+
def last
|
136
|
+
@c.last
|
137
|
+
end
|
138
|
+
|
139
|
+
def jump(key)
|
140
|
+
@c.jump(key)
|
141
|
+
end
|
142
|
+
|
143
|
+
def prev
|
144
|
+
@c.prev
|
145
|
+
end
|
146
|
+
|
147
|
+
def next
|
148
|
+
@c.next
|
149
|
+
end
|
150
|
+
|
151
|
+
def key
|
152
|
+
@c.key
|
153
|
+
end
|
154
|
+
|
155
|
+
def value
|
156
|
+
@c.val
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/agama/config.rb
ADDED
data/lib/agama/graph.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
module Agama
|
2
|
+
class Graph
|
3
|
+
|
4
|
+
#Initialises variables and sets the path
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@db_path = params[:path] || "./"
|
8
|
+
@db = params[:db]
|
9
|
+
end
|
10
|
+
|
11
|
+
#Opens the database for access
|
12
|
+
|
13
|
+
def open
|
14
|
+
@db.open(@db_path)
|
15
|
+
|
16
|
+
#Create meta variables if they are absent
|
17
|
+
unless @db.m_get("n")
|
18
|
+
@db.m_put("n", Marshal.dump(0)) #Total node count
|
19
|
+
@db.m_put("m", Marshal.dump(0)) #Total edge count
|
20
|
+
@db.m_put("node#{Config::DEFAULT_TYPE}",
|
21
|
+
Marshal.dump(0)) #Node count for default type
|
22
|
+
@db.m_put("edge#{Config::DEFAULT_TYPE}",
|
23
|
+
Marshal.dump({:directed => false, :count => 0})) #Edge count for default type
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
#Closes the database connection
|
29
|
+
def close
|
30
|
+
@db.close
|
31
|
+
end
|
32
|
+
|
33
|
+
#Creates/Updates a node
|
34
|
+
|
35
|
+
def set_node(node)
|
36
|
+
return nil unless node[:name]
|
37
|
+
|
38
|
+
#Get the type of the node
|
39
|
+
type = node[:type] || Config::DEFAULT_TYPE
|
40
|
+
node[:type] = type
|
41
|
+
|
42
|
+
#Convert the node into Key and Value strings for storage
|
43
|
+
key = Keyify.node(node)
|
44
|
+
value = Marshal.dump(self.clean_node(node)) #remove key items from value
|
45
|
+
|
46
|
+
#Check if the node type exists, and if so get its count
|
47
|
+
count = Marshal.load(@db.m_get("node#{type}")) if @db.m_get("node#{type}")
|
48
|
+
|
49
|
+
#Check whether the operation is an insert (not an update), if so increment count
|
50
|
+
unless @db.n_get(key)
|
51
|
+
#Increment type-specific count
|
52
|
+
if count
|
53
|
+
count += 1
|
54
|
+
@db.m_put("node#{type}", Marshal.dump(count))
|
55
|
+
else
|
56
|
+
@db.m_put("node#{type}", Marshal.dump(1))
|
57
|
+
end
|
58
|
+
|
59
|
+
#Increment global count
|
60
|
+
@db.m_put("n", Marshal.dump(Marshal.load(@db.m_get("n")) + 1))
|
61
|
+
end
|
62
|
+
|
63
|
+
#Store the node
|
64
|
+
if @db.n_put(key, value)
|
65
|
+
node
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#Fetches the node requested
|
70
|
+
|
71
|
+
def get_node(node)
|
72
|
+
return nil unless node[:name]
|
73
|
+
|
74
|
+
#Convert the node into a Key string and fetch the corresponding data
|
75
|
+
key = Keyify.node(node)
|
76
|
+
value = @db.n_get(key)
|
77
|
+
|
78
|
+
if value
|
79
|
+
new_node = Marshal.load(value)
|
80
|
+
new_node[:name] = node[:name]
|
81
|
+
new_node[:type] = node[:type] || Config::DEFAULT_TYPE
|
82
|
+
new_node
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#Creates/Updates an edge
|
87
|
+
|
88
|
+
def set_edge(edge)
|
89
|
+
return false unless edge
|
90
|
+
return false unless edge[:from][:name]
|
91
|
+
return false unless edge[:to][:name]
|
92
|
+
|
93
|
+
#Get the type of the edge
|
94
|
+
type = edge[:type] || Config::DEFAULT_TYPE
|
95
|
+
edge[:type] = type
|
96
|
+
|
97
|
+
#Check if the edge type exists
|
98
|
+
etype = Marshal.load(@db.m_get("edge#{type}")) if @db.m_get("edge#{type}")
|
99
|
+
|
100
|
+
#Integrity check: Check whether the edge direction is not contradictory
|
101
|
+
if edge[:directed]
|
102
|
+
if etype
|
103
|
+
if etype[:directed] != edge[:directed]
|
104
|
+
raise "Edge creation error: edge direction contradicting existing edges"
|
105
|
+
return
|
106
|
+
end
|
107
|
+
end
|
108
|
+
else
|
109
|
+
if etype
|
110
|
+
edge[:directed] = etype[:directed]
|
111
|
+
else
|
112
|
+
edge[:directed] = false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#Convert the edge into Key, Reversed Key and Value strings for storage
|
117
|
+
key, reverse_key = Keyify.edge(edge)
|
118
|
+
value = Marshal.dump(clean_edge(edge))
|
119
|
+
|
120
|
+
#Integrity check: Check if the incident nodes are defined
|
121
|
+
unless (self.get_node(edge[:from]) and self.get_node(edge[:to]))
|
122
|
+
raise "Edge creation error: node(s) not defined"
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
#Check whether the operation is an insert (not an update), if so increment count
|
127
|
+
unless @db.e_get(key)
|
128
|
+
if etype
|
129
|
+
etype[:count] += 1
|
130
|
+
@db.m_put("edge#{type}", Marshal.dump(etype))
|
131
|
+
else
|
132
|
+
@db.m_put("edge#{type}",
|
133
|
+
Marshal.dump({:directed => edge[:directed], :count => 1}))
|
134
|
+
end
|
135
|
+
|
136
|
+
#Increment global count
|
137
|
+
@db.m_put("m", Marshal.dump(Marshal.load(@db.m_get("m")) + 1))
|
138
|
+
end
|
139
|
+
|
140
|
+
#Add the edge and the reversed edge
|
141
|
+
if @db.e_put(key, value) and @db.e_put(reverse_key, value)
|
142
|
+
return edge
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
#Fetches the value assigned to the edge from the _from_ node to the _to_ node
|
147
|
+
|
148
|
+
def get_edge(edge)
|
149
|
+
return nil unless edge[:from][:name]
|
150
|
+
return nil unless edge[:to][:name]
|
151
|
+
|
152
|
+
#Get the type of the edge
|
153
|
+
type = edge[:type] || Config::DEFAULT_TYPE
|
154
|
+
edge[:type] = type
|
155
|
+
|
156
|
+
#Check if the edge type exists
|
157
|
+
etype = Marshal.load(@db.m_get("edge#{type}")) if @db.m_get("edge#{type}")
|
158
|
+
|
159
|
+
#Integrity check: Check whether the edge direction is not contradictory
|
160
|
+
if edge[:directed]
|
161
|
+
if etype
|
162
|
+
if etype[:directed] != edge[:directed]
|
163
|
+
return false
|
164
|
+
end
|
165
|
+
else
|
166
|
+
#If there is no edge of that type then pre-empt the result
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
else
|
170
|
+
if etype
|
171
|
+
edge[:directed] = etype[:directed]
|
172
|
+
else
|
173
|
+
#If there is no edge of that type then pre-empt the result
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
#Convert the edge into a Key string and fetch the corresponding data
|
179
|
+
key, reverse_key = Keyify.edge(edge)
|
180
|
+
value = @db.e_get(key)
|
181
|
+
|
182
|
+
if value
|
183
|
+
new_edge = Marshal.load(value)
|
184
|
+
new_edge[:from] = self.get_node(edge[:from])
|
185
|
+
new_edge[:to] = self.get_node(edge[:to])
|
186
|
+
new_edge[:type] = edge[:type]
|
187
|
+
new_edge[:directed] = etype[:directed] #Pick direction alone from the meta_db for consistency
|
188
|
+
new_edge
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def neighbours(node)
|
193
|
+
traverser = Traverser.new(@db, self)
|
194
|
+
traverser.set(:from => node)
|
195
|
+
end
|
196
|
+
|
197
|
+
#def filter(params)
|
198
|
+
#end
|
199
|
+
|
200
|
+
#def delete_node(node)
|
201
|
+
#return nil unless node[:name]
|
202
|
+
|
203
|
+
#key = Key.node(node)
|
204
|
+
|
205
|
+
#end
|
206
|
+
|
207
|
+
#def delete_edge(edge)
|
208
|
+
|
209
|
+
#end
|
210
|
+
|
211
|
+
# def put(store, key, value)
|
212
|
+
# unless store.put(key, value)
|
213
|
+
# STDERR.printf("put error: %s\n", store.errmsg(store.ecode))
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
|
217
|
+
#Accessors for meta values
|
218
|
+
|
219
|
+
def node_count(type = nil)
|
220
|
+
if type
|
221
|
+
value = @db.m_get("node#{type}")
|
222
|
+
if value
|
223
|
+
Marshal.load(value)
|
224
|
+
end
|
225
|
+
else
|
226
|
+
Marshal.load(@db.m_get("n"))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def edge_count(type = nil)
|
231
|
+
if type
|
232
|
+
value = @db.m_get("edge#{type}")
|
233
|
+
if value
|
234
|
+
etype = Marshal.load(value)
|
235
|
+
etype[:count]
|
236
|
+
end
|
237
|
+
else
|
238
|
+
Marshal.load(@db.m_get("m"))
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
#Methods to seperate key from the value
|
243
|
+
|
244
|
+
def clean_node(node)
|
245
|
+
new_node = {}
|
246
|
+
node.each do |key, value|
|
247
|
+
next if (key == :type or key == :name)
|
248
|
+
new_node[key] = value
|
249
|
+
end
|
250
|
+
|
251
|
+
return new_node
|
252
|
+
end
|
253
|
+
|
254
|
+
def clean_edge(edge)
|
255
|
+
new_edge = {}
|
256
|
+
edge.each do |key, value|
|
257
|
+
next if (key == :type or key == :from or key == :to or key == :directed)
|
258
|
+
new_edge[key] = value
|
259
|
+
end
|
260
|
+
|
261
|
+
return new_edge
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
data/lib/agama/keyify.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Agama
|
2
|
+
|
3
|
+
module Keyify
|
4
|
+
|
5
|
+
def self.node(node)
|
6
|
+
type = node[:type] || Config::DEFAULT_TYPE
|
7
|
+
[type, node[:name]].join(Config::NODE_DELIMITOR)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.edge(edge)
|
11
|
+
from = self.node(edge[:from])
|
12
|
+
type = edge[:type] || Config::DEFAULT_TYPE
|
13
|
+
outgoing, incoming = if edge[:directed] then
|
14
|
+
["O", "I"]
|
15
|
+
else
|
16
|
+
["N", "N"]
|
17
|
+
end
|
18
|
+
|
19
|
+
to = self.node(edge[:to])
|
20
|
+
|
21
|
+
key = [from, type, outgoing, to].join(Config::EDGE_DELIMITOR)
|
22
|
+
reverse_key = [to, type, incoming, from].join(Config::EDGE_DELIMITOR)
|
23
|
+
|
24
|
+
[key, reverse_key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.range(params)
|
28
|
+
from = self.node(params[:from]) if params[:from]
|
29
|
+
|
30
|
+
type = params[:edge_type]
|
31
|
+
|
32
|
+
if type
|
33
|
+
dir = params[:direction]
|
34
|
+
if dir
|
35
|
+
to_type = params[:to_type]
|
36
|
+
if to_type
|
37
|
+
[from, type, dir, to_type].join(Config::EDGE_DELIMITOR)
|
38
|
+
else
|
39
|
+
[from, type, dir, nil].join(Config::EDGE_DELIMITOR)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
[from, type, nil].join(Config::EDGE_DELIMITOR)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
[from, nil].join(Config::EDGE_DELIMITOR)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.parse(key)
|
50
|
+
from_key, edge_type, direction, to_key = key.split(Config::EDGE_DELIMITOR)
|
51
|
+
type, name = to_key.split(Config::NODE_DELIMITOR)
|
52
|
+
directed = true
|
53
|
+
directed = false if (direction == "N")
|
54
|
+
[{:type => type, :name => name}, edge_type, direction, directed]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.subkey?(subkey, key)
|
58
|
+
key [0, subkey.length] == subkey
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Agama
|
2
|
+
class Traverser
|
3
|
+
|
4
|
+
attr_accessor :params
|
5
|
+
|
6
|
+
def initialize(db, graph)
|
7
|
+
@cursor = db.e_cursor
|
8
|
+
@search_key = ""
|
9
|
+
@unset = true
|
10
|
+
@graph = graph
|
11
|
+
@from = nil
|
12
|
+
@params = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def set(params)
|
16
|
+
@params.merge!(params)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def along(edge_type)
|
21
|
+
self.set(:edge_type => edge_type)
|
22
|
+
end
|
23
|
+
|
24
|
+
def outgoing
|
25
|
+
self.set(:edge_type => Config::DEFAULT_TYPE) unless @params[:edge_type]
|
26
|
+
self.set(:direction => "O")
|
27
|
+
end
|
28
|
+
|
29
|
+
def incoming
|
30
|
+
self.set(:edge_type => Config::DEFAULT_TYPE) unless @params[:edge_type]
|
31
|
+
self.set(:direction => "I")
|
32
|
+
end
|
33
|
+
|
34
|
+
def undirected
|
35
|
+
self.set(:edge_type => Config::DEFAULT_TYPE) unless @params[:edge_type]
|
36
|
+
self.set(:direction => "N")
|
37
|
+
end
|
38
|
+
|
39
|
+
def of_type(node_type)
|
40
|
+
self.set(:edge_type => Config::DEFAULT_TYPE) unless @params[:edge_type]
|
41
|
+
self.set(:direction => "N") unless @params[:direction]
|
42
|
+
self.set(:to_type => node_type)
|
43
|
+
end
|
44
|
+
|
45
|
+
def each
|
46
|
+
search_key = ""
|
47
|
+
from = nil
|
48
|
+
if @unset
|
49
|
+
search_key = Keyify.range(@params)
|
50
|
+
from = @graph.get_node(@params[:from])
|
51
|
+
|
52
|
+
@cursor.jump(search_key)
|
53
|
+
@unset = false
|
54
|
+
end
|
55
|
+
|
56
|
+
while @cursor.key and Keyify.subkey?(search_key, @cursor.key)
|
57
|
+
to, type, direction, directed = Keyify.parse(@cursor.key)
|
58
|
+
|
59
|
+
if @cursor.value
|
60
|
+
new_edge = Marshal.load(@cursor.value)
|
61
|
+
new_edge[:type] = type
|
62
|
+
new_edge[:directed] = directed
|
63
|
+
if direction == "I"
|
64
|
+
new_edge[:to] = from
|
65
|
+
new_edge[:from] = @graph.get_node(to)
|
66
|
+
else
|
67
|
+
new_edge[:from] = from
|
68
|
+
new_edge[:to] = @graph.get_node(to)
|
69
|
+
end
|
70
|
+
yield new_edge
|
71
|
+
end
|
72
|
+
|
73
|
+
@cursor.next
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agama
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 1
|
10
|
+
version: 0.5.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Aditya Rachakonda
|
@@ -95,7 +95,13 @@ files:
|
|
95
95
|
- README.rdoc
|
96
96
|
- Rakefile
|
97
97
|
- VERSION
|
98
|
+
- agama.gemspec
|
98
99
|
- lib/agama.rb
|
100
|
+
- lib/agama/adapters/tokyocabinet.rb
|
101
|
+
- lib/agama/config.rb
|
102
|
+
- lib/agama/graph.rb
|
103
|
+
- lib/agama/keyify.rb
|
104
|
+
- lib/agama/traverser.rb
|
99
105
|
- test/helper.rb
|
100
106
|
- test/test_agama.rb
|
101
107
|
has_rdoc: true
|