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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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