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

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.
@@ -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