Help with Data Modeling for History App (C#, .NET)

Hi everyone,

I'm new to Neo4j and graph databases in general, I'm working on a C# project—a history app. I could really use some guidance on how to best model my data in a graph structure.

I'm still wrapping my head around when to use properties versus relationships. In relational databases, I’d typically use a foreign key as a property to link tables, but in Neo4j I’m not sure if just having the relationship between nodes is enough, or if I should also store some kind of property for that link.

I’m trying to figure out what approach scales better and fits more naturally with Neo4j’s graph model.

For example I have the following nodes:

  • Person
  • Ruler (inherits from Person)
  • War
  • Year

Here are a couple of questions I'm struggling with:

  1. War and Year Range:
    For a War, I want to store a yearFrom and yearTo. Should these be properties on the War node itself, or is it enough to create relationships with Year nodes like (:War)-[:STARTED_IN]->(:Year) and (:War)-[:ENDED_IN]->(:Year)?
  2. Ruler and Children:
    I want to connect a Ruler to their children (also Person nodes). Should this just be a (:Person)-[:CHILD_OF]->(:Person) relationship, or is it better to maintain a list of child nodes as a property on the parent? (I'm guessing relationships are the way to go, but want to be sure.)

I'm trying to model this cleanly and efficiently, and I’d appreciate any tips, best practices, or sample models from similar projects.

Thanks in advance for your help!

Trying to do some ORM analogy:

If Person is an object, properties are attributes of the person so that they would be columns inside a relationship DB.

If YEAR is a significant object in your ORM model, then it would be a table in your DB (e.g. "tax year"), otherwise it would be a column in a table.

(So like in ORM - there's no one size fits all).

In Neo4J you could use labels if your :Person and :Ruler to represent classes and specialisations - because you might want to read "all persons" and "all rulers" in some of your queries, making it very simple.

(Anita:Person { id:'123', firstName: 'Anita', lastName: 'Aleksic' })

(Julian:Person:Ruler { id:'234', firstName: 'Julian', lastName: 'MoveitMoveit', height: 60 })

(Robert:Person:Ruler { id:'345', firstName: 'Robert', lastName: 'MoveitMoveit', height: 55 })

(Julian)-[:childOf]->(Robert)


(Y1944:Year { value: 1944 })
(Y1960:Year { value: 1960 })
(Y1980:Year { value: 1980 })

(Julian)-[:ruling_start]->(Y1960)<-[:ruling_end]-(Robert)-[:ruling_start]->(Y1944)

(Anita)-[:birth]->(Y1980)

In Neo4J, there's no schema, so any node can have any properties. When you get to C#, you might have to read the labels to see which class you are going to instantiate (and check if properties exist in the node fetched).

2 Likes

In addition to @joshcornejo great answer, I typically will then think about the way the data will be displayed in the app. This will flesh out a lot of the queries you will need. If you put the data needed for the UI on a whiteboard, it becomes really obvious what data points are the most important.

With Neo4j, I typically will look at parameters in queries and if it's used more than once or twice to filter data, I promote it to a node or label. (I want to emphasize "filter", not just lookup)

1 Like

Thank you so much for taking the time to explain this so thoroughly! I was overthinking properties vs. relationships, but now I feel more confident moving forward with how I'm structuring things in Neo4j.

2 Likes

Really appreciate the extra insight! Mapping out the UI first is a great way to figure out which data matters most. Definitely going to watch for frequently-used parameters in my own queries. I’ll keep these tips in mind as I refine my model. Thanks again!

1 Like