neo4j 1.0.0.beta.23-java → 1.0.0.beta.24-java

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.
Files changed (50) hide show
  1. data/lib/neo4j.rb +7 -0
  2. data/lib/neo4j/config.rb +2 -9
  3. data/lib/neo4j/event_handler.rb +51 -1
  4. data/lib/neo4j/functions/count.rb +33 -0
  5. data/lib/neo4j/functions/function.rb +72 -0
  6. data/lib/neo4j/functions/sum.rb +27 -0
  7. data/lib/neo4j/mapping/class_methods/property.rb +2 -1
  8. data/lib/neo4j/mapping/class_methods/rule.rb +30 -173
  9. data/lib/neo4j/mapping/node_mixin.rb +3 -1
  10. data/lib/neo4j/mapping/rule.rb +155 -0
  11. data/lib/neo4j/mapping/rule_node.rb +177 -0
  12. data/lib/neo4j/neo4j.rb +0 -4
  13. data/lib/neo4j/node.rb +12 -2
  14. data/lib/neo4j/node_relationship.rb +1 -1
  15. data/lib/neo4j/node_traverser.rb +11 -1
  16. data/lib/neo4j/rails/finders.rb +1 -1
  17. data/lib/neo4j/rails/persistence.rb +1 -0
  18. data/lib/neo4j/rails/relationships/relationships.rb +8 -5
  19. data/lib/neo4j/relationship.rb +12 -1
  20. data/lib/neo4j/type_converters.rb +121 -62
  21. data/lib/neo4j/version.rb +1 -1
  22. data/lib/tmp/neo4j/index/lucene-store.db +0 -0
  23. data/lib/tmp/neo4j/lucene-fulltext/lucene-store.db +0 -0
  24. data/lib/tmp/neo4j/lucene/lucene-store.db +0 -0
  25. data/lib/tmp/neo4j/messages.log +43 -176
  26. data/lib/tmp/neo4j/neostore +0 -0
  27. data/lib/tmp/neo4j/neostore.nodestore.db +0 -0
  28. data/lib/tmp/neo4j/neostore.nodestore.db.id +0 -0
  29. data/lib/tmp/neo4j/neostore.propertystore.db +0 -0
  30. data/lib/tmp/neo4j/neostore.propertystore.db.id +0 -0
  31. data/lib/tmp/neo4j/neostore.propertystore.db.index.keys +0 -0
  32. data/lib/tmp/neo4j/neostore.propertystore.db.index.keys.id +0 -0
  33. data/lib/tmp/neo4j/neostore.propertystore.db.strings +0 -0
  34. data/lib/tmp/neo4j/neostore.propertystore.db.strings.id +0 -0
  35. data/lib/tmp/neo4j/neostore.relationshipstore.db +0 -0
  36. data/lib/tmp/neo4j/neostore.relationshipstore.db.id +0 -0
  37. data/lib/tmp/neo4j/neostore.relationshiptypestore.db +0 -0
  38. data/lib/tmp/neo4j/neostore.relationshiptypestore.db.id +0 -0
  39. data/lib/tmp/neo4j/neostore.relationshiptypestore.db.names +0 -0
  40. data/lib/tmp/neo4j/neostore.relationshiptypestore.db.names.id +0 -0
  41. data/lib/tmp/neo4j/tm_tx_log.1 +0 -0
  42. metadata +8 -11
  43. data/lib/tmp/neo4j/index.db +0 -0
  44. data/lib/tmp/neo4j/index/lucene/node/Person-exact/_0.cfs +0 -0
  45. data/lib/tmp/neo4j/index/lucene/node/Person-exact/_1.cfs +0 -0
  46. data/lib/tmp/neo4j/index/lucene/node/Person-exact/segments.gen +0 -0
  47. data/lib/tmp/neo4j/index/lucene/node/Person-exact/segments_3 +0 -0
  48. data/lib/tmp/neo4j/index/lucene/node/Thing-exact/_0.cfs +0 -0
  49. data/lib/tmp/neo4j/index/lucene/node/Thing-exact/segments.gen +0 -0
  50. data/lib/tmp/neo4j/index/lucene/node/Thing-exact/segments_2 +0 -0
@@ -29,8 +29,10 @@ module Neo4j::Mapping
29
29
  module NodeMixin
30
30
  include Neo4j::Index
31
31
 
32
+ include Neo4j::Functions
33
+
32
34
  delegate :[]=, :[], :property?, :props, :attributes, :update, :neo_id, :id, :rels, :rel?, :to_param, :getId,
33
- :rel, :del, :list?, :print, :print_sub, :outgoing, :incoming, :both,
35
+ :rel, :del, :list?, :print, :print_sub, :outgoing, :incoming, :both, :get_property, :set_property,
34
36
  :equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship, :_rels, :rel,
35
37
  :to => :@_java_node, :allow_nil => true
36
38
 
@@ -0,0 +1,155 @@
1
+ module Neo4j::Mapping
2
+
3
+
4
+ # Holds all defined rules and trigger them when an event is received.
5
+ #
6
+ # See Neo4j::Mapping::ClassMethods::Rule
7
+ #
8
+ class Rule #:nodoc:
9
+
10
+ attr_reader :rule_name, :filter, :triggers, :functions
11
+
12
+ def initialize(rule_name, props, &block)
13
+ @rule_name = rule_name
14
+ @triggers = props[:triggers]
15
+ @functions = props[:functions]
16
+ @triggers = [@triggers] if @triggers && !@triggers.respond_to?(:each)
17
+ @functions = [@functions] if @functions && !@functions.respond_to?(:each)
18
+ @filter = block.nil? ? Proc.new { |*| true } : block
19
+ end
20
+
21
+ def to_s
22
+ "Rule #{rule_name} props=#{props.inspect}"
23
+ end
24
+
25
+ def find_function(function_name, function_id)
26
+ function_id = function_id.to_s
27
+ @functions && @functions.find{|f| f.function_id == function_id && f.class.function_name == function_name}
28
+ end
29
+
30
+ # Reconstruct the properties given when created this rule
31
+ # Needed when inheriting a rule and we want to duplicate a rule
32
+ def props
33
+ props = {}
34
+ props[:triggers] = @triggers if @triggers
35
+ props[:functions] = @functions if @functions
36
+ props
37
+ end
38
+
39
+ def functions_for(property)
40
+ @functions && @functions.find_all { |f| f.calculate?(property) }
41
+ end
42
+
43
+ def execute_filter(node)
44
+ if @filter.arity != 1
45
+ node.wrapper.instance_eval(&@filter)
46
+ else
47
+ @filter.call(node)
48
+ end
49
+ end
50
+
51
+ # ------------------------------------------------------------------------------------------------------------------
52
+ # Class Methods
53
+ # ------------------------------------------------------------------------------------------------------------------
54
+
55
+ class << self
56
+ def add(clazz, rule_name, props, &block)
57
+ @rule_nodes ||= {}
58
+ @rule_nodes[clazz.to_s] ||= RuleNode.new(clazz)
59
+ rule_node = @rule_nodes[clazz.to_s]
60
+ rule_node.remove_rule(rule_name) # remove any previously inherited rules
61
+ rule_node.add_rule(rule_name, props, &block)
62
+ end
63
+
64
+ def rule_names_for(clazz)
65
+ rule_node = rule_node_for(clazz)
66
+ rule_node.rules.map { |rule| rule.rule_name }
67
+ end
68
+
69
+ def rule_node_for(clazz)
70
+ @rule_nodes && @rule_nodes[clazz.to_s]
71
+ end
72
+
73
+ def inherit(parent_class, subclass)
74
+ # copy all the rules
75
+ if rule_node = rule_node_for(parent_class)
76
+ rule_node.inherit(subclass)
77
+ end
78
+ end
79
+
80
+ def delete(clazz)
81
+ if rule_node = rule_node_for(clazz)
82
+ rule_node.delete_node
83
+ end
84
+ end
85
+
86
+ def trigger?(node)
87
+ classname = node[:_classname]
88
+ @rule_nodes && classname && rule_node_for(classname)
89
+ end
90
+
91
+ def trigger_rules(node, *changes)
92
+ classname = node[:_classname]
93
+ rule_node = rule_node_for(classname)
94
+ rule_node.execute_rules(node, *changes)
95
+
96
+ # recursively add relationships for all the parent classes with rules that also pass for this node
97
+ if (clazz = eval("#{classname}.superclass")) && clazz.include?(Neo4j::NodeMixin)
98
+ rule_node = rule_node_for(clazz)
99
+ rule_node && rule_node.execute_rules(node, *changes)
100
+ end
101
+ end
102
+
103
+
104
+ # ----------------------------------------------------------------------------------------------------------------
105
+ # Event handling methods
106
+ # ----------------------------------------------------------------------------------------------------------------
107
+
108
+ def on_relationship_created(rel, *)
109
+ trigger_start_node = trigger?(rel._start_node)
110
+ trigger_end_node = trigger?(rel._end_node)
111
+ trigger_rules(rel._start_node) if trigger_start_node
112
+ trigger_rules(rel._end_node) if trigger_end_node
113
+ end
114
+
115
+ def on_property_changed(node, *changes)
116
+ trigger_rules(node, *changes) if trigger?(node)
117
+ end
118
+
119
+ def on_node_deleted(node, old_properties, data)
120
+ # have we deleted a rule node ?
121
+ del_rule_node = @rule_nodes && @rule_nodes.values.find{|rn| rn.rule_node?(node)}
122
+ del_rule_node && del_rule_node.clear_rule_node
123
+ return if del_rule_node
124
+
125
+ # do we have prop_aggregations for this
126
+ clazz = old_properties['_classname']
127
+ rule_node = rule_node_for(clazz)
128
+ return if rule_node.nil?
129
+
130
+ id = node.getId
131
+ rule_node.rules.each do |rule|
132
+ next if rule.functions.nil?
133
+ rule_name = rule.rule_name.to_s
134
+
135
+ # is the rule node deleted ?
136
+ deleted_rule_node = data.deletedNodes.find{|n| n == rule_node.rule_node}
137
+ next if deleted_rule_node
138
+
139
+ rule.functions.each do |function|
140
+ next unless data.deletedRelationships.find do |r|
141
+ r.getEndNode().getId() == id && r.rel_type == rule_name
142
+ end
143
+ previous_value = old_properties[function.function_id]
144
+ function.delete(rule_name, rule_node.rule_node, previous_value) if previous_value
145
+ end if rule.functions
146
+ end
147
+ end
148
+
149
+ def on_neo4j_started(*)
150
+ @rule_nodes.each_value { |rule_node| rule_node.on_neo4j_started } if @rule_nodes
151
+ end
152
+
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,177 @@
1
+ module Neo4j::Mapping
2
+
3
+
4
+ class RuleNode
5
+ attr_reader :rules
6
+
7
+ def initialize(clazz)
8
+ @clazz = clazz
9
+ @rules = []
10
+ end
11
+
12
+ def to_s
13
+ "RuleNode #{@clazz}, node #{rule_node} #rules: #{@rules.size}"
14
+ end
15
+
16
+ def node_exist?
17
+ !Neo4j.ref_node.rel?(@clazz)
18
+ end
19
+
20
+ def create_node
21
+ Neo4j::Transaction.run do
22
+ node = Neo4j::Node.new
23
+ Neo4j.ref_node.outgoing(@clazz) << node
24
+ node
25
+ end
26
+ end
27
+
28
+ def inherit(subclass)
29
+ @rules.each do |rule|
30
+ subclass.rule rule.rule_name, rule.props, &rule.filter
31
+ end
32
+ end
33
+
34
+ def delete_node
35
+ if Neo4j.ref_node.rel?(@clazz)
36
+ Neo4j.ref_node.outgoing(@clazz).each { |n| n.del }
37
+ end
38
+ clear_rule_node
39
+ end
40
+
41
+ def find_node
42
+ Neo4j.ref_node.rel?(@clazz.to_s) && Neo4j.ref_node._rel(:outgoing, @clazz.to_s)._end_node
43
+ end
44
+
45
+ def on_neo4j_started
46
+ # initialize the rule node when neo4j starts
47
+ @rule_node = find_node || create_node
48
+ end
49
+
50
+ def rule_node
51
+ @rule_node ||= find_node || create_node
52
+ end
53
+
54
+ def rule_node?(node)
55
+ @rule_node == node
56
+ end
57
+
58
+ def clear_rule_node
59
+ @rule_node = nil
60
+ end
61
+
62
+ def rule_names
63
+ @rules.map { |r| r.rule_name }
64
+ end
65
+
66
+ def find_rule(rule_name)
67
+ @rules.find { |rule| rule.rule_name == rule_name }
68
+ end
69
+
70
+ def add_rule(rule_name, props, &block)
71
+ @rules << (rule = Rule.new(rule_name, props, &block))
72
+ rule
73
+ end
74
+
75
+ def remove_rule(rule_name)
76
+ r = find_rule(rule_name)
77
+ r && @rules.delete(r)
78
+ end
79
+
80
+ # Return a traversal object with methods for each rule and function.
81
+ # E.g. Person.all.old or Person.all.sum(:age)
82
+ def traversal(rule_name)
83
+ # define method on the traversal
84
+ traversal = rule_node.outgoing(rule_name)
85
+ @rules.each do |rule|
86
+ traversal.filter_method(rule.rule_name) do |path|
87
+ path.end_node.rel?(rule.rule_name, :incoming)
88
+ end
89
+ rule.functions && rule.functions.each do |func|
90
+ traversal.functions_method(func, self, rule_name)
91
+ end
92
+ end
93
+ traversal
94
+ end
95
+
96
+ def find_function(rule_name, function_name, function_id)
97
+ rule = find_rule(rule_name)
98
+ rule.find_function(function_name, function_id)
99
+ end
100
+
101
+ def execute_rules(node, *changes)
102
+ @rules.each do |rule|
103
+ execute_rule(rule, node, *changes)
104
+ execute_other_rules(rule, node)
105
+ end
106
+ end
107
+
108
+ def execute_other_rules(rule, node)
109
+ rule.triggers && rule.triggers.each do |rel_type|
110
+ node.incoming(rel_type).each { |n| n.trigger_rules }
111
+ end
112
+ end
113
+
114
+ def execute_rule(rule, node, *changes)
115
+ if rule.execute_filter(node)
116
+ if connected?(rule.rule_name, node)
117
+ # it was already connected - the node is in the same rule group but a property has changed
118
+ execute_update_functions(rule, *changes)
119
+ else
120
+ # the node has changed or is in a new rule group
121
+ connect(rule.rule_name, node)
122
+ execute_add_functions(rule, *changes)
123
+ end
124
+ else
125
+ if break_connection(rule.rule_name, node)
126
+ # the node has been removed from a rule group
127
+ execute_delete_functions(rule, *changes)
128
+ end
129
+ end
130
+ end
131
+
132
+ def execute_update_functions(rule, *changes)
133
+ if functions = find_functions_for_changes(rule, *changes)
134
+ functions && functions.each { |f| f.update(rule.rule_name, rule_node, changes[1], changes[2]) }
135
+ end
136
+ end
137
+
138
+ def execute_add_functions(rule, *changes)
139
+ if functions = find_functions_for_changes(rule, *changes)
140
+ functions && functions.each { |f| f.add(rule.rule_name, rule_node, changes[2]) }
141
+ end
142
+ end
143
+
144
+ def execute_delete_functions(rule, *changes)
145
+ if functions = find_functions_for_changes(rule, *changes)
146
+ functions.each { |f| f.delete(rule.rule_name, rule_node, changes[1]) }
147
+ end
148
+ end
149
+
150
+ def find_functions_for_changes(rule, *changes)
151
+ # changes = [property, old_value, new_value]
152
+ !changes.empty? && rule.functions_for(changes[0])
153
+ end
154
+
155
+ # work out if two nodes are connected by a particular relationship
156
+ # uses the end_node to start with because it's more likely to have less relationships to go through
157
+ # (just the number of superclasses it has really)
158
+ def connected?(rule_name, end_node)
159
+ end_node.incoming(rule_name).find { |n| n == rule_node }
160
+ end
161
+
162
+ def connect(rule_name, end_node)
163
+ rule_node.outgoing(rule_name) << end_node
164
+ end
165
+
166
+ # sever a direct one-to-one relationship if it exists
167
+ def break_connection(rule_name, end_node)
168
+ rel = end_node._rels(:incoming, rule_name).find { |r| r._start_node == rule_node }
169
+ rel && rel.del
170
+ !rel.nil?
171
+ end
172
+
173
+ end
174
+
175
+
176
+ end
177
+
data/lib/neo4j/neo4j.rb CHANGED
@@ -91,9 +91,5 @@ module Neo4j
91
91
  this_db.event_handler
92
92
  end
93
93
 
94
- # Ruby to Java type converters
95
- def converters
96
- Neo4j::Config[:converters] || {}
97
- end
98
94
  end
99
95
  end
data/lib/neo4j/node.rb CHANGED
@@ -9,9 +9,19 @@ module Neo4j
9
9
  include Neo4j::Index
10
10
 
11
11
  # Delete the node and all its relationship
12
+ #
13
+ # ==== Returns
14
+ # true :: if the node was deleted
15
+ # false :: if node was not deleted, maybe it has already been deleted
16
+ #
12
17
  def del
13
18
  rels.each {|r| r.del}
14
- delete
19
+ begin
20
+ delete
21
+ true
22
+ rescue
23
+ false
24
+ end
15
25
  end
16
26
 
17
27
  # returns true if the node exists in the database
@@ -118,7 +128,7 @@ module Neo4j
118
128
  # Same as load but does not return the node as a wrapped Ruby object.
119
129
  #
120
130
  def _load(node_id, db)
121
- return nil if node_id.nil?
131
+ return nil if node_id.nil?
122
132
  db.graph.get_node_by_id(node_id.to_i)
123
133
  rescue java.lang.IllegalStateException
124
134
  nil # the node has been deleted
@@ -116,7 +116,7 @@ module Neo4j
116
116
  java_types = types.inject([]) { |result, type| result << type_to_java(type) }.to_java(:'org.neo4j.graphdb.RelationshipType')
117
117
  get_relationships(java_types)
118
118
  elsif types.size == 1
119
- get_relationships(type_to_java(types[0], dir_to_java(dir)))
119
+ get_relationships(type_to_java(types[0]), dir_to_java(dir))
120
120
  elsif dir == :both
121
121
  get_relationships(dir_to_java(dir))
122
122
  else
@@ -71,7 +71,7 @@ module Neo4j
71
71
  break if i >= to
72
72
  end
73
73
  pager.replace res
74
- pager.total_entries ||= size # TODO, this could be very slow to do
74
+ pager.total_entries ||= count
75
75
  end
76
76
 
77
77
  def <<(other_node)
@@ -118,6 +118,16 @@ module Neo4j
118
118
  self
119
119
  end
120
120
 
121
+ def functions_method(func, rule_node, rule_name)
122
+ singelton = class << self; self; end
123
+ singelton.send(:define_method, func.class.function_name) do |*args|
124
+ function_id = args.empty? ? "_classname" : args[0]
125
+ function = rule_node.find_function(rule_name, func.class.function_name, function_id)
126
+ function.value(rule_node.rule_node, rule_name)
127
+ end
128
+ self
129
+ end
130
+
121
131
  def prune(&block)
122
132
  @td = @td.prune(PruneEvaluator.new(block))
123
133
  self
@@ -4,7 +4,7 @@ module Neo4j
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- rule :_all
7
+ rule :_all, :functions => Neo4j::Functions::Count.new
8
8
  end
9
9
 
10
10
  module ClassMethods
@@ -138,6 +138,7 @@ module Neo4j
138
138
 
139
139
  def update
140
140
  write_changed_attributes
141
+ write_changed_relationships
141
142
  clear_changes
142
143
  clear_relationships
143
144
  true
@@ -3,7 +3,7 @@ module Neo4j
3
3
  module Relationships
4
4
 
5
5
  # TODO, reuse for incoming relationships ?
6
- class OutgoingRelationship
6
+ class OutgoingRelationship #:nodoc:
7
7
  include Enumerable
8
8
 
9
9
  def initialize(from_node, mapper)
@@ -22,17 +22,17 @@ module Neo4j
22
22
  end
23
23
 
24
24
 
25
- def write_changed_relationships
25
+ def write_changed_relationships #:nodoc:
26
26
  @relationships.each_value do |mapper|
27
27
  mapper.persist
28
28
  end
29
29
  end
30
30
 
31
- def valid_relationships?
31
+ def valid_relationships? #:nodoc:
32
32
  !@relationships.values.find {|mapper| !mapper.valid?}
33
33
  end
34
34
 
35
- def _decl_rels_for(type)
35
+ def _decl_rels_for(type) #:nodoc:
36
36
  dsl = super
37
37
  if false && persisted?
38
38
  dsl
@@ -42,11 +42,14 @@ module Neo4j
42
42
  end
43
43
 
44
44
 
45
- def clear_relationships
45
+ def clear_relationships #:nodoc:
46
46
  @relationships = {}
47
47
  end
48
48
 
49
49
 
50
+ # See, Neo4j::NodeRelationship#outgoing
51
+ # Creates or traverse relationships in memory without communicating with the neo4j database.
52
+ #
50
53
  def outgoing(rel_type)
51
54
  if persisted?
52
55
  super