graph2relational 0.0.1
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/bin/graph2relational +13 -0
- data/lib/graph2relational.rb +22 -0
- data/lib/graph2relational/neo4j-connection.rb +26 -0
- data/lib/graph2relational/neo4j-database.rb +81 -0
- data/lib/graph2relational/rdbms-column.rb +35 -0
- data/lib/graph2relational/rdbms-connection.rb +38 -0
- data/lib/graph2relational/rdbms-database.rb +146 -0
- data/lib/graph2relational/rdbms-join-table.rb +43 -0
- data/lib/graph2relational/rdbms-table.rb +36 -0
- data/lib/graph2relational/util-commandline-parser.rb +57 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ffd5eda800a15b74f1843d5e5e32c9063d8b2646
|
4
|
+
data.tar.gz: a10dc8539499cb64bbcbd88355bfa2a064ce5970
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aa6904f22f311b822dad0cfa1575b155cf1b3e03b58a1316eea3f026e4f69a7fc34984f242044c8ed2ebbf75dee188181a82c0f03d97dbb973a18d6cfb2bb6ea
|
7
|
+
data.tar.gz: 656a7e830a82e14f0fece65483f6efe32b9c82ba8af15f225839c04037623784ba6e9a09b3455fdbd5a97ec98365116837aab9d1c63366c609ebed3acc1c2fb1
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'graph2relational'
|
3
|
+
|
4
|
+
# parse command line input
|
5
|
+
options = G2R::Util::CommandLineParser.new.parse
|
6
|
+
|
7
|
+
# create database connections
|
8
|
+
neo4j = G2R::Neo4J::Database.new(options[:input])
|
9
|
+
rdbms = G2R::RDBMS::Database.new(options[:output], options)
|
10
|
+
|
11
|
+
# convert
|
12
|
+
rdbms.source = neo4j
|
13
|
+
rdbms.convert()
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'neography'
|
3
|
+
require 'sequel'
|
4
|
+
require 'optparse'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
#
|
8
|
+
$:.unshift File.dirname(__FILE__)
|
9
|
+
|
10
|
+
# Utils
|
11
|
+
require 'graph2relational/util-commandline-parser'
|
12
|
+
|
13
|
+
# Neo4J require
|
14
|
+
require 'graph2relational/neo4j-database'
|
15
|
+
require 'graph2relational/neo4j-connection'
|
16
|
+
|
17
|
+
# RDBMS require
|
18
|
+
require 'graph2relational/rdbms-database'
|
19
|
+
require 'graph2relational/rdbms-connection'
|
20
|
+
require 'graph2relational/rdbms-table'
|
21
|
+
require 'graph2relational/rdbms-join-table'
|
22
|
+
require 'graph2relational/rdbms-column'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module G2R
|
2
|
+
module Neo4J
|
3
|
+
class Connection
|
4
|
+
def initialize(options)
|
5
|
+
@conn = Neography::Rest.new(options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def query_data(cypher)
|
9
|
+
query(cypher)['data']
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_columns(cypher)
|
13
|
+
columns = Set.new
|
14
|
+
query(cypher)['data'].each do |row|
|
15
|
+
columns += row[0]['data'].keys
|
16
|
+
end
|
17
|
+
|
18
|
+
columns.to_a
|
19
|
+
end
|
20
|
+
|
21
|
+
def query(cypher)
|
22
|
+
@conn.execute_query(cypher)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module G2R
|
2
|
+
module Neo4J
|
3
|
+
|
4
|
+
# The Neo4J database that will have its data converted. It is the source of the conversion.
|
5
|
+
class Database
|
6
|
+
|
7
|
+
def initialize(connection_options)
|
8
|
+
# services
|
9
|
+
@conn = Connection.new(connection_options)
|
10
|
+
|
11
|
+
# data
|
12
|
+
@labels = nil
|
13
|
+
@label_attributes = {}
|
14
|
+
@label_relationships = {}
|
15
|
+
@relationships = nil
|
16
|
+
@relationship_attributes = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# ==========================================================================
|
20
|
+
# NODES
|
21
|
+
# ==========================================================================
|
22
|
+
def labels
|
23
|
+
# lazy load labels
|
24
|
+
if @labels.nil?
|
25
|
+
rows = @conn.query_data("MATCH (n) UNWIND labels(n) AS label RETURN DISTINCT label ORDER BY label")
|
26
|
+
@labels = rows.map {|row| row[0]}
|
27
|
+
end
|
28
|
+
|
29
|
+
@labels
|
30
|
+
end
|
31
|
+
|
32
|
+
def label_attributes(label)
|
33
|
+
if not @label_attributes.key? label
|
34
|
+
@label_attributes[label] = @conn.query_columns("MATCH (n:#{label}) RETURN n LIMIT 1000")
|
35
|
+
end
|
36
|
+
|
37
|
+
@label_attributes[label]
|
38
|
+
end
|
39
|
+
|
40
|
+
def label_relationships(label)
|
41
|
+
if not @label_relationships.key? label
|
42
|
+
@label_relationships[label] = @conn.query_data("MATCH (n:#{label})-[r]->(m) UNWIND labels(m) AS label WITH label, type(r) as relationship RETURN DISTINCT relationship, label")
|
43
|
+
end
|
44
|
+
|
45
|
+
@label_relationships[label]
|
46
|
+
end
|
47
|
+
|
48
|
+
def label_data(label)
|
49
|
+
return_clause = label_attributes(label).map {|column| "n.#{column}"}.join(", ")
|
50
|
+
@conn.query_data("MATCH (n:#{label}) RETURN id(n), #{return_clause}")
|
51
|
+
end
|
52
|
+
|
53
|
+
# ==========================================================================
|
54
|
+
# RELATIONSIIPS
|
55
|
+
# ==========================================================================
|
56
|
+
def relationships
|
57
|
+
# lazy load relationships
|
58
|
+
if @relationships.nil?
|
59
|
+
rows = @conn.query_data("MATCH (n)-[r]-(m) RETURN DISTINCT type(r) AS relationship ORDER BY relationship")
|
60
|
+
@relationships = rows.map{|row| row[0]}
|
61
|
+
end
|
62
|
+
|
63
|
+
@relationships
|
64
|
+
end
|
65
|
+
|
66
|
+
def relationship_attributes(source, type, target)
|
67
|
+
key = "#{source}_#{type}_#{target}"
|
68
|
+
if not @relationship_attributes.has_key? key
|
69
|
+
@relationship_attributes[key] = @conn.query_columns("MATCH (n:#{source})-[r:#{type}]->(m:#{target}) RETURN r LIMIT 1000")
|
70
|
+
end
|
71
|
+
|
72
|
+
@relationship_attributes[key]
|
73
|
+
end
|
74
|
+
|
75
|
+
def relationship_data(source, type, target)
|
76
|
+
return_clause = relationship_attributes(source, type, target).map {|column| "r.#{column}"}.join(", ")
|
77
|
+
@conn.query_data("MATCH (n:#{source})-[r:#{type}]->(m:#{target}) RETURN id(n), id(m), #{return_clause}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module G2R
|
2
|
+
module RDBMS
|
3
|
+
class Column
|
4
|
+
attr_accessor :name
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name.downcase.to_sym
|
8
|
+
@primary_key = false
|
9
|
+
@foreign_key = false
|
10
|
+
end
|
11
|
+
|
12
|
+
# Define the column as a primary key
|
13
|
+
def primary_key
|
14
|
+
@primary_key = true
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define the column as a foreign key
|
19
|
+
def foreign_key
|
20
|
+
@foreign_key = true
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if the column is a primary key
|
25
|
+
def primary_key?
|
26
|
+
@primary_key
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check if the column is a foreign key
|
30
|
+
def foreign_key?
|
31
|
+
@foreign_key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module G2R
|
2
|
+
module RDBMS
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
# sequel
|
7
|
+
@db = Sequel.connect(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_table(table)
|
11
|
+
@db.create_table! table.name do
|
12
|
+
|
13
|
+
table.columns.each do |column|
|
14
|
+
# primary key
|
15
|
+
if column.primary_key?
|
16
|
+
primary_key column.name
|
17
|
+
# foreign key
|
18
|
+
elsif column.foreign_key?
|
19
|
+
Integer column.name
|
20
|
+
# common column
|
21
|
+
else
|
22
|
+
String column.name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def insert_data(table)
|
29
|
+
@db.transaction do
|
30
|
+
table.data.each do |row|
|
31
|
+
@db[table.name.to_sym].insert(row)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module G2R
|
2
|
+
module RDBMS
|
3
|
+
|
4
|
+
# The target RDBMS database that will have the schema and data generated for. It is the target of the conversion.
|
5
|
+
class Database
|
6
|
+
|
7
|
+
# ==========================================================================
|
8
|
+
# INITIALIZATION
|
9
|
+
# ==========================================================================
|
10
|
+
def initialize(connection_options, app_options = {})
|
11
|
+
# services
|
12
|
+
@options = app_options
|
13
|
+
@conn = Connection.new(connection_options)
|
14
|
+
|
15
|
+
# data
|
16
|
+
reset_data()
|
17
|
+
end
|
18
|
+
|
19
|
+
# ==========================================================================
|
20
|
+
# CONVERSION
|
21
|
+
# ==========================================================================
|
22
|
+
def source=(source)
|
23
|
+
@source = source
|
24
|
+
end
|
25
|
+
|
26
|
+
def source
|
27
|
+
@source
|
28
|
+
end
|
29
|
+
|
30
|
+
# Export the schema creation and data import scripts to the specified location
|
31
|
+
def convert
|
32
|
+
# prepare
|
33
|
+
reset_data()
|
34
|
+
|
35
|
+
# create tables
|
36
|
+
puts "Creating base tables"
|
37
|
+
base_tables.each do |table|
|
38
|
+
@conn.create_table(table)
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "Creating relationship tables"
|
42
|
+
relationship_tables.each do |table|
|
43
|
+
@conn.create_table(table)
|
44
|
+
end
|
45
|
+
|
46
|
+
# insert data
|
47
|
+
puts "Inserting base tables data"
|
48
|
+
base_tables.each do |table|
|
49
|
+
@conn.insert_data(table)
|
50
|
+
end
|
51
|
+
|
52
|
+
puts "Inserting relationship tables data"
|
53
|
+
relationship_tables.each do |table|
|
54
|
+
@conn.insert_data(table)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Reset the saved data to perform a new conversion
|
59
|
+
def reset_data
|
60
|
+
@base_tables = nil
|
61
|
+
@relationship_tables = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# ==========================================================================
|
65
|
+
# TABLES
|
66
|
+
# ==========================================================================
|
67
|
+
# Get all the base tables that will be generated. They are based on the Neo4J labels.
|
68
|
+
# Columns are based on the Neo4J node attributes.
|
69
|
+
def base_tables
|
70
|
+
# lazy initialization
|
71
|
+
if @base_tables.nil?
|
72
|
+
@base_tables = @source.labels.map do |label|
|
73
|
+
|
74
|
+
# if excluding label, do not transform into base table
|
75
|
+
next if exclude? label
|
76
|
+
|
77
|
+
# generate table
|
78
|
+
table = Table.new(label)
|
79
|
+
|
80
|
+
# generate primary key
|
81
|
+
table.add_columns(Column.new(:id).primary_key)
|
82
|
+
|
83
|
+
# generate columns
|
84
|
+
columns = @source.label_attributes(label).map {|attribute| Column.new(attribute)}
|
85
|
+
table.add_columns(columns)
|
86
|
+
|
87
|
+
# generate data
|
88
|
+
label_data = @source.label_data(label)
|
89
|
+
table.add_data(label_data)
|
90
|
+
|
91
|
+
# generated table
|
92
|
+
table
|
93
|
+
end.compact
|
94
|
+
end
|
95
|
+
|
96
|
+
@base_tables.compact
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get all the relationships tables that will be generated. They are based in the found Neo4J relationships.
|
100
|
+
# Columss are based on the Neo4J relationship attributes.
|
101
|
+
def relationship_tables
|
102
|
+
# lazy initialization
|
103
|
+
if @relationship_tables.nil?
|
104
|
+
@relationship_tables = @source.labels.flat_map do |label|
|
105
|
+
|
106
|
+
# if excluding label, do not transform into relationship table
|
107
|
+
next if exclude? label
|
108
|
+
|
109
|
+
@source.label_relationships(label).map do |relationship|
|
110
|
+
relationship, target_label = relationship
|
111
|
+
|
112
|
+
# if excluding label, do not transform into relationship table
|
113
|
+
next if exclude? target_label
|
114
|
+
|
115
|
+
# gerarate table
|
116
|
+
table_name = "#{label}_#{relationship}_#{target_label}"
|
117
|
+
table = JoinTable.new(table_name)
|
118
|
+
|
119
|
+
# generate keys
|
120
|
+
table.source = label
|
121
|
+
table.target = target_label
|
122
|
+
|
123
|
+
# generate columns
|
124
|
+
columns = @source.relationship_attributes(label, relationship, target_label).map {|attribute| Column.new(attribute)}
|
125
|
+
table.add_columns(columns)
|
126
|
+
|
127
|
+
# generate data
|
128
|
+
relationship_data = @source.relationship_data(label, relationship, target_label)
|
129
|
+
table.add_data(relationship_data)
|
130
|
+
|
131
|
+
# generated table
|
132
|
+
table
|
133
|
+
end
|
134
|
+
end.compact
|
135
|
+
end
|
136
|
+
|
137
|
+
@relationship_tables
|
138
|
+
end
|
139
|
+
|
140
|
+
# Check if a label should be excluded and not be transformed into a table
|
141
|
+
def exclude?(label)
|
142
|
+
@options.has_key? :exclude and @options[:exclude].include? label
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module G2R
|
2
|
+
module RDBMS
|
3
|
+
|
4
|
+
class JoinTable < Table
|
5
|
+
|
6
|
+
# Additionally return the source and target column ids plus all defined columns
|
7
|
+
def columns
|
8
|
+
[source_column, target_column] + super
|
9
|
+
end
|
10
|
+
|
11
|
+
def source_column
|
12
|
+
Column.new(source + "_id").foreign_key
|
13
|
+
end
|
14
|
+
|
15
|
+
def target_column
|
16
|
+
if source != target
|
17
|
+
Column.new(target + "_id").foreign_key
|
18
|
+
else
|
19
|
+
Column.new(target + "_id_2").foreign_key
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# ==========================================================================
|
24
|
+
# ACESSORS
|
25
|
+
# ==========================================================================
|
26
|
+
def source=(source)
|
27
|
+
@source = source
|
28
|
+
end
|
29
|
+
|
30
|
+
def source
|
31
|
+
@source
|
32
|
+
end
|
33
|
+
|
34
|
+
def target=(target)
|
35
|
+
@target = target
|
36
|
+
end
|
37
|
+
|
38
|
+
def target
|
39
|
+
@target
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module G2R
|
2
|
+
module RDBMS
|
3
|
+
class Table
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name.downcase.to_sym
|
7
|
+
@columns = []
|
8
|
+
@data = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_columns(columns)
|
16
|
+
if columns.class == Array
|
17
|
+
@columns += columns
|
18
|
+
else
|
19
|
+
@columns << columns
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def columns
|
24
|
+
@columns
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_data(data)
|
28
|
+
@data += data
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
@data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module G2R
|
2
|
+
module Util
|
3
|
+
|
4
|
+
class CommandLineParser
|
5
|
+
|
6
|
+
def initialize()
|
7
|
+
@valid = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse()
|
11
|
+
# default values
|
12
|
+
options = {:input => nil, :output => nil, :exclude => []}
|
13
|
+
|
14
|
+
# display help if no args
|
15
|
+
ARGV << '-h' if ARGV.empty?
|
16
|
+
|
17
|
+
# do parsing
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: graph2relational -i <neo4j_connection_url> -o <rdbms_connection_url> [optional_arguments]"
|
20
|
+
|
21
|
+
# input file
|
22
|
+
opts.on("-i CONNECTION", "--input CONNECTION", "Neo4J connection URI from where the schema and data will be geberated") do |input_connection|
|
23
|
+
options[:input] = input_connection
|
24
|
+
end
|
25
|
+
|
26
|
+
# output dir
|
27
|
+
opts.on("-o CONNECTION", "--output CONNECTION", "RDBMS connection URI to where the schema and data will be persisted") do |output_connection|
|
28
|
+
options[:output] = output_connection
|
29
|
+
end
|
30
|
+
|
31
|
+
# exclusion
|
32
|
+
opts.on("-x EXCLUSION", "--exclude EXCLUSION", "Neo4J labels (separated by comma) to exclude from the conversion") do |exclude|
|
33
|
+
options[:exclude] = exclude.split(",").map{|exclude| exclude.strip}
|
34
|
+
end
|
35
|
+
|
36
|
+
# help
|
37
|
+
opts.on_tail("-h", "--help", "Help message") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end.parse!
|
42
|
+
|
43
|
+
# check mandatory args
|
44
|
+
if options[:input].nil?
|
45
|
+
puts "Missing parameter: specify Neo4J connection URI using -i <connection> parameter"
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
if options[:output].nil?
|
49
|
+
puts "Missing parameter: specify RDBMS connection URI using -o <connection> parameter"
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
|
53
|
+
return options
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graph2relational
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Renato Dinhani Conceição
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: neography
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sequel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.32'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.32'
|
41
|
+
description: Converts a Neo4J graph database to a supported RDBMS database automatically
|
42
|
+
generating tables from its labels, relationships and attributes
|
43
|
+
email: renatodinhani@gmail.com
|
44
|
+
executables:
|
45
|
+
- graph2relational
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- bin/graph2relational
|
50
|
+
- lib/graph2relational.rb
|
51
|
+
- lib/graph2relational/neo4j-connection.rb
|
52
|
+
- lib/graph2relational/neo4j-database.rb
|
53
|
+
- lib/graph2relational/rdbms-column.rb
|
54
|
+
- lib/graph2relational/rdbms-connection.rb
|
55
|
+
- lib/graph2relational/rdbms-database.rb
|
56
|
+
- lib/graph2relational/rdbms-join-table.rb
|
57
|
+
- lib/graph2relational/rdbms-table.rb
|
58
|
+
- lib/graph2relational/util-commandline-parser.rb
|
59
|
+
homepage: https://github.com/renatodinhani/graph2relational
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.4.5.1
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Converts a Neo4J graph database to a RDBMS database
|
83
|
+
test_files: []
|