Graph DB + ITS

Hello,
I am interesting to use graph DB in Intelligent Transportation Systems. I need to represent entities (nodes) and relations (edges) as well as their changes over time. So, node or edge will be represented with time. When I want to update node or edge, the old one will be archived and the new one will take place. So, updates are not destructive. Later, I can ask for a situation in a specific time.
Is this possible with Neo4j.

Hi @achraf.makni, yes it is possible. Neo4j support Temporal datatypes.

You can query max(timestamp) and get the latest record.

But I do have some questions -> If a node has already like 10 relationship, and when you update that node, I believe you want create a new node, along with all the 10 relationships. I believe this will create a overhead on the number of nodes and relationships.
What use case are you trying to solve ? Let me see if there is a better design approach.

I had an interesting time researching this. Originally, I had thought that you can create multiple labels for a Relationship or Remove a label from a relationship, but it appears that in Neo4J, every Relationship MUST have one and only one Label.

So, the trick is to make archived Relationships different from current ones

I can think of two ways of doing this.

First way is to add a property on a Relationship to indicate whether it is archived or not. I think that may have performance issues. In addition, it might be harder to hide the archived relationships. (I think Bloom can do this better than the Browser.)

Second way, is to rename the Relationship Label. You need APOC call to do this:

As an example, using the movie DB:

MATCH(p:Person {name:"Ed Harris"})-[r:ACTED_IN]->(m:Movie)
WITH collect(r) as rels,   // need a list of relationships
p as p, m as m  // need this to be able to return p and m
call apoc.refactor.rename.type("ACTED_IN", "EMOTED_IN", rels). // "type" means relationship label
YIELD committedOperations  // number of renames done
return p, m

I believe Bloom can better hide the archived relationships but the Browser will show all relationships whether you want them or not. Unfortunately, you need an Enterprise license to use Bloom.

Hi @achraf.makni,

I have created a sample code to illustrate your archival process. I have taken an example of a truck at various cities with a date and current property set to True.
Note this is a very simple demo I have created.

// create an empty database
match (n) detach delete (n);
//Create a truck
merge (n:Truck{truck_id:1}) ;

//Create an initial city
merge (l:Location{name:"Austin"});

//Create an initial relationship
match (n:Truck{truck_id:1}), (l:Location{name:"Austin"})
merge (n)-[p:Position{date:date('2020-12-20'),current:True}]->(l);

//verify result
match (t:Truck{truck_id:1})-[r:Position]->(l:Location)
return t.truck_id,r.date,r.current,l.name;

╒════════════╤════════════╤═══════════╤════════╕
│"t.truck_id"│"r.date"    │"r.current"│"l.name"│
╞════════════╪════════════╪═══════════╪════════╡
│1           │"2020-12-20"│true       │"Austin"│
└────────────┴────────────┴───────────┴────────┘


//Add a new city, with date, with current status

match (t:Truck{truck_id:1})-[r:Position]->(l:Location)
with max(r.date) as max_date,t,l
match (t)-[r]->(l)
where r.date=max_date
set r.current=False
with t
merge (l:Location{name:"Houston"})
merge (t)-[r:Position{date:date('2020-12-21'),current:True}]->(l)
return distinct t.truck_id,r.date,r.current,l.name ;

╒════════════╤════════════╤═══════════╤═════════╕
│"t.truck_id"│"r.date"    │"r.current"│"l.name" │
╞════════════╪════════════╪═══════════╪═════════╡
│1           │"2020-12-21"│true       │"Houston"│
└────────────┴────────────┴───────────┴─────────┘

//Verify

match (t:Truck{truck_id:1})-[r:Position]->(l:Location)
return t.truck_id,r.date,r.current,l.name

╒════════════╤════════════╤═══════════╤═════════╕
│"t.truck_id"│"r.date"    │"r.current"│"l.name" │
╞════════════╪════════════╪═══════════╪═════════╡
│1           │"2020-12-21"│true       │"Houston"│
├────────────┼────────────┼───────────┼─────────┤
│1           │"2020-12-20"│false      │"Austin" │
└────────────┴────────────┴───────────┴─────────┘


//Add another city

match (t:Truck{truck_id:1})-[r:Position]->(l:Location)
with max(r.date) as max_date,t,l
match (t)-[r]->(l)
where r.date=max_date
set r.current=False
with t
merge (l:Location{name:"Dallas"})
merge (t)-[r:Position{date:date('2020-12-22'),current:True}]->(l)
return distinct t.truck_id,r.date,r.current,l.name ;

//Result- 
╒════════════╤════════════╤═══════════╤════════╕
│"t.truck_id"│"r.date"    │"r.current"│"l.name"│
╞════════════╪════════════╪═══════════╪════════╡
│1           │"2020-12-22"│true       │"Dallas"│
└────────────┴────────────┴───────────┴────────┘


//Verify

match (t:Truck{truck_id:1})-[r:Position]->(l:Location)
return t.truck_id,r.date,r.current,l.name

╒════════════╤════════════╤═══════════╤═════════╕
│"t.truck_id"│"r.date"    │"r.current"│"l.name" │
╞════════════╪════════════╪═══════════╪═════════╡
│1           │"2020-12-22"│true       │"Dallas" │
├────────────┼────────────┼───────────┼─────────┤
│1           │"2020-12-21"│false      │"Houston"│
├────────────┼────────────┼───────────┼─────────┤
│1           │"2020-12-20"│false      │"Austin" │
└────────────┴────────────┴───────────┴─────────┘

//Query the latest date
match (t:Truck)-[r:Position]->(l:Location)
with  max(r.date) as max_date
match (t)-[r:Position{date:max_date}]->(l)
return t as truck_id,r as  date_status,l as location_name;

╒══════════════╤════════════════════════════════════╤═════════════════╕
│"truck_id"    │"date_status"                       │"location_name"  │
╞══════════════╪════════════════════════════════════╪═════════════════╡
│{"truck_id":1}│{"date":"2020-12-22","current":true}│{"name":"Dallas"}│
└──────────────┴────────────────────────────────────┴─────────────────┘

Things to improve -

  1. Relationship constraints needs to be added. (Enterprise edition)
  2. OOP Programming required for production-grade adding,updating,removing nodes or relationships. (my favorite is python)
  3. use of apoc procedures for better functionality.
  4. Use of NOT operator automatically on the Boolean to set the opposite of the status, rather than setting it manually in the query.
  5. Python Some decorators for pre-checking functionalities like if provided date is less than "the" max and other dates ..etc etc (again, based on my sample demo set)
  6. Python Use of list (similar to collect), and use yield to get the max.

With your limited use case, this is the demo I have come up with. If you need more details, please, feel free to contact me.