Class: Neo4j::Server::CypherNode

Inherits:
Node
  • Object
show all
Includes:
Core::ActiveEntity, Core::CypherTranslator, Resource
Defined in:
lib/neo4j-server/cypher_node.rb

Constant Summary

Constants included from Core::CypherTranslator

Core::CypherTranslator::SANITIZE_ESCAPED_REGEXP

Constants included from PropertyValidator

PropertyValidator::VALID_PROPERTY_VALUE_CLASSES

Instance Attribute Summary

Attributes included from Resource

#resource_data, #resource_url

Instance Method Summary collapse

Methods included from Core::ActiveEntity

#persisted?

Methods included from Core::CypherTranslator

#cypher_prop_list, #escape_quotes, #escape_value, #sanitize_escape_sequences, sanitized_column_names, translate_response

Methods included from Resource

#convert_from_json_value, #expect_response_code, #handle_response_error, #init_resource_data, #resource_headers, #resource_url_id, #response_exception, #wrap_resource

Methods inherited from Node

_load, #_rel, create, find_nodes, load

Methods included from PropertyContainer

#[], #[]=

Methods included from PropertyValidator

#valid_property?, #validate_property

Methods included from Node::Wrapper

#neo4j_obj, #wrapper

Methods included from EntityEquality

#==

Constructor Details

#initialize(session, value) ⇒ CypherNode

Returns a new instance of CypherNode.



7
8
9
10
11
12
13
14
15
16
17
# File 'lib/neo4j-server/cypher_node.rb', line 7

def initialize(session, value)
  @session = session

  @id = if value.is_a?(Hash)
    hash = value['data']
    @props = Hash[hash.map{ |k, v| [k.to_sym, v] }]
    value['id'] # value['self'].match(/\d+$/)[0].to_i
  else
    value
  end
end

Instance Method Details

#_cypher_label_list(labels) ⇒ Object



113
114
115
# File 'lib/neo4j-server/cypher_node.rb', line 113

def _cypher_label_list(labels)
  ':' + labels.map{|label| "`#{label}`"}.join(':')
end

#_java_nodeObject

TODO, needed by neo4j-cypher



28
29
30
# File 'lib/neo4j-server/cypher_node.rb', line 28

def _java_node
  self
end

#_map_result(r) ⇒ Object



209
210
211
# File 'lib/neo4j-server/cypher_node.rb', line 209

def _map_result(r)
  r.to_node_enumeration.map { |rel| rel.result }
end

#add_label(*labels) ⇒ Object



117
118
119
# File 'lib/neo4j-server/cypher_node.rb', line 117

def add_label(*labels)
  @session._query_or_fail("START n=node(#{neo_id}) SET n #{_cypher_label_list(labels)}")
end

#create_rel(type, other_node, props = nil) ⇒ Object

Creates a relationship of given type to other_node with optionally properties

Parameters:

  • type (Symbol)

    the type of the relation between the two nodes

  • other_node (Neo4j::Node)

    the other node

  • props (Hash<Symbol, Object>) (defaults to: nil)

    optionally properties for the created relationship



33
34
35
36
37
38
# File 'lib/neo4j-server/cypher_node.rb', line 33

def create_rel(type, other_node, props = nil)
  q = "START a=node(#{neo_id}), b=node(#{other_node.neo_id}) CREATE (a)-[r:`#{type}` #{cypher_prop_list(props)}]->(b) RETURN ID(r)"
  id = @session._query_or_fail(q, true)
  data_hash = { 'type' => type, 'data' => props, 'start' => self.neo_id.to_s, 'end' => other_node.neo_id.to_s, 'id' => id }
  CypherRelationship.new(@session, data_hash)
end

#delObject Also known as: delete, destroy

Deletes this node from the database



142
143
144
145
# File 'lib/neo4j-server/cypher_node.rb', line 142

def del
  @session._query_or_fail("START n = node(#{neo_id}) MATCH n-[r]-() DELETE r")
  @session._query_or_fail("START n = node(#{neo_id}) DELETE n")
end

#exist?Boolean

Returns true if the node exists.

Returns:

  • (Boolean)

    true if the node exists



151
152
153
154
155
156
157
158
159
160
# File 'lib/neo4j-server/cypher_node.rb', line 151

def exist?
  response = @session._query("START n=node(#{neo_id}) RETURN ID(n)")
  if (!response.error?)
    return true
  elsif (response.error_status =~ /EntityNotFound/)
    return false
  else
    response.raise_error
  end
end

#get_property(key) ⇒ Object

Directly get the property on the node (low level method, may need transaction)

Parameters:

  • key (Symbol, String)

Returns:

  • the value of the key



99
100
101
102
103
104
105
# File 'lib/neo4j-server/cypher_node.rb', line 99

def get_property(key)
  if @props
    @props[key.to_sym]
  else
    @session._query_or_fail("START n=node(#{neo_id}) RETURN n.`#{key}`", true)
  end
end

#inspectObject



23
24
25
# File 'lib/neo4j-server/cypher_node.rb', line 23

def inspect
  "CypherNode #{neo_id} (#{object_id})"
end

#labelsObject

Returns all the Neo4j labels for this node.

Returns:

  • all the Neo4j labels for this node



108
109
110
111
# File 'lib/neo4j-server/cypher_node.rb', line 108

def labels
  r = @session._query_or_fail("START n=node(#{neo_id}) RETURN labels(n) as labels", true)
  r.map(&:to_sym)
end

#match(clazz, returns, match = {}) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/neo4j-server/cypher_node.rb', line 195

def match(clazz, returns, match={})
  to_dir = {outgoing: ->(rel) {"-#{rel}->"},
            incoming: ->(rel) {"<-#{rel}-"},
            both:     ->(rel) {"-#{rel}-"} }

  cypher_rel = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
  between_id = match[:between] ? ",p=node(#{match[:between].neo_id}) " : ""
  dir_func = to_dir[match[:dir] || :both]
  cypher = "START n=node(#{neo_id}) #{between_id} MATCH (n)#{dir_func.call(cypher_rel)}(p) RETURN #{returns}"
  r = @session._query(cypher)
  r.raise_error if r.error?
  _map_result(r)
end

#neo_idObject



19
20
21
# File 'lib/neo4j-server/cypher_node.rb', line 19

def neo_id
  @id
end

#node(match = {}) ⇒ Object

Returns the only node of a given type and direction that is attached to this node, or nil. This is a convenience method that is used in the commonly occuring situation where a node has exactly zero or one relationships of a given type and direction to another node. Typically this invariant is maintained by the rest of the code: if at any time more than one such relationships exist, it is a fatal error that should generate an exception.

This method reflects that semantics and returns either:

  • nil if there are zero relationships of the given type and direction,

  • the relationship if there’s exactly one, or

  • throws an exception in all other cases.

This method should be used only in situations with an invariant as described above. In those situations, a “state-checking” method (e.g. #rel?) is not required, because this method behaves correctly “out of the box.”



164
165
166
167
168
# File 'lib/neo4j-server/cypher_node.rb', line 164

def node(match={})
  result = match(CypherNode, "p as result LIMIT 2", match)
  raise "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
  result.first
end

#nodes(match = {}) ⇒ Enumerable<Neo4j::Node>

This method is abstract.
Note:

it’s possible that the same node is returned more than once because of several relationship reaching to the same node, see #outgoing for alternative

Works like #rels method but instead returns the nodes. It does try to load a Ruby wrapper around each node

Parameters:

  • match (Hash) (defaults to: {})

    the options to create a message with.

Returns:

  • (Enumerable<Neo4j::Node>)

    an Enumeration of either Neo4j::Node objects or wrapped Neo4j::Node objects



184
185
186
# File 'lib/neo4j-server/cypher_node.rb', line 184

def nodes(match={})
  match(CypherNode, "p as result", match)
end

#propsHash<Symbol, Object>

Returns all properties of the node.

Returns:

  • (Hash<Symbol, Object>)

    all properties of the node



41
42
43
44
45
46
47
48
# File 'lib/neo4j-server/cypher_node.rb', line 41

def props
  if @props
    @props
  else
    hash = @session._query_entity_data("START n=node(#{neo_id}) RETURN n")
    @props = Hash[hash['data'].map{ |k, v| [k.to_sym, v] }]
  end
end

#props=(properties) ⇒ Object

replace all properties with new properties

Parameters:

  • properties (Hash<Symbol, Object>)

    a hash of properties the node should have



68
69
70
71
72
# File 'lib/neo4j-server/cypher_node.rb', line 68

def props=(properties)
  refresh
  @session._query_or_fail("START n=node(#{neo_id}) SET n = { props }", false, {props: properties})
  properties
end

#refreshObject



50
51
52
# File 'lib/neo4j-server/cypher_node.rb', line 50

def refresh
  @props = nil
end

#rel(match = {}) ⇒ Object

Same as #node but returns the relationship. Notice it may raise an exception if there are more then one relationship matching.



171
172
173
174
175
# File 'lib/neo4j-server/cypher_node.rb', line 171

def rel(match={})
  result = match(CypherRelationship, "r as result LIMIT 2", match)
  raise "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
  result.first
end

#rel?(match = {}) ⇒ Boolean

Returns true or false if there is one or more relationships

Returns:

  • (Boolean)


178
179
180
181
# File 'lib/neo4j-server/cypher_node.rb', line 178

def rel?(match={})
  result = match(CypherRelationship, "r as result", match)
  !!result.first
end

#rels(match = {dir: :both}) ⇒ Enumerable<Neo4j::Relationship>

Returns an enumeration of relationships. It always returns relationships of depth one.

Examples:

Return both incoming and outgoing relationships of any type

node_a.rels

All outgoing or incoming relationship of type friends

node_a.rels(type: :friends)

All outgoing relationships between me and another node of type friends

node_a.rels(type: :friends, dir: :outgoing, between: node_b)

Parameters:

  • match (Hash) (defaults to: {dir: :both})

    the options to create a message with.

Options Hash (match):

  • :dir (Symbol)

    dir the direction of the relationship, allowed values: :both, :incoming, :outgoing.

  • :type (Symbol)

    the type of relationship to navigate

  • :between (Symbol)

    return all the relationships between this and given node

Returns:



190
191
192
# File 'lib/neo4j-server/cypher_node.rb', line 190

def rels(match = {dir: :both})
  match(CypherRelationship, "r as result", match)
end

#remove_label(*labels) ⇒ Object



121
122
123
# File 'lib/neo4j-server/cypher_node.rb', line 121

def remove_label(*labels)
  @session._query_or_fail("START n=node(#{neo_id}) REMOVE n #{_cypher_label_list(labels)}")
end

#remove_properties(properties) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/neo4j-server/cypher_node.rb', line 74

def remove_properties(properties)
  refresh
  q = "START n=node(#{neo_id}) REMOVE " + properties.map do |k|
    "n.`#{k}`"
  end.join(', ')
  @session._query_or_fail(q)
end

#remove_property(key) ⇒ Object

Directly remove the property on the node (low level method, may need transaction)



55
56
57
58
# File 'lib/neo4j-server/cypher_node.rb', line 55

def remove_property(key)
  refresh
  @session._query_or_fail("START n=node(#{neo_id}) REMOVE n.`#{key}`")
end

#set_label(*label_names) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/neo4j-server/cypher_node.rb', line 125

def set_label(*label_names)
  label_as_symbols = label_names.map(&:to_sym)
  to_keep = labels & label_as_symbols
  to_remove = labels - to_keep
  to_set = label_as_symbols - to_keep

  # no change ?
  return if to_set.empty? && to_remove.empty?

  q = "START n=node(#{neo_id})"
  q += " SET n #{_cypher_label_list(to_set)}" unless to_set.empty?
  q += " REMOVE n #{_cypher_label_list(to_remove)}" unless to_remove.empty?

  @session._query_or_fail(q)
end

#set_property(key, value) ⇒ Object

Directly set the property on the node (low level method, may need transaction)

Parameters:

  • key (Symbol, String)
  • value

    see Neo4j::PropertyValidator::VALID_PROPERTY_VALUE_CLASSES for valid values



61
62
63
64
65
# File 'lib/neo4j-server/cypher_node.rb', line 61

def set_property(key,value)
  refresh
  @session._query_or_fail("START n=node(#{neo_id}) SET n.`#{key}` = { value }", false, value: value)
  value
end

#update_props(properties) ⇒ Object

Updates the properties, keeps old properties

Parameters:

  • properties (Hash<Symbol, Object>)

    hash of properties that should be updated on the node



83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/neo4j-server/cypher_node.rb', line 83

def update_props(properties)
  refresh
  return if properties.empty?

  removed_keys = properties.keys.select{|k| properties[k].nil?}
  remove_properties(removed_keys) unless removed_keys.empty?
  properties_to_set = properties.keys - removed_keys
  return if properties_to_set.empty?
  q = "START n=node(#{neo_id}) SET " + properties_to_set.map do |k|
    "n.`#{k}`= #{escape_value(properties[k])}"
  end.join(',')
  @session._query_or_fail(q)
  properties
end