neo4j_bolt 0.1.6 → 0.1.7
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/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/lib/neo4j_bolt/version.rb +1 -1
- data/lib/neo4j_bolt.rb +133 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86471d4b049b4054a378f5084ae13dda181bd818a205ee4a67e6aa4bc5d0607f
|
4
|
+
data.tar.gz: b1e855e4689946026eaa7971acb730577a57973b02605c893916d15e2438b1f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4e414cb24861f58ae67f2a5c92da46f720c861fd49131befa16a9c046ea557d1e31fb40130fe892aa91b38d0ec3de881d4da94de4fde4a5288ab0add379c33f
|
7
|
+
data.tar.gz: be8ed37b6ea0a7e358c25257eea3be90f78c2fd1342a4666bc244a5a379e241780a6df01d4005c4b26a4986a2fd24cd0a65f7569f4de19bcbf4a65517a33aa8a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Neo4jBolt
|
2
2
|
|
3
|
-
A Neo4j/Bolt driver written in pure Ruby. Currently only supporting Neo4j 4.4.
|
3
|
+
A Neo4j/Bolt driver written in pure Ruby. Currently only supporting Neo4j 4.4. Caution: This gem is not feature complete, and also the documention is not complete yet.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
data/lib/neo4j_bolt/version.rb
CHANGED
data/lib/neo4j_bolt.rb
CHANGED
@@ -11,6 +11,7 @@ module Neo4jBolt
|
|
11
11
|
self.bolt_port = 7687
|
12
12
|
|
13
13
|
NEO4J_DEBUG = 0
|
14
|
+
CONSTRAINT_INDEX_PREFIX = 'neo4j_bolt_'
|
14
15
|
|
15
16
|
module ServerState
|
16
17
|
DISCONNECTED = 0
|
@@ -830,6 +831,50 @@ module Neo4jBolt
|
|
830
831
|
end
|
831
832
|
rows.first
|
832
833
|
end
|
834
|
+
|
835
|
+
def setup_constraints_and_indexes(constraints, indexes)
|
836
|
+
wanted_constraints = Set.new()
|
837
|
+
wanted_indexes = Set.new()
|
838
|
+
# STDERR.puts "Setting up constraints and indexes..."
|
839
|
+
constraints.each do |constraint|
|
840
|
+
unless constraint =~ /\w+\/\w+/
|
841
|
+
raise "Unexpected constraint format: #{constraint}"
|
842
|
+
end
|
843
|
+
constraint_name = "#{CONSTRAINT_INDEX_PREFIX}#{constraint.gsub('/', '_')}"
|
844
|
+
wanted_constraints << constraint_name
|
845
|
+
label = constraint.split('/').first
|
846
|
+
property = constraint.split('/').last
|
847
|
+
query = "CREATE CONSTRAINT #{constraint_name} IF NOT EXISTS FOR (n:#{label}) REQUIRE n.#{property} IS UNIQUE"
|
848
|
+
# STDERR.puts query
|
849
|
+
neo4j_query(query)
|
850
|
+
end
|
851
|
+
indexes.each do |index|
|
852
|
+
unless index =~ /\w+\/\w+/
|
853
|
+
raise "Unexpected index format: #{index}"
|
854
|
+
end
|
855
|
+
index_name = "#{CONSTRAINT_INDEX_PREFIX}#{index.gsub('/', '_')}"
|
856
|
+
wanted_indexes << index_name
|
857
|
+
label = index.split('/').first
|
858
|
+
property = index.split('/').last
|
859
|
+
query = "CREATE INDEX #{index_name} IF NOT EXISTS FOR (n:#{label}) ON (n.#{property})"
|
860
|
+
# STDERR.puts query
|
861
|
+
neo4j_query(query)
|
862
|
+
end
|
863
|
+
neo4j_query("SHOW ALL CONSTRAINTS").each do |row|
|
864
|
+
next unless row['name'].index(CONSTRAINT_INDEX_PREFIX) == 0
|
865
|
+
next if wanted_constraints.include?(row['name'])
|
866
|
+
query = "DROP CONSTRAINT #{row['name']}"
|
867
|
+
# STDERR.puts query
|
868
|
+
neo4j_query(query)
|
869
|
+
end
|
870
|
+
neo4j_query("SHOW ALL INDEXES").each do |row|
|
871
|
+
next unless row['name'].index(CONSTRAINT_INDEX_PREFIX) == 0
|
872
|
+
next if wanted_indexes.include?(row['name']) || wanted_constraints.include?(row['name'])
|
873
|
+
query = "DROP INDEX #{row['name']}"
|
874
|
+
# STDERR.puts query
|
875
|
+
neo4j_query(query)
|
876
|
+
end
|
877
|
+
end
|
833
878
|
end
|
834
879
|
|
835
880
|
def transaction(&block)
|
@@ -864,7 +909,7 @@ module Neo4jBolt
|
|
864
909
|
end
|
865
910
|
end
|
866
911
|
|
867
|
-
def dump_database(
|
912
|
+
def dump_database(io)
|
868
913
|
tr_id = {}
|
869
914
|
id = 0
|
870
915
|
neo4j_query("MATCH (n) RETURN n ORDER BY ID(n);") do |row|
|
@@ -874,7 +919,7 @@ module Neo4jBolt
|
|
874
919
|
:labels => row['n'].labels,
|
875
920
|
:properties => row['n']
|
876
921
|
}
|
877
|
-
|
922
|
+
io.puts "n #{node.to_json}"
|
878
923
|
id += 1
|
879
924
|
end
|
880
925
|
neo4j_query("MATCH ()-[r]->() RETURN r;") do |row|
|
@@ -884,14 +929,99 @@ module Neo4jBolt
|
|
884
929
|
:type => row['r'].type,
|
885
930
|
:properties => row['r']
|
886
931
|
}
|
887
|
-
|
932
|
+
io.puts "r #{rel.to_json}"
|
888
933
|
end
|
889
934
|
end
|
890
935
|
|
936
|
+
def load_database_dump(io, force_append: false)
|
937
|
+
unless force_append
|
938
|
+
transaction do
|
939
|
+
node_count = neo4j_query_expect_one('MATCH (n) RETURN COUNT(n) as count;')['count']
|
940
|
+
unless node_count == 0
|
941
|
+
raise "Error: There are nodes in this database, exiting now."
|
942
|
+
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
n_count = 0
|
946
|
+
r_count = 0
|
947
|
+
node_tr = {}
|
948
|
+
node_batch_by_label = {}
|
949
|
+
relationship_batch_by_type = {}
|
950
|
+
io.each_line do |line|
|
951
|
+
line.strip!
|
952
|
+
next if line.empty?
|
953
|
+
if line[0] == 'n'
|
954
|
+
line = line[2, line.size - 2]
|
955
|
+
node = JSON.parse(line)
|
956
|
+
label_key = node['labels'].sort.join('/')
|
957
|
+
node_batch_by_label[label_key] ||= []
|
958
|
+
node_batch_by_label[label_key] << node
|
959
|
+
elsif line[0] == 'r'
|
960
|
+
line = line[2, line.size - 2]
|
961
|
+
relationship = JSON.parse(line)
|
962
|
+
relationship_batch_by_type[relationship['type']] ||= []
|
963
|
+
relationship_batch_by_type[relationship['type']] << relationship
|
964
|
+
else
|
965
|
+
STDERR.puts "Invalid entry: #{line}"
|
966
|
+
exit(1)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
node_batch_by_label.each_pair do |label_key, batch|
|
970
|
+
while !batch.empty? do
|
971
|
+
slice = []
|
972
|
+
json_size = 0
|
973
|
+
while (!batch.empty?) && json_size < 0x20000 && slice.size < 256
|
974
|
+
x = batch.shift
|
975
|
+
slice << x
|
976
|
+
json_size += x.to_json.size
|
977
|
+
end
|
978
|
+
ids = neo4j_query(<<~END_OF_QUERY, {:properties => slice.map { |x| x['properties']}})
|
979
|
+
UNWIND $properties AS props
|
980
|
+
CREATE (n:#{slice.first['labels'].join(':')})
|
981
|
+
SET n = props
|
982
|
+
RETURN ID(n) AS id;
|
983
|
+
END_OF_QUERY
|
984
|
+
slice.each.with_index do |node, i|
|
985
|
+
node_tr[node['id']] = ids[i]['id']
|
986
|
+
end
|
987
|
+
n_count += slice.size
|
988
|
+
STDERR.print "\rLoaded #{n_count} nodes, #{r_count} relationships..."
|
989
|
+
end
|
990
|
+
end
|
991
|
+
relationship_batch_by_type.each_pair do |rel_type, batch|
|
992
|
+
batch.each_slice(256) do |slice|
|
993
|
+
slice.map! do |rel|
|
994
|
+
rel['from'] = node_tr[rel['from']]
|
995
|
+
rel['to'] = node_tr[rel['to']]
|
996
|
+
rel
|
997
|
+
end
|
998
|
+
count = neo4j_query_expect_one(<<~END_OF_QUERY, {:slice => slice})['count_r']
|
999
|
+
UNWIND $slice AS props
|
1000
|
+
MATCH (from), (to) WHERE ID(from) = props.from AND ID(to) = props.to
|
1001
|
+
CREATE (from)-[r:#{rel_type}]->(to)
|
1002
|
+
SET r = props.properties
|
1003
|
+
RETURN COUNT(r) AS count_r, COUNT(from) AS count_from, COUNT(to) AS count_to;
|
1004
|
+
END_OF_QUERY
|
1005
|
+
if count != slice.size
|
1006
|
+
raise "Ooops... expected #{slice.size} relationships, got #{count}."
|
1007
|
+
end
|
1008
|
+
r_count += slice.size
|
1009
|
+
STDERR.print "\rLoaded #{n_count} nodes, #{r_count} relationships..."
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
STDERR.puts
|
1014
|
+
end
|
1015
|
+
|
891
1016
|
def cleanup_neo4j
|
892
1017
|
if @bolt_socket
|
893
1018
|
@bolt_socket.disconnect()
|
894
1019
|
@bolt_socket = nil
|
895
1020
|
end
|
896
1021
|
end
|
1022
|
+
|
1023
|
+
def setup_constraints_and_indexes(constraints, indexes)
|
1024
|
+
@bolt_socket ||= BoltSocket.new()
|
1025
|
+
@bolt_socket.setup_constraints_and_indexes(constraints, indexes)
|
1026
|
+
end
|
897
1027
|
end
|