neo4j-core 3.0.0.alpha.18 → 3.0.0.alpha.19

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,6 +23,10 @@ module Neo4j::Embedded
23
23
  Neo4j::Session.register(self)
24
24
  end
25
25
 
26
+ def db_type
27
+ :embedded_db
28
+ end
29
+
26
30
  def inspect
27
31
  "#{self.class} db_location: '#{@db_location}', running: #{running?}"
28
32
  end
@@ -41,6 +45,16 @@ module Neo4j::Embedded
41
45
  Java::OrgNeo4jTest::ImpermanentGraphDatabase
42
46
  end
43
47
 
48
+ def begin_tx
49
+ if Neo4j::Transaction.current
50
+ # Handle nested transaction "placebo transaction"
51
+ Neo4j::Transaction.current.push_nested!
52
+ else
53
+ Neo4j::Embedded::EmbeddedTransaction.new(@graph_db.begin_tx)
54
+ end
55
+ Neo4j::Transaction.current
56
+ end
57
+
44
58
  def close
45
59
  super
46
60
  shutdown
@@ -0,0 +1,145 @@
1
+ # Plugin
2
+
3
+ Neo4j::Session.register_db(:embedded_db) do |*args|
4
+ Neo4j::Embedded::EmbeddedSession.new(*args)
5
+ end
6
+
7
+
8
+ module Neo4j::Embedded
9
+ class EmbeddedSession < Neo4j::Session
10
+
11
+ class Error < StandardError
12
+ end
13
+
14
+ attr_reader :graph_db, :db_location
15
+ extend Forwardable
16
+ extend Neo4j::Core::TxMethods
17
+ def_delegator :@graph_db, :begin_tx
18
+
19
+
20
+ def initialize(db_location, config={})
21
+ @db_location = db_location
22
+ @auto_commit = !!config[:auto_commit]
23
+ Neo4j::Session.register(self)
24
+ @query_builder = Neo4j::Core::QueryBuilder.new
25
+ end
26
+
27
+ def start
28
+ raise Error.new("Embedded Neo4j db is already running") if running?
29
+ puts "Start embedded Neo4j db at #{db_location}"
30
+ factory = Java::OrgNeo4jGraphdbFactory::GraphDatabaseFactory.new
31
+ @graph_db = factory.newEmbeddedDatabase(db_location)
32
+ Neo4j::Session._notify_listeners(:session_available, self)
33
+ end
34
+
35
+ def factory_class
36
+ Java::OrgNeo4jGraphdbFactory::GraphDatabaseFactory
37
+ Java::OrgNeo4jTest::ImpermanentGraphDatabase
38
+ end
39
+
40
+ def close
41
+ super
42
+ shutdown
43
+ end
44
+
45
+ def shutdown
46
+ graph_db && graph_db.shutdown
47
+ @graph_db = nil
48
+ end
49
+
50
+ def running?
51
+ !!graph_db
52
+ end
53
+
54
+ def create_label(name)
55
+ EmbeddedLabel.new(self, name)
56
+ end
57
+
58
+ def load_node(neo_id)
59
+ _load_node(neo_id)
60
+ end
61
+ tx_methods :load_node
62
+
63
+ # Same as load but does not return the node as a wrapped Ruby object.
64
+ #
65
+ def _load_node(neo_id)
66
+ return nil if neo_id.nil?
67
+ @graph_db.get_node_by_id(neo_id.to_i)
68
+ rescue Java::OrgNeo4jGraphdb.NotFoundException
69
+ nil
70
+ end
71
+
72
+ def load_relationship(neo_id)
73
+ _load_relationship(neo_id)
74
+ end
75
+ tx_methods :load_relationship
76
+
77
+ def _load_relationship(neo_id)
78
+ return nil if neo_id.nil?
79
+ @graph_db.get_relationship_by_id(neo_id.to_i)
80
+ rescue Java::OrgNeo4jGraphdb.NotFoundException
81
+ nil
82
+ end
83
+
84
+ def query(*params)
85
+ query_hash = @query_builder.to_query_hash(params, :to_node)
86
+ cypher = @query_builder.to_cypher(query_hash)
87
+
88
+ result = _query(cypher, query_hash[:params])
89
+ if result.respond_to?(:error?) && result.error?
90
+ raise Neo4j::Session::CypherError.new(result.error_msg, result.error_code, result.error_status)
91
+ end
92
+
93
+ map_return_procs = @query_builder.to_map_return_procs(query_hash)
94
+ ResultWrapper.new(result, map_return_procs, cypher)
95
+ end
96
+
97
+ def find_all_nodes(label)
98
+ EmbeddedLabel.new(self, label).find_nodes
99
+ end
100
+
101
+ def find_nodes(label, key, value)
102
+ EmbeddedLabel.new(self, label).find_nodes(key,value)
103
+ end
104
+
105
+ # Performs a cypher query with given string.
106
+ # Remember that you should close the resource iterator.
107
+ # @param [String] q the cypher query as a String
108
+ # @return (see #query)
109
+ def _query(q, params={})
110
+ engine = Java::OrgNeo4jCypherJavacompat::ExecutionEngine.new(@graph_db)
111
+ engine.execute(q, Neo4j::Core::HashWithIndifferentAccess.new(params))
112
+ rescue Exception => e
113
+ raise Neo4j::Session::CypherError.new(e.message, e.class, 'cypher error')
114
+ end
115
+
116
+ def query_default_return(as)
117
+ " RETURN #{as}"
118
+ end
119
+
120
+ def _query_or_fail(q)
121
+ engine = Java::OrgNeo4jCypherJavacompat::ExecutionEngine.new(@graph_db)
122
+ engine.execute(q)
123
+ end
124
+
125
+ def search_result_to_enumerable(result)
126
+ result.map {|column| column['n'].wrapper}
127
+ end
128
+
129
+ def create_node(properties = nil, labels=[])
130
+ if labels.empty?
131
+ _java_node = graph_db.create_node
132
+ else
133
+ labels = EmbeddedLabel.as_java(labels)
134
+ _java_node = graph_db.create_node(labels)
135
+ end
136
+ properties.each_pair { |k, v| _java_node[k]=v } if properties
137
+ _java_node
138
+ end
139
+ tx_methods :create_node
140
+
141
+ end
142
+
143
+
144
+
145
+ end
@@ -0,0 +1,35 @@
1
+ module Neo4j::Embedded
2
+ class EmbeddedTransaction
3
+ attr_reader :root_tx
4
+ include Neo4j::Transaction::Instance
5
+
6
+ def initialize(root_tx)
7
+ @root_tx = root_tx
8
+ register_instance
9
+ end
10
+
11
+ def acquire_read_lock(entity)
12
+ @root_tx.acquire_read_lock(entity)
13
+ end
14
+
15
+ def acquire_write_lock(entity)
16
+ @root_tx.acquire_write_lock(entity)
17
+ end
18
+
19
+
20
+ def inspect
21
+ "EmbeddedTransaction [nested: #{@pushed_nested} failed?: #{failure?} active: #{Neo4j::Transaction.current == self}]"
22
+ end
23
+
24
+ def _delete_tx
25
+ @root_tx.failure
26
+ @root_tx.close
27
+ end
28
+
29
+ def _commit_tx
30
+ @root_tx.success
31
+ @root_tx.close
32
+ end
33
+
34
+ end
35
+ end
@@ -22,8 +22,8 @@ module Neo4j::Embedded::Property
22
22
  # * Values in the array must be of the same type.
23
23
  # * You can *not* delete or add one item in the array (e.g. person.phones.delete('123')) but instead you must create a new array instead.
24
24
  #
25
- # @param [String, Symbol] key of the property to set
26
- # @param [String,Fixnum,Float,true,false, Array] value to set
25
+ # @param [String, Symbol] k of the property to set
26
+ # @param [String,Fixnum,Float,true,false, Array] v to set
27
27
  def []=(k, v)
28
28
  to_java_property(k, v)
29
29
  end
@@ -61,6 +61,10 @@ module Neo4j::Embedded::Property
61
61
  get_id
62
62
  end
63
63
 
64
+ def refresh
65
+ # nothing is needed in the embedded db since we always asks the database
66
+ end
67
+
64
68
  private
65
69
 
66
70
  def to_ruby_property(key)
@@ -7,9 +7,9 @@ module Neo4j::Server
7
7
  @session = session
8
8
 
9
9
  @id = if value.is_a?(Hash)
10
- @response_hash = value
11
- @props = @response_hash['data']
12
- @response_hash['self'].match(/\d+$/)[0].to_i
10
+ hash = value['data']
11
+ @props = Hash[hash.map{ |k, v| [k.to_sym, v] }]
12
+ value['id'] # value['self'].match(/\d+$/)[0].to_i
13
13
  else
14
14
  value
15
15
  end
@@ -40,29 +40,37 @@ module Neo4j::Server
40
40
  if @props
41
41
  @props
42
42
  else
43
- props = @session._query_or_fail("START n=node(#{neo_id}) RETURN n", true)['data']
44
- props.keys.inject({}){|hash,key| hash[key.to_sym] = props[key]; hash}
43
+ hash = @session._query_entity_data("START n=node(#{neo_id}) RETURN n")
44
+ @props = Hash[hash['data'].map{ |k, v| [k.to_sym, v] }]
45
45
  end
46
46
  end
47
47
 
48
+ def refresh
49
+ @props = nil
50
+ end
51
+
48
52
  # (see Neo4j::Node#remove_property)
49
53
  def remove_property(key)
54
+ refresh
50
55
  @session._query_or_fail("START n=node(#{neo_id}) REMOVE n.`#{key}`")
51
56
  end
52
57
 
53
58
  # (see Neo4j::Node#set_property)
54
59
  def set_property(key,value)
60
+ refresh
55
61
  @session._query_or_fail("START n=node(#{neo_id}) SET n.`#{key}` = { value }", false, value: value)
56
62
  value
57
63
  end
58
64
 
59
65
  # (see Neo4j::Node#props=)
60
66
  def props=(properties)
67
+ refresh
61
68
  @session._query_or_fail("START n=node(#{neo_id}) SET n = { props }", false, {props: properties})
62
69
  properties
63
70
  end
64
71
 
65
72
  def remove_properties(properties)
73
+ refresh
66
74
  q = "START n=node(#{neo_id}) REMOVE " + properties.map do |k|
67
75
  "n.`#{k}`"
68
76
  end.join(', ')
@@ -71,6 +79,7 @@ module Neo4j::Server
71
79
 
72
80
  # (see Neo4j::Node#update_props)
73
81
  def update_props(properties)
82
+ refresh
74
83
  return if properties.empty?
75
84
 
76
85
  removed_keys = properties.keys.select{|k| properties[k].nil?}
@@ -86,7 +95,11 @@ module Neo4j::Server
86
95
 
87
96
  # (see Neo4j::Node#get_property)
88
97
  def get_property(key)
89
- @session._query_or_fail("START n=node(#{neo_id}) RETURN n.`#{key}`", true)
98
+ if @props
99
+ @props[key.to_sym]
100
+ else
101
+ @session._query_or_fail("START n=node(#{neo_id}) RETURN n.`#{key}`", true)
102
+ end
90
103
  end
91
104
 
92
105
  # (see Neo4j::Node#labels)
@@ -134,7 +147,7 @@ module Neo4j::Server
134
147
  response = @session._query("START n=node(#{neo_id}) RETURN ID(n)")
135
148
  if (!response.error?)
136
149
  return true
137
- elsif (response.error_status == 'EntityNotFoundException')
150
+ elsif (response.error_status =~ /EntityNotFound/)
138
151
  return false
139
152
  else
140
153
  response.raise_error
@@ -15,8 +15,7 @@ module Neo4j::Server
15
15
  @props = @response_hash['data']
16
16
  @start_node_neo_id = @response_hash['start'].match(/\d+$/)[0].to_i
17
17
  @end_node_neo_id = @response_hash['end'].match(/\d+$/)[0].to_i
18
-
19
- @response_hash['self'].match(/\d+$/)[0].to_i
18
+ @response_hash['id']
20
19
  else
21
20
  @rel_type = rel_type
22
21
 
@@ -44,6 +43,14 @@ module Neo4j::Server
44
43
  end
45
44
  end
46
45
 
46
+ def _start_node_id
47
+ @start_node_neo_id ||= get_node_id(:start)
48
+ end
49
+
50
+ def _end_node_id
51
+ @end_node_neo_id ||= get_node_id(:end)
52
+ end
53
+
47
54
  def _start_node
48
55
  load_resource
49
56
  id = resource_url_id(resource_url(:start))
@@ -56,6 +63,11 @@ module Neo4j::Server
56
63
  Neo4j::Node._load(id)
57
64
  end
58
65
 
66
+ def get_node_id(direction)
67
+ load_resource
68
+ resource_url_id(resource_url(direction))
69
+ end
70
+
59
71
  def get_property(key)
60
72
  id = neo_id
61
73
  @session._query_or_fail("START n=relationship(#{id}) RETURN n.`#{key}`", true)
@@ -76,8 +88,8 @@ module Neo4j::Server
76
88
  if @props
77
89
  @props
78
90
  else
79
- props = @session._query_or_fail("START n=relationship(#{neo_id}) RETURN n", true)['data']
80
- props.keys.inject({}){|hash,key| hash[key.to_sym] = props[key]; hash}
91
+ hash = @session._query_entity_data("START n=relationship(#{neo_id}) RETURN n")
92
+ @props = Hash[hash['data'].map{ |k, v| [k.to_sym, v] }]
81
93
  end
82
94
  end
83
95
 
@@ -19,7 +19,6 @@ module Neo4j::Server
19
19
  def_delegator :@response, :error_msg
20
20
  def_delegator :@response, :error_status
21
21
  def_delegator :@response, :error_code
22
- def_delegator :@response, :data
23
22
  def_delegator :@response, :columns
24
23
  def_delegator :@response, :struct
25
24
 
@@ -37,7 +36,7 @@ module Neo4j::Server
37
36
  end
38
37
 
39
38
  def each(&block)
40
- data.each do |row|
39
+ @response.each_data_row do |row|
41
40
  yield(row.each_with_index.each_with_object(struct.new) do |(value, i), result|
42
41
  result[columns[i].to_sym] = value
43
42
  end)
@@ -53,23 +52,26 @@ module Neo4j::Server
53
52
  Enumerator.new do |yielder|
54
53
  self.to_struct_enumeration(cypher).each do |row|
55
54
  yielder << row.each_pair.each_with_object(@struct.new) do |(column, value), result|
56
-
57
- result[column] = if value.is_a?(Hash)
58
- if value['labels']
59
- CypherNode.new(session, value).wrapper
60
- elsif value['type']
61
- CypherRelationship.new(session, value).wrapper
62
- else
63
- value
64
- end
65
- else
66
- value
67
- end
55
+ result[column] = map_row_value(value, session)
68
56
  end
69
57
  end
70
58
  end
71
59
  end
72
60
 
61
+ def map_row_value(value, session)
62
+ return value unless value.is_a?(Hash)
63
+
64
+ if value['labels']
65
+ add_entity_id(value)
66
+ CypherNode.new(session, value).wrapper
67
+ elsif value['type']
68
+ add_entity_id(value)
69
+ CypherRelationship.new(session, value).wrapper
70
+ else
71
+ value
72
+ end
73
+ end
74
+
73
75
  attr_reader :struct
74
76
 
75
77
  def initialize(response, uncommited = false)
@@ -78,14 +80,30 @@ module Neo4j::Server
78
80
  end
79
81
 
80
82
 
81
- def first_data
83
+ def entity_data(id=nil)
84
+ if uncommited?
85
+ data = @data.first['row'].first
86
+ data.is_a?(Hash) ? {'data' => data, 'id' => id} : data
87
+ else
88
+ data = @data[0][0]
89
+ data.is_a?(Hash) ? add_entity_id(data) : data
90
+ end
91
+ end
92
+
93
+ def first_data(id = nil)
82
94
  if uncommited?
83
- @data.first['row'].first
95
+ data = @data.first['row'].first
96
+ #data.is_a?(Hash) ? {'data' => data, 'id' => id} : data
84
97
  else
85
- @data[0][0]
98
+ data = @data[0][0]
99
+ data.is_a?(Hash) ? add_entity_id(data) : data
86
100
  end
87
101
  end
88
102
 
103
+ def add_entity_id(data)
104
+ data.merge!({'id' => data['self'].match(/\d+$/)[0].to_i})
105
+ end
106
+
89
107
  def error?
90
108
  !!@error
91
109
  end
@@ -98,6 +116,14 @@ module Neo4j::Server
98
116
  raise "Response code #{response.code}, expected #{code} for #{response.request.path}, #{response.body}" unless response.code == code
99
117
  end
100
118
 
119
+ def each_data_row
120
+ if uncommited?
121
+ data.each{|r| yield r['row']}
122
+ else
123
+ data.each{|r| yield r}
124
+ end
125
+ end
126
+
101
127
  def set_data(data, columns)
102
128
  @data = data
103
129
  @columns = columns