7

I need to store an array of User objects inside a Tile node. Each User object contains three primitive properties; Id(a single alpha-character string) , fName and lName. This list of objects is a property of the Tile node with several other primitive properties. The entire Tile node needs to be serialized to Json, including the nested User objects.

I understand that Neo can't store complex objects as properties. I created the User as a separate node with id, fName and lName as properties, and I can get these returned via Cypher. I can also get Json output results for the parent Tile node. (In this case, Users is just a string of comma-separated alphas). But how do I get the User node output nested inside the parent node?

I have created a list of User objects (userList) by relating user objects with the string of user ids in the Tile Node via a Cypher Query. I just need to get from two separate json outputs to a single nested output.

I hope this is enough detail. I'm using Neo4j 2.1.6 and Neo4jClient. I'm also using .Net 4.0.

Codebling
  • 10,764
  • 2
  • 38
  • 66
Wayne Cordrey
  • 75
  • 1
  • 3

3 Answers3

7

You could do something like this with cypher and have the cypher return a composite object.

MATCH (t:Tile)-[:CONTAINS_USER]-(u:User)
WHERE t.name =~ 'Tile.*'
WITH {name: t.name, users: collect(u) } AS tile
RETURN collect(tile) AS tiles 
Dave Bennett
  • 10,996
  • 3
  • 30
  • 41
  • 3
    Oooh, that's cheating. I like it. :) – FrobberOfBits May 12 '15 at 21:14
  • What do I add to this to display the results of the collection? – Wayne Cordrey May 19 '15 at 22:33
  • there are two collections: 1) the first part of the query just matches the tiles and creates a collections of associated users. That is returned as a composite `tile` map which has some tile data and the colection of users for that tile. 2) is just taking all of the new tile maps and returning them as a collection. – Dave Bennett May 20 '15 at 12:03
  • 1
    Hah! Now I get it! I tested this out and understand this solution. I also understand how to use relationships much more clearly. This helps ALOT. – Wayne Cordrey May 20 '15 at 20:48
6

You shouldn't store another object as a nested property. As you correctly state, neo4j doesn't support that but even if it did, you shouldn't do it, because you should link the two with a relationship. That's the key strength of a graph database like neo4j, so you should play to that strength and use the relationships.

The server has a default JSON format that tends to output nodes as their own JSON objects. That means that practically speaking, since you're going to model this as two separate nodes with a relationship, you can't get the server by default to nest the JSON for one object underneath of the other. It won't nest the JSON that way because that's not how the data will be stored.

In this case, I'd use the REST services to fetch the JSON for each object individually, and then do the nesting yourself in your code -- your code is the only place where you'll know which property it should be nested under, and how that should be done.

FrobberOfBits
  • 17,634
  • 4
  • 52
  • 86
  • I agree that you should consider whether or not to store an object as a nested property, but it's incorrect to say you _can't_ do this, also I think it's too broad to say you shouldn't store any objects as nested properties. An example is something like a date, we can use a `DateTime` but you could also break that into 3 connected nodes of Day, Month and Year, which might be suitable or a massive overkill. – Charlotte Skardon May 13 '15 at 10:38
  • 1
    What I ended up doing is serializing that list of user objects, and putting that as a string into the user property on the Tile node. Requesting a Tile node outputs valid nested JSON, so I'm on the right track I think. I have some other bug, so its hard to say for certain if this works or not. Is this acceptable as a strategy? Both of your answers were excellent, stronger together- nice teamwork! – Wayne Cordrey May 13 '15 at 14:21
  • @ChrisSkardon point taken; of course with any technology there's very little that you fundamentally can't do. And I take what you mean by DateTime, but that's a special case, usually thought of as a built-in primitive type and not a complex user-defined type. I was thinking of complex domain/user-defined types like "Bank Account". *Could* you do it? Well yeah...sure, but it's hard to imagine *common* scenarios where that would be the better decision. (Of course you can also come up with uncommon niches where it might be preferable) – FrobberOfBits May 13 '15 at 16:18
  • @WayneCordrey I thought Dave Bennet's solution was creative and neat, I wish I had thought of it. Serializing to strings might work in a pinch, but if it were me I'd try to avoid that since it buys you the extra problem later of *deserializing* those strings back to objects. Not sure I understand why you don't just use a separate node and relationship, again, that's what would play to the strengths of the DB you're using. – FrobberOfBits May 13 '15 at 16:20
  • @WayneCordrey As you're using `Neo4jClient` you can create your own Json Serializer and pass it into the client, which will then use it for instances of your class (see http://stackoverflow.com/questions/23132187/can-neo4j-store-a-dictionary-in-a-node). @FrobberOfBits I totally agree with you, just wanted to make sure that someone who had a niche didn't read the comment and then give up :) – Charlotte Skardon May 15 '15 at 15:06
3

In addition to these answers, note that if you don't need to involve the subfield properties in any of your queries (e.g. search for Tiles where a User.name is "X"), you can simply serialise the objects fields to a string before insertion (e.g. with JSON.stringify), and unserialise them when reading from the DB.

This is especially useful when you want to "attach" structured data to a node, but that you don't much care about this data with regards to the relations in your DB (e.g. User preferences).

Jonathan H
  • 7,591
  • 5
  • 47
  • 80