How to Splice a Linked Lists in Cypher

161 Views Asked by At

I have a Linked-List of LogEntries. Each entry has a date. Every user has exactly 1 Log.

What's the best way to splice in a new log entry if I receive a date in the past?

(log:Log)
(e1:LogEntry {date: Date('2020-03-19')})
(e2:LogEntry {date: Date('2020-03-17')})
…

CREATE (log)-[:PREV_ENTRY]->(e1)
CREATE (e1)-[:PREV_ENTRY]->(e2)
CREATE (e2)-[:PREV_ENTRY]->(e3)
CREATE (e3)-[:PREV_ENTRY]->(e4)

I'm building a public graphql API to trace contact of COVID-19 case: https://github.com/mmmoli/contacttracing.app-graphql-api/blob/master/src/appConfigs/schema.ts#L55

Any help would be awesome!

Inspiration from Max's Fraud post: https://maxdemarzi.com/2019/08/19/finding-fraud/

2

There are 2 best solutions below

2
Marcus On BEST ANSWER

Is this when you're first creating the database (i.e. the entries weren't sorted to start) or is this entry appearing later on and you're trying to update/insert with a new entry? If I'm understanding correctly, you want to insert a node in the chain for the log. How about

EDIT: Included additional constraint to make sure matching the chain finds one set of nodes between which to insert the new entry

CREATE (eInsert:LogEntry:NewEntry {date: Date('2020-03-18')})
WITH eInsert

MATCH (log:Log {id: "log1"})
WITH log, eInsert

MATCH (log)-[:PREV_ENTRY*]->(e1)-[oldLink:PREV_ENTRY]->(e2:LogEntry)
WHERE e2.date < eInsert.date
  AND e1.date >= eInsert.date 
WITH e1, e2, eInsert, oldLink

CREATE (e1)-[:PREV_ENTRY]->(eInsert)-[:PREV_ENTRY]->(e2)
DELETE oldLink

That should work, but for completeness, here's the script I used to create the sample chain

CREATE (log:Log {id: 'log1'})
CREATE (e1:LogEntry {date: Date('2020-03-30')})
CREATE (e2:LogEntry {date: Date('2020-03-28')})
CREATE (e3:LogEntry {date: Date('2020-03-23')})
CREATE (e4:LogEntry {date: Date('2020-03-17')})
CREATE (e5:LogEntry {date: Date('2020-03-09')})

CREATE (log)-[:PREV_ENTRY]->(e1)
CREATE (e1)-[:PREV_ENTRY]->(e2)
CREATE (e2)-[:PREV_ENTRY]->(e3)
CREATE (e3)-[:PREV_ENTRY]->(e4)
CREATE (e4)-[:PREV_ENTRY]->(e5)
0
mmmoli On

I've come up with this so far:

MATCH p=(log:Log {id: "log1"})-[:PREV_ENTRY*]->(e:LogEntry)
FOREACH (rel IN relationships(p) | 
  DELETE rel
)
WITH e, log
ORDER BY e.id DESC
WITH collect(e) AS entries, log, e
CALL apoc.nodes.link(entries, 'PREV_ENTRY')
WITH apoc.agg.first(e) AS latest, log
CREATE (log)-[:PREV_ENTRY]->(latest)
RETURN latest

But it doesn't work quite yet.