8.11. Implementing newsfeeds in a graph

cypher-newsfeed-graph.svg

Implementation of newsfeed or timeline feature is a frequent requirement for social applications. The following exmaples are inspired by Newsfeed feature powered by Neo4j Graph Database. The query asked here is:

Starting at me, retrieve the time-ordered status feed of the status updates of me and and all friends that are connected via a CONFIRMED FRIEND relationship to me.

Query. 

MATCH me-[rels:FRIEND*0..1]-myfriend
WHERE me.name = 'Joe' AND ALL (r IN rels
WHERE r.status = 'CONFIRMED')
WITH myfriend
MATCH myfriend-[:STATUS]-latestupdate-[:NEXT*0..1]-statusupdates
RETURN myfriend.name AS name, statusupdates.date AS date, statusupdates.text AS text
ORDER BY statusupdates.date DESC LIMIT 3

To understand the strategy, let’s divide the query into five steps:

  1. First Get the list of all my friends (along with me) through FRIEND relationship (MATCH me-[rels:FRIEND*0..1]-myfriend). Also, the WHERE predicate can be added to check whether the friend request is pending or confirmed.
  2. Get the latest status update of my friends through Status relationship (MATCH myfriend-[:STATUS]-latestupdate).
  3. Get subsequent status updates (along with the latest one) of my friends through NEXT relationships (MATCH myfriend-[:STATUS]-latestupdate-[:NEXT*0..1]-statusupdates) which will give you the latest and one additional statusupdate, adjust 0..1 to whatever suits your case.
  4. Sort the status updates by posted date (ORDER BY statusupdates.date DESC).
  5. LIMIT the number of updates you need in every query (LIMIT x SKIP x*y).

Result

namedatetext
3 rows

"Joe"

6

"Joe status2"

"Bob"

4

"bobs status2"

"Joe"

3

"Joe status1"


Here, the example shows how to add a new status update into the existing data for a user.

Query. 

START me=node:node_auto_index(name='Bob')
MATCH me-[r?:STATUS]-secondlatestupdate
WHERE me.name='Bob'
CREATE me-[:STATUS]->(latest_update { text:'Status',date:123 })
DELETE r
WITH latest_update, collect(secondlatestupdate) AS seconds
FOREACH (x IN seconds | CREATE latest_update-[:NEXT]->x)
RETURN latest_update.text AS new_status

Dividing the query into steps, this query resembles adding new item in middle of a doubly linked list:

  1. Get the latest update (if it exists) of the user through the STATUS relationship (MATCH me-[r?:STATUS]-secondlatestupdate).
  2. Delete the STATUS relationship between user and secondlatestupdate (if it exists), as this would become the second latest update now and only the latest update would be added through a STATUS relationship, all earlier updates would be connected to their subsequent updates through a NEXT relationship. (DELETE r).
  3. Now, create the new statusupdate node (with text and date as properties) and connect this with the user through a STATUS relationship (CREATE me-[:STATUS]->(latest_update{text:'Status',date:123})).
  4. Now, create a NEXT relationship between the latest status update and the second latest status update (if it exists) (CREATE latest_update-[:NEXT]-secondlatestupdate WHERE secondlatestupdate <> null).

Result

new_status
1 row
Nodes created: 1
Relationships created: 2
Properties set: 2
Relationships deleted: 1

"Status"


cypher-insertstatusupdate-graph.svg