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 { name: 'Joe' })-[rels:FRIEND*0..1]-(myfriend) WHERE 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:
-
First Get the list of all my friends (along with me) through
FRIEND
relationship (MATCH (me {name: 'Joe'})-[rels:FRIEND*0..1]-(myfriend)
). Also, theWHERE
predicate can be added to check whether the friend request is pending or confirmed. -
Get the latest status update of my friends through Status relationship (
MATCH (myfriend)-[:STATUS]-(latestupdate)
). -
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; adjust0..1
to whatever suits your case. -
Sort the status updates by posted date (
ORDER BY statusupdates.date DESC
). -
LIMIT
the number of updates you need in every query (LIMIT 3
).
Result
name | date | text |
---|---|---|
3 rows | ||
|
|
|
|
|
|
|
|
|
Try this query live create (_0 {`name`:"Bob"}) create (_1 {`date`:1, `name`:"bob_s1", `text`:"bobs status1"}) create (_2 {`date`:4, `name`:"bob_s2", `text`:"bobs status2"}) create (_3 {`name`:"Alice"}) create (_4 {`date`:2, `name`:"alice_s1", `text`:"Alices status1"}) create (_5 {`date`:5, `name`:"alice_s2", `text`:"Alices status2"}) create (_6 {`name`:"Joe"}) create (_7 {`date`:3, `name`:"joe_s1", `text`:"Joe status1"}) create (_8 {`date`:6, `name`:"joe_s2", `text`:"Joe status2"}) create (_0)-[:`FRIEND` {`status`:"CONFIRMED"}]->(_3) create (_0)-[:`STATUS`]->(_1) create (_1)-[:`NEXT`]->(_2) create (_3)-[:`FRIEND` {`status`:"PENDING"}]->(_6) create (_3)-[:`STATUS`]->(_4) create (_4)-[:`NEXT`]->(_5) create (_6)-[:`FRIEND` {`status`:"CONFIRMED"}]->(_0) create (_6)-[:`STATUS`]->(_7) create (_7)-[:`NEXT`]->(_8) ; MATCH (me {name: 'Joe'})-[rels:FRIEND*0..1]-(myfriend) WHERE 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
Here, the example shows how to add a new status update into the existing data for a user.
Query
MATCH (me) WHERE me.name='Bob' OPTIONAL MATCH (me)-[r:STATUS]-(secondlatestupdate) DELETE r CREATE (me)-[:STATUS]->(latest_update { text:'Status',date:123 }) 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:
-
Get the latest update (if it exists) of the user through the
STATUS
relationship (OPTIONAL MATCH (me)-[r:STATUS]-(secondlatestupdate)
). -
Delete the
STATUS
relationship betweenuser
andsecondlatestupdate
(if it exists), as this would become the second latest update now and only the latest update would be added through aSTATUS
relationship; all earlier updates would be connected to their subsequent updates through aNEXT
relationship. (DELETE r
). -
Now, create the new
statusupdate
node (with text and date as properties) and connect this with the user through aSTATUS
relationship (CREATE (me)-[:STATUS]->(latest_update { text:'Status',date:123 })
). -
Pipe over
statusupdate
or an empty list to the next query part (WITH latest_update, collect(secondlatestupdate) AS seconds
). -
Now, create a
NEXT
relationship between the latest status update and the second latest status update (if it exists) (FOREACH(x in seconds | CREATE (latest_update)-[:NEXT]->(x))
).
Result
new_status |
---|
1 row |
Nodes created: 1 |
Relationships created: 2 |
Properties set: 2 |
Relationships deleted: 1 |
|
Try this query live create (_0 {`name`:"Bob"}) create (_1 {`date`:1, `name`:"bob_s1", `text`:"bobs status1"}) create (_2 {`date`:4, `name`:"bob_s2", `text`:"bobs status2"}) create (_0)-[:`STATUS`]->(_1) create (_1)-[:`NEXT`]->(_2) ; MATCH (me) WHERE me.name='Bob' OPTIONAL MATCH (me)-[r:STATUS]-(secondlatestupdate) DELETE r CREATE (me)-[:STATUS]->(latest_update {text:'Status',date:123}) WITH latest_update, collect(secondlatestupdate) as seconds FOREACH(x in seconds | CREATE (latest_update)-[:NEXT]->(x)) RETURN latest_update.text as new_status