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