neo4j 1.0.0.beta.13 → 1.0.0.beta.14

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.
@@ -0,0 +1,59 @@
1
+ module Neo4j
2
+ module Index
3
+
4
+ class WrappedQuery
5
+ include Enumerable
6
+
7
+ def initialize(index, query)
8
+ @index = index
9
+ @query = query
10
+ end
11
+
12
+ def each
13
+ hits.each { |n| yield n.wrapper }
14
+ end
15
+
16
+ def close
17
+ @hits.close if @hits
18
+ end
19
+
20
+ def empty?
21
+ hits.size == 0
22
+ end
23
+
24
+ def [](index)
25
+ each_with_index {|node,i| break node if index == i}
26
+ end
27
+
28
+ def size
29
+ hits.size
30
+ end
31
+
32
+ def hits
33
+ @hits ||= perform_query
34
+ end
35
+
36
+ def desc(*fields)
37
+ @order = fields.inject(@order || {}) { |memo, field| memo[field] = true; memo }
38
+ self
39
+ end
40
+
41
+ def asc(*fields)
42
+ @order = fields.inject(@order || {}) { |memo, field| memo[field] = false; memo }
43
+ self
44
+ end
45
+
46
+ def perform_query
47
+ if @order
48
+ java_sort_fields = @order.keys.inject([]) do |memo, field|
49
+ memo << org.apache.lucene.search.SortField.new(field.to_s, org.apache.lucene.search.SortField::STRING, @order[field])
50
+ end
51
+ sort = org.apache.lucene.search.Sort.new(*java_sort_fields)
52
+ @query = org.neo4j.index.impl.lucene.QueryContext.new(@query).sort(sort)
53
+ end
54
+ @index.query(@query)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
data/lib/neo4j/load.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  module Neo4j
2
+
3
+ # Module used to load both Nodes and Relationship from the database
2
4
  module Load
3
- def wrapper(node)
5
+ def wrapper(node) # :nodoc:
4
6
  return node unless node.property?(:_classname)
5
7
  to_class(node[:_classname]).load_wrapper(node)
6
8
  end
7
9
 
8
- def to_class(class_name)
10
+ def to_class(class_name) # :nodoc:
9
11
  class_name.split("::").inject(Kernel) {|container, name| container.const_get(name.to_s) }
10
12
  end
11
13
 
14
+ # Checks if the given node or node id exists in the database.
12
15
  def exist?(node_or_node_id, db = Neo4j.started_db)
13
16
  id = node_or_node_id.kind_of?(Fixnum) ? node_or_node_id : node_or_node_id.id
14
- load(id, db) != nil
17
+ _load(id, db) != nil
15
18
  end
16
19
  end
17
20
  end
@@ -33,8 +33,8 @@ module Neo4j::Mapping
33
33
  module_eval(%Q{
34
34
  def #{rel_type}_rels
35
35
  dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
36
- Neo4j::Mapping::HasN.new(self, dsl).rels
37
- end}, __FILE__, __LINE__)
36
+ dsl.all_relationships(self).wrapped
37
+ end}, __FILE__, __LINE__)
38
38
 
39
39
  _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, false, params)
40
40
  end
@@ -64,26 +64,20 @@ module Neo4j::Mapping
64
64
  clazz = self
65
65
  module_eval(%Q{def #{rel_type}=(value)
66
66
  dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
67
- dir = dsl.direction
68
- dsl = dsl.incoming? ? dsl.incoming_dsl(self) : dsl
69
- rel = dsl.single_relationship(self, dir)
67
+ rel = dsl.single_relationship(self)
70
68
  rel.del unless rel.nil?
71
- dsl.create_relationship_to(self, value)
69
+ dsl.create_relationship_to(self, value) if value
72
70
  end}, __FILE__, __LINE__)
73
71
 
74
72
  module_eval(%Q{def #{rel_type}
75
73
  dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
76
- dir = dsl.direction
77
- dsl = dsl.incoming? ? dsl.incoming_dsl(self) : dsl
78
- dsl.single_relationship(self, dir)
74
+ dsl.single_node(self)
79
75
  end}, __FILE__, __LINE__)
80
76
 
81
- # TODO
82
77
  module_eval(%Q{
83
78
  def #{rel_type}_rel
84
79
  dsl = #{clazz}._decl_rels[:'#{rel_type.to_s}']
85
- r = Neo4j::Mapping::HasN.new(self, dsl).rels
86
- [*r][0]
80
+ dsl.single_relationship(self)
87
81
  end}, __FILE__, __LINE__)
88
82
 
89
83
  _decl_rels[rel_type.to_sym] = Neo4j::Mapping::DeclRelationshipDsl.new(rel_type, true, params)
@@ -91,4 +85,4 @@ module Neo4j::Mapping
91
85
 
92
86
  end
93
87
  end
94
- end
88
+ end
@@ -61,16 +61,16 @@ module Neo4j::Mapping
61
61
  end
62
62
 
63
63
  def rule_for(clazz)
64
- Neo4j.ref_node.outgoing(clazz).first
64
+ Neo4j.ref_node._rel(:outgoing, clazz)._end_node
65
65
  end
66
66
 
67
67
 
68
- def on_relationship_created(rel)
69
- trigger_start_node = trigger?(rel.start_node)
70
- trigger_end_node = trigger?(rel.end_node)
68
+ def on_relationship_created(rel, *)
69
+ trigger_start_node = trigger?(rel._start_node)
70
+ trigger_end_node = trigger?(rel._end_node)
71
71
  # end or start node must be triggered by this event
72
72
  return unless trigger_start_node || trigger_end_node
73
- on_property_changed(trigger_start_node ? rel.start_node : rel.end_node)
73
+ on_property_changed(trigger_start_node ? rel._start_node : rel._end_node)
74
74
  end
75
75
 
76
76
 
@@ -100,8 +100,7 @@ module Neo4j::Mapping
100
100
 
101
101
  def run_rule(rule, node)
102
102
  if rule.arity != 1
103
- wrapper = Neo4j::Node.wrapper(node)
104
- wrapper.instance_eval(&rule)
103
+ node.wrapper.instance_eval(&rule)
105
104
  else
106
105
  rule.call(node)
107
106
  end
@@ -89,22 +89,33 @@ module Neo4j::Mapping
89
89
  @direction == :incoming
90
90
  end
91
91
 
92
- def incoming_dsl(node)
93
- # which class specifies the incoming DSL ?
94
- clazz = to_class || node.class
95
- dsl = clazz._decl_rels[to_type]
96
- raise "Unspecified outgoing relationship '#{to_type}' for incoming relationship '#{rel_id}' on class #{clazz}" if dsl.nil?
92
+ def incoming_dsl
93
+ dsl = to_class._decl_rels[to_type]
94
+ raise "Unspecified outgoing relationship '#{to_type}' for incoming relationship '#{rel_id}' on class #{to_class}" if dsl.nil?
97
95
  dsl
98
96
  end
99
97
 
100
- def single_relationship(node, dir)
101
- type = type_to_java(namespace_type)
102
- dir = dir_to_java(dir)
103
- rel = node._java_node.getSingleRelationship(type, dir)
104
- rel.nil? ? nil : rel.other_node(node).wrapper
98
+ def single_node(node)
99
+ rel = single_relationship(node)
100
+ rel && rel.other_node(node).wrapper
101
+ end
102
+
103
+ def single_relationship(node)
104
+ dir = direction
105
+ dsl = incoming? ? incoming_dsl : self
106
+ node._java_node.rel(dir, dsl.namespace_type)
107
+ end
108
+
109
+ def all_relationships(node)
110
+ dsl = incoming? ? incoming_dsl : self
111
+ type = type_to_java(dsl.namespace_type)
112
+ dir = dir_to_java(direction)
113
+ node._java_node.getRelationships(type, dir)
105
114
  end
106
115
 
107
116
  def create_relationship_to(node, other)
117
+ dsl = incoming? ? incoming_dsl : self
118
+
108
119
  # If the are creating an incoming relationship we need to swap incoming and outgoing nodes
109
120
  if @direction == :outgoing
110
121
  from, to = node, other
@@ -112,9 +123,10 @@ module Neo4j::Mapping
112
123
  from, to = other, node
113
124
  end
114
125
 
115
- java_type = type_to_java(namespace_type)
126
+ java_type = type_to_java(dsl.namespace_type)
127
+
116
128
  rel = from._java_node.create_relationship_to(to._java_node, java_type)
117
- rel[:_classname] = relationship_class.to_s if relationship_class
129
+ rel[:_classname] = dsl.relationship_class.to_s if dsl.relationship_class
118
130
 
119
131
  # TODO - not implemented yet
120
132
  # the from.neo_id is only used for cascade_delete_incoming since that node will be deleted when all the list items has been deleted.
@@ -11,7 +11,7 @@ module Neo4j
11
11
  def initialize(node, dsl) # :nodoc:
12
12
  @node = node
13
13
  @direction = dsl.direction
14
- @dsl = @direction == :outgoing ? dsl : dsl.incoming_dsl(node)
14
+ @dsl = @direction == :outgoing ? dsl : dsl.incoming_dsl
15
15
  end
16
16
 
17
17
  def to_s
@@ -41,16 +41,6 @@ module Neo4j
41
41
  end
42
42
 
43
43
 
44
- # Returns the relationships instead of the nodes.
45
- #
46
- # ==== Example
47
- # # return the relationship objects between the folder and file nodes:
48
- # folder.files.rels.each {|x| ...}
49
- #
50
- def rels
51
- Neo4j::RelationshipTraverser.new(@node._java_node, [@dsl.namespace_type], @direction)
52
- end
53
-
54
44
  # Returns true if there are no node in this type of relationship
55
45
  def empty?
56
46
  first != nil
@@ -6,7 +6,7 @@ module Neo4j::Mapping
6
6
 
7
7
  def_delegators :@_java_node, :[]=, :[], :property?, :props, :attributes, :update, :neo_id, :id, :rels, :rel?, :to_param, :getId,
8
8
  :rel, :del, :list?, :print, :print_sub, :outgoing, :incoming, :both,
9
- :equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship
9
+ :equal?, :eql?, :==, :exist?, :getRelationships, :getSingleRelationship, :rels_raw, :rel
10
10
 
11
11
 
12
12
  # --------------------------------------------------------------------------
@@ -49,7 +49,7 @@ module Neo4j::Mapping
49
49
  class << self
50
50
  alias_method :orig_new, :new
51
51
  end
52
- end
52
+ end unless c.respond_to?(:orig_new)
53
53
 
54
54
  c.extend ClassMethods::Root
55
55
  c.extend ClassMethods::Property
data/lib/neo4j/neo4j.rb CHANGED
@@ -59,6 +59,11 @@ module Neo4j
59
59
  Enumerator.new(this_db, :each_node)
60
60
  end
61
61
 
62
+ # Same as #all_nodes but does not return wrapped nodes.
63
+ def _all_nodes(this_db = self.started_db)
64
+ Enumerator.new(this_db, :_each_node)
65
+ end
66
+
62
67
  def event_handler(this_db = default_db)
63
68
  this_db.event_handler
64
69
  end
data/lib/neo4j/node.rb CHANGED
@@ -42,16 +42,52 @@ module Neo4j
42
42
  end
43
43
 
44
44
 
45
+ # A node in the graph with properties and relationships to other entities.
46
+ # Along with relationships, nodes are the core building blocks of the Neo4j data representation model.
47
+ # Node has three major groups of operations: operations that deal with relationships, operations that deal with properties and operations that traverse the node space.
48
+ # The property operations give access to the key-value property pairs.
49
+ # Property keys are always strings. Valid property value types are the primitives(<tt>String</tt>, <tt>Fixnum</tt>, <tt>Float</tt>, <tt>Boolean</tt>), and arrays of those primitives.
50
+ #
51
+ # === Instance Methods form Included Mixins
52
+ # * Neo4j::Property - methods that deal with properties
53
+ # * Neo4j::NodeRelationship methods for relationship
54
+ # * Neo4j::Equal equality operators: <tt>eql?</tt>, <tt>equal</tt>, <tt>==</tt>
55
+ # * Neo4j::Index lucene index methods, like indexing a node
56
+ #
57
+ # === Class Methods from Included Mixins
58
+ # * Neo4j::Index::ClassMethods lucene index class methods, like find
59
+ # * Neo4j::Load - methods for loading a node
60
+ #
61
+ # See also the Neo4j::NodeMixin if you want to wrap a node with your own Ruby class.
62
+ #
45
63
  class Node
46
64
  extend Neo4j::Index::ClassMethods
65
+ extend Neo4j::Load
47
66
 
48
67
  self.node_indexer self
49
68
 
50
69
  class << self
51
- include Neo4j::Load
52
70
 
53
- # Creates a new node using the default db instance when given no args
54
- # Same as Neo4j::Node#create
71
+ # Returns a new neo4j Node.
72
+ # The return node is actually an Java obejct of type org.neo4j.graphdb.Node java object
73
+ # which has been extended (see the included mixins for Neo4j::Node).
74
+ #
75
+ # The created node will have a unique id - Neo4j::Property#neo_id
76
+ #
77
+ # ==== Parameters
78
+ # *args :: a hash of properties to initialize the node with or nil
79
+ #
80
+ # ==== Returns
81
+ # org.neo4j.graphdb.Node java object
82
+ #
83
+ # ==== Examples
84
+ #
85
+ # Neo4j::Transaction.run do
86
+ # Neo4j::Node.new
87
+ # Neo4j::Node.new :name => 'foo', :age => 100
88
+ # end
89
+ #
90
+ #
55
91
  def new(*args)
56
92
  # the first argument can be an hash of properties to set
57
93
  props = args[0].respond_to?(:each_pair) && args[0]
@@ -67,15 +103,28 @@ module Neo4j
67
103
  # create is the same as new
68
104
  alias_method :create, :new
69
105
 
106
+ # Loads a node or wrapped node given a native java node or an id.
107
+ # If there is a Ruby wrapper for the node then it will create a Ruby object that will
108
+ # wrap the java node (see Neo4j::NodeMixin).
109
+ #
110
+ # If the node does not exist it will return nil
111
+ #
70
112
  def load(node_id, db = Neo4j.started_db)
71
- wrapper(db.graph.get_node_by_id(node_id.to_i))
113
+ node = _load(node_id, db)
114
+ return nil if node.nil?
115
+ node.wrapper
116
+ end
117
+
118
+ # Same as load but does not return the node as a wrapped Ruby object.
119
+ #
120
+ def _load(node_id, db)
121
+ db.graph.get_node_by_id(node_id.to_i)
72
122
  rescue java.lang.IllegalStateException
73
123
  nil # the node has been deleted
74
124
  rescue org.neo4j.graphdb.NotFoundException
75
125
  nil
76
126
  end
77
127
 
78
-
79
128
  end
80
129
  end
81
130
  end
@@ -4,7 +4,38 @@ module Neo4j
4
4
  module NodeRelationship
5
5
  include ToJava
6
6
 
7
- def outgoing(type=nil)
7
+
8
+ # Returns the outgoing nodes for this node.
9
+ #
10
+ # ==== Returns
11
+ # a Neo4j::NodeTraverser which can be used to further specify which nodes should be included
12
+ # in traversal by using the <tt>depth</tt>, <tt>filter</tt> and <tt>prune</tt> methods.
13
+ #
14
+ # ==== Examples
15
+ # # Find all my friends (nodes of depth 1 of type <tt>friends</tt>)
16
+ # me.outgoing(:friends).each {|friend| puts friend[:name]}
17
+ #
18
+ # # Find all my friends and their friends (nodes of depth 1 of type <tt>friends</tt>)
19
+ # # me.outgoing(:friends).depth(2).each {|friend| puts friend[:name]}
20
+ #
21
+ # # Find all my friends and include my self in the result
22
+ # me.outgoing(:friends).depth(4).include_start_node.each {...}
23
+ #
24
+ # # Find all my friends friends friends, etc. at any depth
25
+ # me.outgoing(:friends).depth(:all).each {...}
26
+ #
27
+ # # Find all my friends friends but do not include my friends (only depth == 2)
28
+ # me.outgoing(:friends).depth(2).filter{|path| path.length == 2}
29
+ #
30
+ # # Find all my friends but 'cut off' some parts of the traversal path
31
+ # me.outgoing(:friends).depth(42).prune(|path| an_expression_using_path_returning_true_false }
32
+ #
33
+ # # Find all my friends and work colleges
34
+ # me.outgoing(:friends).outgoing(:work).each {...}
35
+ #
36
+ # Of course all the methods <tt>outgoing</tt>, <tt>incoming</tt>, <tt>both</tt>, <tt>depth</tt>, <tt>include_start_node</tt>, <tt>filter</tt>, and <tt>prune</tt> can be combined.
37
+ #
38
+ def outgoing(type)
8
39
  if type
9
40
  NodeTraverser.new(self).outgoing(type)
10
41
  else
@@ -13,7 +44,12 @@ module Neo4j
13
44
  end
14
45
  end
15
46
 
16
- def incoming(type=nil)
47
+
48
+ # Returns the incoming nodes of given type(s).
49
+ #
50
+ # See #outgoing
51
+ #
52
+ def incoming(type)
17
53
  if type
18
54
  NodeTraverser.new(self).incoming(type)
19
55
  else
@@ -22,6 +58,12 @@ module Neo4j
22
58
  end
23
59
  end
24
60
 
61
+ # Returns both incoming and outgoing nodes of given types(s)
62
+ #
63
+ # If a type is not given then it will return all types of relationships.
64
+ #
65
+ # See #outgoing
66
+ #
25
67
  def both(type=nil)
26
68
  if type
27
69
  NodeTraverser.new(self).both(type)
@@ -30,11 +72,58 @@ module Neo4j
30
72
  end
31
73
  end
32
74
 
75
+
76
+ # Returns an enumeration of relationship objects.
77
+ # It always returns relationship of depth one.
78
+ #
79
+ # See Neo4j::Relationship
80
+ #
81
+ # ==== Examples
82
+ # # Return both incoming and outgoing relationships
83
+ # me.rels(:friends, :work).each {|relationship|...}
84
+ #
85
+ # # Only return outgoing relationship of given type
86
+ # me.rels(:friends).outgoing.first.end_node # => my friend node
87
+ #
33
88
  def rels(*type)
34
89
  RelationshipTraverser.new(self, type, :both)
35
90
  end
36
91
 
37
92
 
93
+ # Returns the only relationship of a given type and direction that is attached to this node, or null.
94
+ # This is a convenience method that is used in the commonly occuring situation where a node has exactly zero or
95
+ # one relationships of a given type and direction to another node.
96
+ # Typically this invariant is maintained by the rest of the code: if at any time more than one such relationships
97
+ # exist, it is a fatal error that should generate an unchecked exception. This method reflects that semantics and
98
+ # returns either:
99
+ #
100
+ # * nil if there are zero relationships of the given type and direction,
101
+ # * the relationship if there's exactly one, or
102
+ # * raise an exception in all other cases.
103
+ def rel(dir, type)
104
+ result = _rel(dir, type)
105
+ result && result.wrapper
106
+ end
107
+
108
+ # Same as rel but does not return a ruby wrapped object but instead returns the Java object.
109
+ def _rel(dir, type)
110
+ get_single_relationship(type_to_java(type), dir_to_java(dir))
111
+ end
112
+
113
+ # Returns the raw java neo4j relationship object.
114
+ def rels_raw(dir=:both, *types)
115
+ if types.size > 1
116
+ java_types = types.inject([]) { |result, type| result << type_to_java(type) }.to_java(:'org.neo4j.graphdb.RelationshipType')
117
+ get_relationships(java_types)
118
+ elsif types.size == 1
119
+ get_relationships(type_to_java(types[0], dir_to_java(dir)))
120
+ elsif dir == :both
121
+ get_relationships(dir_to_java(dir))
122
+ else
123
+ raise "illegal argument, does not accept #{dir} #{types.join(',')} - only dir=:both for any relationship types"
124
+ end
125
+ end
126
+
38
127
  # Check if the given relationship exists
39
128
  # Returns true if there are one or more relationships from this node to other nodes
40
129
  # with the given relationship.
@@ -57,4 +146,4 @@ module Neo4j
57
146
 
58
147
  end
59
148
 
60
- end
149
+ end