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

Sign up to get free protection for your applications and to get access to all the features.
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
data/lib/neo4j.rb CHANGED
@@ -23,6 +23,11 @@ require 'neo4j/config'
23
23
  require 'neo4j/database'
24
24
  require 'neo4j/neo4j'
25
25
 
26
+ require "neo4j/functions/function"
27
+ require "neo4j/functions/count"
28
+ require "neo4j/functions/sum"
29
+
30
+
26
31
  require 'neo4j/index/index'
27
32
  require 'neo4j/index/class_methods'
28
33
  require 'neo4j/index/indexer_registry'
@@ -46,6 +51,8 @@ require 'neo4j/mapping/has_n'
46
51
  require 'neo4j/mapping/has_list'
47
52
  require 'neo4j/mapping/node_mixin'
48
53
  require 'neo4j/mapping/relationship_mixin'
54
+ require 'neo4j/mapping/rule'
55
+ require 'neo4j/mapping/rule_node'
49
56
  require 'neo4j/node_mixin'
50
57
  require 'neo4j/relationship_mixin'
51
58
  require 'neo4j/mapping/class_methods/rule'
data/lib/neo4j/config.rb CHANGED
@@ -13,7 +13,6 @@ module Neo4j
13
13
  # <tt>:storage_path</tt>:: default <tt>tmp/neo4j</tt> where the database is stored
14
14
  # <tt>:timestamps</tt>:: default <tt>true</tt> for Rails Neo4j::Model - if timestamps should be used when saving the model
15
15
  # <tt>:lucene</tt>:: default hash keys: <tt>:fulltext</tt>, <tt>:exact</tt> configuration how the lucene index is stored
16
- # <tt>:converters</tt>:: defines which converters should be used before writing and reading to neo4j, see Neo4j::TypeConverters
17
16
  #
18
17
  class Config
19
18
  # This code is copied from merb-core/config.rb.
@@ -27,12 +26,6 @@ module Neo4j
27
26
  :storage_path => 'tmp/neo4j',
28
27
  :timestamps => true,
29
28
 
30
- # TODO: Just pickup all converter classes that are in the Neo4j::TypeConverters module?
31
- :converters => { Date => Neo4j::TypeConverters::DateConverter,
32
- DateTime => Neo4j::TypeConverters::DateTimeConverter,
33
- Time => Neo4j::TypeConverters::TimeConverter
34
- },
35
-
36
29
  :lucene => {
37
30
  :fulltext => {"provider" => "lucene", "type" => "fulltext" },
38
31
  :exact => {"provider" => "lucene", "type" => "exact" }}
@@ -117,10 +110,10 @@ module Neo4j
117
110
  @configuration.fetch(key, default)
118
111
  end
119
112
 
120
- # Sets up the configuration
113
+ # Sets up the configuration to use the default.
121
114
  #
122
115
  # ==== Returns
123
- # The configuration as a hash.
116
+ # The a new configuration using default values as a hash.
124
117
  #
125
118
  def setup()
126
119
  @configuration = {}
@@ -13,7 +13,57 @@ module Neo4j
13
13
  # * <tt>on_property_changed</tt>
14
14
  # * <tt>on_rel_property_changed</tt>
15
15
  #
16
- # === Usage
16
+ # ==== on_neo4j_started(db)
17
+ #
18
+ # Called when the neo4j engine starts.
19
+ # Notice that the neo4j will be started automatically when the first neo4j operation is performed.
20
+ # You can also start Neo4j: <tt>Neo4j.start</tt>
21
+ #
22
+ # * <tt>db</tt> :: the Neo4j::Database instance
23
+ #
24
+ # ==== on_neo4j_shutdown(db)
25
+ #
26
+ # Called when the neo4j engine shutdown. You don't need to call <tt>Neo4j.shutdown</tt> since
27
+ # the it will automatically be shutdown when the application exits (using the at_exit ruby hook).
28
+ #
29
+ # * <tt>db</tt> :: the Neo4j::Database instance
30
+ #
31
+ # ==== on_node_created(node)
32
+ #
33
+ # * <tt>node</tt> :: the node that was created
34
+ #
35
+ # ==== on_node_deleted(node, old_props, tx_data)
36
+ #
37
+ # * <tt>node</tt> :: the node that was deleted
38
+ # * <tt>old_props</tt> :: a hash of the old properties this node had
39
+ # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
40
+ #
41
+ # ==== on_relationship_created(rel, tx_data)
42
+ #
43
+ # * <tt>rel</tt> :: the relationship that was created
44
+ # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
45
+ #
46
+ # ==== on_relationship_deleted(rel, old_props, tx_data)
47
+ #
48
+ # * <tt>rel</tt> :: the relationship that was created
49
+ # * <tt>old_props</tt> :: a hash of the old properties this relationship had
50
+ # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
51
+ #
52
+ # ==== on_property_changed(node, key, old_value, new_value)
53
+ #
54
+ # * <tt>node</tt> :: the node
55
+ # * <tt>key</tt> :: the name of the property that was changed (String)
56
+ # * <tt>old_value</tt> :: old value of the property
57
+ # * <tt>new_value</tt> :: new value of the property
58
+ #
59
+ # ==== on_rel_property_changed(rel, key, old_value, new_value)
60
+ #
61
+ # * <tt>rel</tt> :: the node that was created
62
+ # * <tt>key</tt> :: the name of the property that was changed (String)
63
+ # * <tt>old_value</tt> :: old value of the property
64
+ # * <tt>new_value</tt> :: new value of the property
65
+ #
66
+ # == Usage
17
67
  #
18
68
  # class MyListener
19
69
  # def on_node_deleted(node, old_props, tx_data)
@@ -0,0 +1,33 @@
1
+ module Neo4j
2
+ module Functions
3
+ class Count < Function
4
+ def initialize
5
+ @property = '_classname'
6
+ end
7
+
8
+ def calculate?(changed_property)
9
+ true
10
+ end
11
+
12
+ def delete(rule_name, rule_node, old_value)
13
+ key = rule_node_property(rule_name)
14
+ rule_node[key] ||= 0
15
+ rule_node[key] -= 1
16
+ end
17
+
18
+ def add(rule_name, rule_node, new_value)
19
+ key = rule_node_property(rule_name)
20
+ rule_node[key] ||= 0
21
+ rule_node[key] += 1
22
+ end
23
+
24
+ def update(*)
25
+ # we are only counting, not interested in property changes
26
+ end
27
+
28
+ def self.function_name
29
+ :count
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+ module Neo4j
2
+ module Functions
3
+
4
+ # The base class of rule functions.
5
+ #
6
+ # You are expected to at least implement two methods:
7
+ # * update :: update the rule node value of this function
8
+ # * function_name :: the name of this function, the name of the generated method - A class method !
9
+ #
10
+ class Function
11
+
12
+ # Initialize the the function with a property which is usually the same as the function identity.
13
+ # See the #calculate? method how this property is used.
14
+ #
15
+ def initialize(property)
16
+ @property = property.to_s
17
+ end
18
+
19
+ def to_s
20
+ "Function #{self.class.function_name} function_id: #{function_id}"
21
+ end
22
+
23
+ # Decides if the function should be called are not
24
+ #
25
+ def calculate?(changed_property)
26
+ @property == changed_property
27
+ end
28
+
29
+
30
+ # The identity of the function.
31
+ # Used to identify function.
32
+ #
33
+ # ==== Example
34
+ # Person.sum(:young, :age)
35
+ #
36
+ # In the example above the property :age is the used to identify which function will be called
37
+ # since there could be several sum method. In the example we want use the sum method that uses the :age property.
38
+ #
39
+ def function_id
40
+ @property
41
+ end
42
+
43
+ # The value of the rule
44
+ def value(rule_node, rule_name)
45
+ key = rule_node_property(rule_name)
46
+ rule_node[key] || 0
47
+ end
48
+
49
+ # Called when a node is removed from a rule group
50
+ # Default is calling update method which is expected to be implemented in a subclass
51
+ def delete(rule_name, rule_node, old_value)
52
+ update(rule_name, rule_node, old_value, nil)
53
+ end
54
+
55
+ # Called when a node is added to a rule group
56
+ # Default is calling update method which is expected to be implemented in a subclass
57
+ def add(rule_name, rule_node, new_value)
58
+ update(rule_name, rule_node, nil, new_value)
59
+ end
60
+
61
+ # the name of the property that holds the value of the function
62
+ def rule_node_property(rule_name)
63
+ self.class.rule_node_property(self.class.function_name, rule_name, @property)
64
+ end
65
+
66
+ def self.rule_node_property(function_name, rule_name, prop)
67
+ "_#{function_name}_#{rule_name}_#{prop}"
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ module Neo4j
2
+ module Functions
3
+
4
+ class Sum < Function
5
+ # Updates the function's value.
6
+ # Called after the transactions commits and a property has been changed on a node.
7
+ #
8
+ # ==== Arguments
9
+ # * rule_name :: the name of the rule group
10
+ # * rule_node :: the node which contains the value of this function
11
+ # * old_value new value :: the changed value of the property (when the transaction commits)
12
+ def update(rule_name, rule_node, old_value, new_value)
13
+ key = rule_node_property(rule_name)
14
+ rule_node[key] ||= 0
15
+ old_value ||= 0
16
+ new_value ||= 0
17
+ rule_node[key] += new_value - old_value
18
+ end
19
+
20
+ def self.function_name
21
+ :sum
22
+ end
23
+ end
24
+
25
+
26
+ end
27
+ end
@@ -35,7 +35,8 @@ module Neo4j::Mapping
35
35
  # property :since, :type => DateTime # will be converted into a fixnum
36
36
  # end
37
37
  #
38
- # You can write your own converter and register it in the Neo4j::Config.
38
+ # You can write your own converter by writing a class that respond to :convert?, :to_ruby and
39
+ # :to_java in the Neo4j::TypeConverters module.
39
40
  #
40
41
  def property(*props)
41
42
  if props.size == 2 and props[1].kind_of?(Hash)
@@ -1,154 +1,5 @@
1
1
  module Neo4j::Mapping
2
2
  module ClassMethods
3
- # Holds all defined rules and trigger them when an event is received.
4
- #
5
- # See Rule
6
- #
7
- class Rules
8
- class << self
9
- def add(clazz, field, props, &block)
10
- clazz = clazz.to_s
11
- @rules ||= {}
12
- # was there no ruls for this class AND is neo4j running ?
13
- if !@rules.include?(clazz) && Neo4j.running?
14
- # maybe Neo4j was started first and the rules was added later. Create rule nodes now
15
- create_rule_node_for(clazz)
16
- end
17
- @rules[clazz] ||= {}
18
- filter = block.nil? ? Proc.new { |*| true } : block
19
- @rules[clazz][field] = filter
20
- @triggers ||= {}
21
- @triggers[clazz] ||= {}
22
- trigger = props[:trigger].nil? ? [] : props[:trigger]
23
- @triggers[clazz][field] = trigger.respond_to?(:each) ? trigger : [trigger]
24
- end
25
-
26
- def inherit(parent_class, subclass)
27
- # copy all the rules
28
- @rules[parent_class.to_s].each_pair do |field, filter|
29
- subclass.rule field, &filter
30
- end if @rules[parent_class.to_s]
31
- end
32
-
33
- def trigger_other_rules(node)
34
- clazz = node[:_classname]
35
- @rules[clazz].keys.each do |field|
36
- rel_types = @triggers[clazz][field]
37
- rel_types.each do |rel_type|
38
- node.incoming(rel_type).each { |n| n.trigger_rules }
39
- end
40
- end
41
- end
42
-
43
- def fields_for(clazz)
44
- clazz = clazz.to_s
45
- return [] if @rules.nil? || @rules[clazz].nil?
46
- @rules[clazz].keys
47
- end
48
-
49
- def delete(clazz)
50
- clazz = clazz.to_s
51
- # delete the rule node if found
52
- if Neo4j.ref_node.rel?(clazz)
53
- Neo4j.ref_node.outgoing(clazz).each { |n| n.del }
54
- end
55
- @rules.delete(clazz) if @rules
56
- end
57
-
58
- def on_neo4j_started(*)
59
- @rules.each_key { |clazz| create_rule_node_for(clazz) } if @rules
60
- end
61
-
62
- def create_rule_node_for(clazz)
63
- if !Neo4j.ref_node.rel?(clazz)
64
- Neo4j::Transaction.run do
65
- node = Neo4j::Node.new
66
- Neo4j.ref_node.outgoing(clazz) << node
67
- node
68
- end
69
- end
70
- end
71
-
72
- def trigger?(node)
73
- @rules && node.property?(:_classname) && @rules.include?(node[:_classname])
74
- end
75
-
76
- def rule_for(clazz)
77
- if Neo4j.ref_node.rel?(clazz)
78
- Neo4j.ref_node._rel(:outgoing, clazz)._end_node
79
- else
80
- # this should be called if the rule node gets deleted
81
- create_rule_node_for(clazz)
82
- end
83
- end
84
-
85
-
86
- def on_relationship_created(rel, *)
87
- trigger_start_node = trigger?(rel._start_node)
88
- trigger_end_node = trigger?(rel._end_node)
89
- # end or start node must be triggered by this event
90
- return unless trigger_start_node || trigger_end_node
91
- on_property_changed(trigger_start_node ? rel._start_node : rel._end_node)
92
- end
93
-
94
-
95
- def on_property_changed(node, *)
96
- trigger_rules(node) if trigger?(node)
97
- end
98
-
99
- def trigger_rules(node)
100
- trigger_rules_for_class(node, node[:_classname])
101
- trigger_other_rules(node)
102
- end
103
-
104
- def trigger_rules_for_class(node, clazz)
105
- return if @rules[clazz].nil?
106
-
107
- agg_node = rule_for(clazz)
108
- @rules[clazz].each_pair do |field, rule|
109
- if run_rule(rule, node)
110
- # is this node already included ?
111
- unless connected?(field, agg_node, node)
112
- agg_node.outgoing(field) << node
113
- end
114
- else
115
- # remove old ?
116
- break_connection(field, agg_node, node)
117
- end
118
- end
119
-
120
- # recursively add relationships for all the parent classes with rules that also pass for this node
121
- if clazz = eval("#{clazz}.superclass")
122
- trigger_rules_for_class(node, clazz.to_s)
123
- end
124
- end
125
-
126
- # work out if two nodes are connected by a particular relationship
127
- # uses the end_node to start with because it's more likely to have less relationships to go through
128
- # (just the number of superclasses it has really)
129
- def connected?(relationship, start_node, end_node)
130
- end_node.incoming(relationship).each do |n|
131
- return true if n == start_node
132
- end
133
- false
134
- end
135
-
136
- # sever a direct one-to-one relationship if it exists
137
- def break_connection(relationship, start_node, end_node)
138
- end_node.rels(relationship).incoming.each do |r|
139
- return r.del if r.start_node == start_node
140
- end
141
- end
142
-
143
- def run_rule(rule, node)
144
- if rule.arity != 1
145
- node.wrapper.instance_eval(&rule)
146
- else
147
- rule.call(node)
148
- end
149
- end
150
- end
151
- end
152
3
 
153
4
 
154
5
  # Allows you to group nodes by providing a rule.
@@ -196,7 +47,7 @@ module Neo4j::Mapping
196
47
  # class Reader
197
48
  # include Neo4j::NodeMixin
198
49
  # property :age
199
- # rule(:young, :trigger => :readers) { age < 15 }
50
+ # rule(:young, :triggers => :readers) { age < 15 }
200
51
  # end
201
52
  #
202
53
  # class NewsStory
@@ -218,7 +69,8 @@ module Neo4j::Mapping
218
69
  # end
219
70
  #
220
71
  # === Thread Safe ?
221
- # Not sure...
72
+ # Yes, since operations are performed in an transaction. However you may get a deadlock exception:
73
+ # http://docs.neo4j.org/html/snapshot/#_deadlocks
222
74
  #
223
75
  module Rule
224
76
 
@@ -228,8 +80,10 @@ module Neo4j::Mapping
228
80
  # Example of usage:
229
81
  # class Person
230
82
  # include Neo4j
83
+ # property :age
231
84
  # rule :all
232
85
  # rule :young { self[:age] < 10 }
86
+ # rule(:old, :functions => [Sum.new[:age]) { age > 20 }
233
87
  # end
234
88
  #
235
89
  # p1 = Person.new :age => 5
@@ -239,35 +93,38 @@ module Neo4j::Mapping
239
93
  # Person.all # => [p1,p2,p3]
240
94
  # Person.young # => [p1,p2]
241
95
  # p1.young? # => true
96
+ # p1.sum(old, :age) # the some of the old people's age
242
97
  #
243
- def rule(name, props = {}, &block)
244
- singelton = class << self;
98
+ def rule(rule_name, props = {}, &block)
99
+ singleton = class << self;
245
100
  self;
246
101
  end
247
102
 
248
103
  # define class methods
249
- singelton.send(:define_method, name) do
250
- agg_node = Rules.rule_for(self)
251
- raise "no rule node for #{name} on #{self}" if agg_node.nil?
252
- traversal = agg_node.outgoing(name) # TODO possible to cache this object
253
- Rules.fields_for(self).each do |filter_name|
254
- traversal.filter_method(filter_name) do |path|
255
- path.end_node.rel?(filter_name, :incoming)
256
- end
257
- end
258
- traversal
259
- end unless respond_to?(name)
104
+ singleton.send(:define_method, rule_name) do
105
+ rule_node = Neo4j::Mapping::Rule.rule_node_for(self)
106
+ rule_node.traversal(rule_name)
107
+ end unless respond_to?(rule_name)
260
108
 
261
109
  # define instance methods
262
- self.send(:define_method, "#{name}?") do
110
+ self.send(:define_method, "#{rule_name}?") do
263
111
  instance_eval &block
264
112
  end
265
113
 
266
- Rules.add(self, name, props, &block)
114
+ rule = Neo4j::Mapping::Rule.add(self, rule_name, props, &block)
115
+
116
+ rule.functions && rule.functions.each do |func|
117
+ singleton.send(:define_method, func.class.function_name) do |r_name, *args|
118
+ rule_node = Neo4j::Mapping::Rule.rule_node_for(self)
119
+ function_id = args.empty? ? "_classname" : args[0]
120
+ function = rule_node.find_function(r_name, func.class.function_name, function_id)
121
+ function.value(rule_node.rule_node, r_name)
122
+ end unless respond_to?(func.class.function_name)
123
+ end
267
124
  end
268
125
 
269
126
  def inherit_rules_from(clazz)
270
- Rules.inherit(clazz, self)
127
+ Neo4j::Mapping::Rule.inherit(clazz, self)
271
128
  end
272
129
 
273
130
  # This is typically used for RSpecs to clean up rule nodes created by the #rule method.
@@ -276,20 +133,20 @@ module Neo4j::Mapping
276
133
  singelton = class << self;
277
134
  self;
278
135
  end
279
- Rules.fields_for(self).each do |name|
280
- singelton.send(:remove_method, name)
281
- end
282
- Rules.delete(self)
136
+ rule_node = Neo4j::Mapping::Rule.rule_node_for(self)
137
+
138
+ rule_node.rule_names.each {|rule_name| singelton.send(:remove_method, rule_name)}
139
+ rule_node.rules.clear
283
140
  end
284
141
 
285
142
  # Force to trigger the rules.
286
143
  # You don't normally need that since it will be done automatically.
287
144
  def trigger_rules(node)
288
- Rules.trigger_rules(node)
145
+ Neo4j::Mapping::Rule.trigger_rules(node)
289
146
  end
290
147
 
291
148
  end
292
149
 
293
- Neo4j.unstarted_db.event_handler.add(Rules) unless Neo4j.read_only?
150
+ Neo4j.unstarted_db.event_handler.add(Neo4j::Mapping::Rule) unless Neo4j.read_only?
294
151
  end
295
152
  end