Skip to content

Commit

Permalink
Updated documentation and RDocs.
Browse files Browse the repository at this point in the history
Changed Neo4j::NodeMixin#props to return _neo_id instead of id for the node id.
The rest specs are broken
  • Loading branch information
andreas committed Feb 4, 2010
1 parent cf78df5 commit 7950d54
Show file tree
Hide file tree
Showing 14 changed files with 256 additions and 147 deletions.
45 changes: 33 additions & 12 deletions README.rdoc
Expand Up @@ -51,8 +51,8 @@ This page contains the following information:
* Three Minute Tutorial
* Ten Minute Tutorial
* Neo4j API Documentation
* Extension: REST (see Neo4j::RestMixin)
* Extension: find_path
* Extensions: REST (see Neo4j::RestMixin) and find_path
* Performance issues
* Ruby on Rails with Neo4j.rb
* Lucene API Documentation

Expand Down Expand Up @@ -250,7 +250,7 @@ There are three ways of finding/querying nodes in Neo4j:
3. using the unique neo4j id (Neo4j::NodeMixin#neo_id).

When doing a traversal one starts from a node and traverses one or more relationships (one or more levels deep).
This start node can be either the reference node which is always found (Neo4j.ref_node) or by finding a start
This start node can be either the reference node which is always found (Neo4j#ref_node) or by finding a start
node from a Lucene query.

=== Lucene Queries
Expand Down Expand Up @@ -284,6 +284,11 @@ Example of using the search result.

=== Creating a Relationships

Since we declared a relationship in the example above with <tt>has_n :friends</tt> (see Neo4j::RelClassMethods#has_n) we
can use the generated methods <tt>Person#friends</tt> and <tt>Person#friends_rels</tt>
The <tt>friends_rels</tt> method is used to access relationships and the <tt>Person#friends</tt>
method for accessing nodes.

Adding a relationship between two nodes:

person2 = Person.new
Expand All @@ -301,7 +306,7 @@ Example

To delete the relationship between person and person2:

person.rels[person2].del
person.friends_rels.first.del

If a node is deleted then all its relationship will also be deleted
Deleting a node is performed by using the delete method:
Expand Down Expand Up @@ -601,13 +606,19 @@ Example
Neo relationships are asymmetrical. That means that if A has a relationship to B
then it may not be true that B has a relationship to A.

Relationships can be declared by using the 'has_n', 'has_one' or 'has_list'
Neo4j::NodeMixin class methods.
Relationships can be declared by using 'has_n', 'has_one' or 'has_list' Neo4j::RelClassMethods methods (included in the Neo4j::NodeMixin).

This methods generates accessor methods for relationships.
By using those accessor methods we do no longer need to know which direction to navigate in the relationship.
There are accessor methods for both relationships and nodes.

The 'has_n', 'has_one' or 'has_list' Neo4j::RelClassMethods methods returns a Neo4j::Relationships::DeclRelationshipDsl.

=== has_n

The has_n Neo4j::NodeMixin class method creates a new instance method that can
The Neo4j::NodeMixin#has_n class method (see Neo4j::RelClassMethods#has_n) creates a new instance method that can
be used for both traversing and adding new objects to a specific relationship type.
The has_n method returns a DSL object Neo4j::Relationships::DeclRelationshipDsl

For example, let say that Person can have a relationship to any other node class with the type 'friends':

Expand Down Expand Up @@ -752,7 +763,7 @@ This work for both incoming and outgoing nodes.

=== Relationship has_one

Example of has_one: A person can have at most one Address
Example: A person can have at most one Address

class Address; end

Expand Down Expand Up @@ -784,6 +795,8 @@ Or from the incoming ''address'' relationship
a.people << Person.new
a.people.first.address # => a

For more documentation see the Neo4j::RelClassMethods#has_one.

=== Relationship has_list
The has_n relationship will not maintain the order of when items are inserted to the relationship.
If order should be preserved then use the has_list class method instead.
Expand Down Expand Up @@ -813,6 +826,8 @@ Example
company.employees << employee1 << employee2
company.employees.size # => 2

For more documentation see the Neo4j::RelClassMethods#has_list.

==== Deleted List Items

The list will be updated if an item is deleted in a list.
Expand Down Expand Up @@ -911,7 +926,7 @@ To return nodes (just like the relationships method)


The reindexer extension that is used in the example above will for each created node create a relationship
from the index node (Neo4j.ref_node.relationships.outgoing(:index_node)) to that new node.
from the index node (Neo4j#ref_node.relationships.outgoing(:index_node)) to that new node.
The all method use these relationships in order to return nodes of a certain class.
The update_index method also uses this all method in order to update index for all nodes of a specific class.

Expand Down Expand Up @@ -1425,14 +1440,20 @@ Example:
This node can have a relationship to the index node (Neo4j::IndexNode), which has relationships to all created nodes.
You can add relationships from this node to your nodes.

=== Performance Issues
== Performance Issues

It is recommended to wrap several Neo4j operations including read operations
in a singe transaction if possible for better performance.
Updating a Lucene index can be slow. A solution to this is to keep the index in memory instead of on disk.

I'm currently looking at how to scale neo4j.rb by a simple master-slave cluster by
using REST, see the REST extension below.
Using raw java nodes (Neo4j::Node) and relationship (Neo4j::Relationship) will also increase performance.
Here is an example how to traverse only using Java objects (instead of Ruby wrappers):

iter = folder.outgoing(:child_folders).raw(true).depth(:all).iterator
iter.hasNext()

The example above gives you access to the raw Java iterator class


== Extensions: Replication

Expand Down
36 changes: 22 additions & 14 deletions lib/neo4j/mixins/java_property_mixin.rb
@@ -1,35 +1,43 @@
module Neo4j::JavaPropertyMixin

# This is the property to use to map ruby classes to Neo4j Nodes
CLASSNAME_PROPERTY = "_classname"

# Returns the unique id of this node.
# Ids are garbage collected over time so are only guaranteed to be unique at a specific set of time: if the node is deleted,
# it's likely that a new node at some point will get the old id. Note: this make node ids brittle as public APIs.
def neo_id
getId
end

def _wrapper=(wrapper)
def _wrapper=(wrapper) # :nodoc:
@_wrapper = wrapper
end

def _java_node
self
end


# Returns true if this property container has a property accessible through the given key, false otherwise.
def property?(key)
has_property?(key.to_s)
end

# Returns the given property if it exist or nil if it does not exist.
def [](key)
return unless property?(key)
if @_wrapper and @_wrapper.class.marshal?(key)
Marshal.load(String.from_java_bytes(get_property(key.to_s)))
Marshal.load(String.from_java_bytes(get_property(key.to_s)))
else
get_property(key.to_s)
end
end

# Sets the given property to given value.
# Will generate an event if the property does not start with '_' (which could be an internal property, like _classname)
#
def []=(key, value)
k = key.to_s
return if k == 'id'
old_value = self[key]

if value.nil?
Expand All @@ -41,7 +49,7 @@ def []=(key, value)
setProperty(k, value)
end

if (@_wrapper and k[0,1] != '_') # do not want events on internal properties
if (@_wrapper and k[0, 1] != '_') # do not want events on internal properties
@_wrapper.class.indexer.on_property_changed(@_wrapper, k) if @_wrapper.class.respond_to? :indexer
Neo4j.event_handler.property_changed(@_wrapper, k, old_value, value)
end
Expand All @@ -50,6 +58,8 @@ def []=(key, value)


# Removes the property from this node.
# This is same as setting a property value to nil.
#
# For more information see JavaDoc PropertyContainer#removeProperty
#
# ==== Example
Expand All @@ -59,9 +69,8 @@ def []=(key, value)
# a[:foo] # => nil
#
# ==== Returns
# true if the property was removed, false otherwise
# <tt>true</tt> if the property was removed, <tt>false</tt> otherwise
#
# :api: public
def delete_property (name)
removed = !removeProperty(name).nil?
if (removed and @_wrapper and name[0] != '_') # do not want events on internal properties
Expand All @@ -73,11 +82,10 @@ def delete_property (name)
# Returns a hash of all properties.
#
# ==== Returns
# Hash:: property key and property value
# Hash:: property key and property value with the '_neo_id' as the neo_id
#
# :api: public
def props
ret = {"id" => getId()}
ret = {"_neo_id" => getId()}
iter = getPropertyKeys.iterator
while (iter.hasNext) do
key = iter.next
Expand All @@ -100,9 +108,9 @@ def props
# :api: public
def update(struct_or_hash, options={})
strict = options[:strict]
keys_to_delete = props.keys - %w(id classname) if strict
keys_to_delete = props.keys - %w(_neo_id) if strict # TODO can probably simply all this code a lot
struct_or_hash.each_pair do |key, value|
next if %w(id classname).include? key.to_s # do not allow special properties to be mass assigned
next if %w(_neo_id).include? key.to_s # do not allow special properties to be mass assigned
keys_to_delete.delete(key) if strict
self[key] = value
end
Expand All @@ -114,7 +122,7 @@ def update(struct_or_hash, options={})
def equal?(o)
eql?(o)
end

def eql?(o)
return false unless o.respond_to?(:neo_id)
o.neo_id == neo_id
Expand All @@ -132,7 +140,7 @@ def ==(o)
def to_param
neo_id.to_s
end

# Loads a Neo node wrapper if possible
# If the neo property '_classname' does not exist then it will map the neo node to the ruby class Neo4j::Node
def wrapper
Expand Down
29 changes: 15 additions & 14 deletions lib/neo4j/mixins/node_mixin.rb
Expand Up @@ -3,14 +3,21 @@ module Neo4j

# Represents a node in the Neo4j space.
#
# Is a wrapper around a Java Neo4j node (org.neo4j.graphdb.Node)
# The following methods are delegated to the Java Neo4j Node:
# Is a wrapper around a Java Neo4j::Node (org.neo4j.graphdb.Node)
# The following methods are delegated to the Java Neo4j::Node
# []=, [], property?, props, update, neo_id, rels, rel?, to_param
# rel, del, list?, list, lists, print, add_rel, outgoing, incoming,
# next, prev, next=, prev=, head
#
# Those methods are defined in included mixins
# This mixin also include the class method in the
#
# Those methods are defined in the mixins Neo4j::JavaPropertyMixin, Neo4j::JavaNodeMixin, Neo4j::JavaListMixin.
# === Included Mixins
# * Neo4j::JavaPropertyMixin - instance methods for properties
# * Neo4j::JavaNodeMixin - instance methods
# * Neo4j::JavaListMixin - instance methods for list methods
# * Neo4j::RelClassMethods - class methods for generating relationship accessors
# * Neo4j::PropertyClassMethods - class methods for generating property accessors
#
module NodeMixin
extend Forwardable
Expand All @@ -36,7 +43,8 @@ module NodeMixin
# If you want to provide your own initialize method you should instead implement the
# method init_node method.
#
# Example:
# === Example
#
# class MyNode
# include Neo4j::NodeMixin
#
Expand All @@ -47,6 +55,8 @@ module NodeMixin
# end
#
# node = MyNode('jimmy', 23)
# # or also possible
# node = MyNode :name => 'jimmy', :age => 12
#
# The init_node is only called when the node is constructed the first, unlike te initialize method which is used both for
# loading the node from the Neo4j database and creating the Ruby object.
Expand All @@ -73,7 +83,6 @@ def initialize(*args)

# Inits this node with the specified java neo node
#
# :api: private
def init_with_node(java_node) # :nodoc:
@_java_node = java_node
java_node._wrapper=self
Expand All @@ -90,7 +99,6 @@ def _java_node

# Inits when no neo java node exists. Must create a new neo java node first.
#
# :api: private
def init_without_node # :nodoc:
@_java_node = Neo4j.create_node
@_java_node._wrapper = self
Expand All @@ -117,7 +125,6 @@ def init_without_node # :nodoc:
# ==== Returns
# a value object struct
#
# :api: public
def value_object
vo = self.class.value_object.new
vo._update(props)
Expand Down Expand Up @@ -163,7 +170,6 @@ def ignore_incoming_cascade_delete?(relationship) # :nodoc:
# This method will be automatically called when needed
# (a property changed or a relationship was created/deleted)
#
# @api private
def update_index # :nodoc:
self.class.indexer.index(self)
end
Expand All @@ -175,18 +181,14 @@ def update_index # :nodoc:
# Returns a Neo4j::Relationships::NodeTraverser object for traversing nodes from and to this node.
# The Neo4j::Relationships::NodeTraverser is an Enumerable that returns Neo4j::NodeMixin objects.
#
# ==== See Also
# Neo4j::Relationships::NodeTraverser
#
# ==== Example
#
# person_node.traverse.outgoing(:friends).each { ... }
# person_node.traverse.outgoing(:friends).raw(true).each { }
# person_node.traverse.outgoing(:friends).raw.each { }
#
# The raw false parameter means that the ruby wrapper object will not be loaded, instead the raw Java Neo4j object will be used,
# it might improve the performance.
#
# :api: public
def traverse(*args)
if args.empty?
Neo4j::Relationships::NodeTraverser.new(self)
Expand All @@ -201,7 +203,6 @@ def traverse(*args)
# Private methods
#

# :api: private
def _to_java_direction(dir) # :nodoc:
case dir
when :outgoing
Expand Down

0 comments on commit 7950d54

Please sign in to comment.