Feel free to skim it or not, just don't want it to get lost so that no one else can learn from that or contribute alternative solutions.
Cheers Michael 1) All nodes are equal in Neo4j if I am correct, so if I have different types myself then I just create a node_type property with a text string to differentiate between f.ex. users and jobs. Is this the correct way to handle this? 2) Complex data structures, I need to store complex data structures in Neo4j, think about a whole CV like LinkedIn. When I create or update nodes can U pass-in this complex data structure as a whole? F.ex. below here could I add a parameter CV that contains a Hash where some values are Hashes themselves again? Or can should I use JSON as a value? Neography::Node.create("age" => 31, "name" => "Max") 3) I am currently creating my own models (Rails based web app) where the models themselves are not so much used like in Rails, they are only a nice way to collect logic in one place. F.ex. I could have Employee.create_new hash which internally will use neography to create a new employee and return the created one as a hash. Is this a good way to thank about the problem, or have other patterns emerged? 1) There are several strategies, marking the nodes with certain properties is one, others are - linking the nodes to a "category" node so that they can be easily accessed from there, or putting them in a dedicated index for quick lookup. 2) Nodes only have primitive properties right now. To store hashes or more complex document structures you use other nodes and relationships between them. That should be quite easy to put in a helper method. i.e. if the inner property is a hash take its property name and instead of making a property out of that one, create a relationship with this name to a node which has the hash keys + values as properties (and recursively so). Fortunately it is easy with ruby to just reopen the Node class and add those convenience methods. I did that as well. (Code below) 3) I create my own Domain objects for the Neo4j-Hosting Domain and just have them get a Neography node as intitializer param, then you can delegate all store-methods to this node or its node.neo_server and have the other domain code directly in the object (code also below). I'm also in the process of exploring this whole remote-rest-store domain modelling, so we can certainly learn a lot from each other. Mark you can still do your uniqueness constraint of the email with the index. I would have written the same as Jim, put the E-Mail addresses in a string array field, but be sure to index them not as an array but individually. Regarding your other questions of reading structures. Traversing is crazy fast in Neo4j. So what you do is not a manual navigation over the relationships but: - you get your start node (i.e. the one being the person) from the index or from another traversal. (you can see that node like a root entity). Then you specify a traversal description. I.e. which relationships to traverse, how deep etc. And then let the neo4j traverser do the work and have you return all the paths to the nodes that are included in your traversal - i.e. your whole document == aggregate structure. Then in the client you pull those paths into your domain model and have it ready for processing/displaying whatever. For hints for the traversal either look at the code of the previous email or that a quick look at the neo4j-imdb-app sourcecode at github: https://github.com/jexp/heroku-neo4j-example Cheers Michael Ok one more question for modeling the data: Say a user can have multiple e-mail addresses what is the best way to model this? Have a single property containing a list with addresses or let email_address be its own entity and create relations between the user and e-mail_address? The email address will be used to search for a user f.ex. so I rather have Neo4j being able to determine if it already exists. -Mark There's nothing in Neo4j that determines this one way or another. If email addresses are first class citizens in your domain, then they should be nodes. Otherwise make them properties. For instane, if you want to be able to assert that two users are actually the same person because they share an email address, then make the email address a node. Otherwise it's a property. Jim I am using e-mail address as a way to select a user, but I didn't think of it as a first class citizen. A user can have multiple e-mail addresses and in cases non. The thing I am not sure about is how to select a user by e-mail address. Can Neo4j have an Array as property type, using the ReST API and select a node if it the array contains the filter value? I am starting to realize that how I think about my data needs to be a bit different from f.ex. MongoDB or CouchDB. In many ways I see great possibilities, but need to get my head around these fundamental things :) -Mark It's possible to index node properties, that gives you effectively what Mongo does. Arrays are supported, you just pass in a JSON array: { "email_addresses" : ["j...@webber.name", "j...@jimwebber.org", "j...@neotechnology.com"]} The question you need to answer is: is there any value in the relationship between a user and their email address(es)? Jim Ah cool, that is what I was wondering about how easy Neo could index properties like an array. Because this is a border case (I might actually promote e-mail address to a first class citizen for uniqueness check), but there are other cases where I do not see the need to do this. Then it is good to know that Neo will handle that easily. How much is the ReST API behind the direct Java way? -Mark Regarding the second - information about those concepts is distributed over blog posts, wiki entries and mailing list posts. We certainly need more docs from the users/developers point of view, e.g. on how to model your domain in a graph. What I've just found (but that is by far not enough: http://wiki.neo4j.org/content/Domain_Modeling_Gallery) http://wiki.neo4j.org/content/Design_Guide http://stackoverflow.com/questions/1000162/has-anyone-used-graph-based-databases-http-neo4j-org http://www.build47.com/posts/neo4j-presentation-at-twitter-headquaters/ http://www.slideshare.net/directi/neo4j-and-the-benefits-of-graph-dbs-3-3325734 I also would like to discuss how one could map the DDD terms onto a graph database and where it is fitting and where not. Two last points: don't underestimate relationships, they are full first level citizens and go for traversals rather than manually navigating along the relationships. node_ext.rb require 'rubygems' require 'neography' module Neography class Node class << self def create_and_index(data, to_index) node = Neography::Node.create(data) return node unless to_index to_index.each do |index,names| names.each do | prop | node.neo_server.add_node_to_index(index,prop,data[prop],node.neo_id) end end node end def find(index, prop, value) res = Neography::Rest.new.get_node_index(index,prop,value) return nil unless res Neography::Node.load(res.first) end end end class Rest def get_type(type) case type when :node, "nodes", :nodes, "nodes" "node" when :relationship, "relationship", :relationships, "relationships" "relationship" when :path, "path", :paths, "paths" "path" when :fullpath, "fullpath", :fullpaths, "fullpaths" "fullpath" else "node" end end end end domain.rb require 'node_ext' # todo I want to be able to filter for properties with the REST api w/o reverting to JS module Gateway class Provider def initialize(node) @node = node end def to_s "#{@node.name} (#{@node.neo_id})" end class << self def get(name) node = Neography::Node.find(:providers,:name,name) return nil unless node Provider.new(node) end def add(name) provider = Provider.get(name) return provider if provider node = Neography::Node.create_and_index({:name => name}, :providers => [:name]) return nil unless node Provider.new(node) end end def plans @node.outgoing(:active_plan).collect end def plan(name) plans.find { |plan| plan.name == name } end def users rels.incoming(:account).collect end def userFor(account) users = @node.incoming(:account).filter(" position.lastRelationship() != null && position.lastRelationship().getProperty('account') == '#{account}';") return User.new(users.first) if users && !users.empty? user = Neography::Node.create_and_index({:name => account, :email => account}, :users => [:email, :name ]) @node.neo_server.create_relationship(:account, user, @node, {:account => account }) User.new(user) end def new_hash(size = 8) Digest::SHA1.hexdigest(rand.to_s)[0..size] end def databaseFor(user, plan) begin id = new_hash end while !Node.find(:databases, :id, id) db = Neography::Node.create_and_index({ :id => id, :login => new_hash, :password => new_hash, :state => :creating }, :databases => [:id]) user.outgoing(:active_database) << db db.outgoin(:has_plan) << plan Database.new(db) end end end _______________________________________________ Neo4j mailing list User@lists.neo4j.org https://lists.neo4j.org/mailman/listinfo/user