neo4j_bolt 0.1.5 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a1bba2d869c0d9218879d2778a6b32fe896fb2dea9d8875e97a1e239c76af7e
4
- data.tar.gz: 24f0b6a563bd155dbee2b0181b464e750f71484d839c8ad8d225a26e2d0d2ab2
3
+ metadata.gz: 86471d4b049b4054a378f5084ae13dda181bd818a205ee4a67e6aa4bc5d0607f
4
+ data.tar.gz: b1e855e4689946026eaa7971acb730577a57973b02605c893916d15e2438b1f3
5
5
  SHA512:
6
- metadata.gz: 8f327d578de66a09226c46d850ec9c6e0a1f5f9592f39d16a1264abb378fcfe7470b287b84646832fb1ab52bd950a873ee6ef08524da2f7983c315ec1840f2dc
7
- data.tar.gz: 1a669a5f0968d23e9b944e26294f4d12a2ccbe6bca365dd8b06ad9e71e4f078e16cbeb40af77d57224ccc96b85bc08697cd2fccb95a11b1c02dd12e8f093dd5c
6
+ metadata.gz: e4e414cb24861f58ae67f2a5c92da46f720c861fd49131befa16a9c046ea557d1e31fb40130fe892aa91b38d0ec3de881d4da94de4fde4a5288ab0add379c33f
7
+ data.tar.gz: be8ed37b6ea0a7e358c25257eea3be90f78c2fd1342a4666bc244a5a379e241780a6df01d4005c4b26a4986a2fd24cd0a65f7569f4de19bcbf4a65517a33aa8a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- neo4j_bolt (0.1.5)
4
+ neo4j_bolt (0.1.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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
 
@@ -1,3 +1,3 @@
1
1
  module Neo4jBolt
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.7"
3
3
  end
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
@@ -47,6 +48,7 @@ module Neo4jBolt
47
48
  class Error < StandardError; end
48
49
  class IntegerOutOfRangeError < Error; end
49
50
  class SyntaxError < Error; end
51
+ class ConstraintValidationFailedError < Error; end
50
52
  class ExpectedOneResultError < Error; end
51
53
  class UnexpectedServerResponse < Error
52
54
  def initialize(token)
@@ -583,6 +585,8 @@ module Neo4jBolt
583
585
  def bolt_error(code, message)
584
586
  if code == 'Neo.ClientError.Statement.SyntaxError'
585
587
  SyntaxError.new(message)
588
+ elsif code == 'Neo.ClientError.Schema.ConstraintValidationFailed'
589
+ ConstraintValidationFailedError.new(message)
586
590
  else
587
591
  Error.new("#{code}\n#{message}")
588
592
  end
@@ -827,6 +831,50 @@ module Neo4jBolt
827
831
  end
828
832
  rows.first
829
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
830
878
  end
831
879
 
832
880
  def transaction(&block)
@@ -861,7 +909,7 @@ module Neo4jBolt
861
909
  end
862
910
  end
863
911
 
864
- def dump_database(&block)
912
+ def dump_database(io)
865
913
  tr_id = {}
866
914
  id = 0
867
915
  neo4j_query("MATCH (n) RETURN n ORDER BY ID(n);") do |row|
@@ -871,7 +919,7 @@ module Neo4jBolt
871
919
  :labels => row['n'].labels,
872
920
  :properties => row['n']
873
921
  }
874
- yield "n #{node.to_json}"
922
+ io.puts "n #{node.to_json}"
875
923
  id += 1
876
924
  end
877
925
  neo4j_query("MATCH ()-[r]->() RETURN r;") do |row|
@@ -881,14 +929,99 @@ module Neo4jBolt
881
929
  :type => row['r'].type,
882
930
  :properties => row['r']
883
931
  }
884
- yield "r #{rel.to_json}"
932
+ io.puts "r #{rel.to_json}"
885
933
  end
886
934
  end
887
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
+
888
1016
  def cleanup_neo4j
889
1017
  if @bolt_socket
890
1018
  @bolt_socket.disconnect()
891
1019
  @bolt_socket = nil
892
1020
  end
893
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
894
1027
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j_bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Specht