Skip to content

Commit

Permalink
After cursor implemented, improved decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-cowley committed Jun 11, 2019
1 parent 691d2db commit 41aac99
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 46 deletions.
4 changes: 0 additions & 4 deletions src/main/java/ac/owley/social/feed/Feed.java
Expand Up @@ -31,10 +31,6 @@ public Stream<PostResult> socialFeed(
) {
GetFeed feed = new GetFeed(db, log);

if ( limit == null ) {
limit = 10d;
}

return feed.forUser(username, cursorType, sinceId, limit);
}

Expand Down
1 change: 1 addition & 0 deletions src/main/java/ac/owley/social/feed/Properties.java
Expand Up @@ -7,5 +7,6 @@ public class Properties

public final static String postId = "id";
public final static String postCreatedAt = "createdAt";
public final static String postBody = "body";

}
85 changes: 85 additions & 0 deletions src/main/java/ac/owley/social/feed/procedures/Decorator.java
@@ -0,0 +1,85 @@
package ac.owley.social.feed.procedures;

import ac.owley.social.feed.Properties;
import ac.owley.social.feed.RelationshipTypes;
import ac.owley.social.feed.result.PostResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.logging.Log;

public class Decorator
{


private final static int INTERACTION_USERS = 2;

public static Map<String, Object> decoratePost(Node post) {
Map<String, Object> output = new HashMap<>();

output.put( Properties.postId, post.getProperty(Properties.postId) );
output.put( Properties.postCreatedAt, post.getProperty(Properties.postCreatedAt) );
output.put( Properties.postBody, post.getProperty(Properties.postBody) );

// POSTED
// Node author = post.getSingleRelationship( RelationshipTypes.POSTED, Direction.INCOMING ).getStartNode();

// output.put( "author", decorateAuthor( author ));

// REPOSTED,
output.putAll( decorateInteraction(post, "reposted", RelationshipTypes.REPOSTED, Direction.INCOMING ) );

// COMMENTED
output.putAll( decorateInteraction(post, "reposted", RelationshipTypes.COMMENTED, Direction.INCOMING ) );

return output;
}

public static Map<String, Object> decorateAuthor(Node author) {
Map<String, Object> output = new HashMap<>();

output.put( Properties.username, author.getProperty(Properties.username) );

return output;
}

public static Map<String, Object> decorateInteraction(Node post, String key, RelationshipType relationshipType, Direction direction) {
Map<String, Object> output = new HashMap<>();

// Count
int degree = post.getDegree(relationshipType, direction);
output.put(key +"_count", degree);

// People who have performed the interaction
if ( degree > 0 ) {
Iterator<Relationship> rels = post.getRelationships(relationshipType, direction).iterator();

List<String> interactors = new ArrayList<>();

int names = 0;

while ( rels.hasNext() && names < INTERACTION_USERS ) {
Relationship rel = rels.next();

Node other = rel.getOtherNode(post);

interactors.add( (String) other.getProperty( Properties.username ) );
}

output.put(key +"_users", interactors);
}

//

}

}
66 changes: 57 additions & 9 deletions src/main/java/ac/owley/social/feed/procedures/GetFeed.java
Expand Up @@ -26,6 +26,7 @@
import org.neo4j.logging.Log;

import static ac.owley.social.feed.Time.POSTED_ON;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;

public class GetFeed
Expand All @@ -34,7 +35,6 @@ public class GetFeed
private final static String CURSOR_TYPE_BEFORE = "before";
private final static String CURSOR_TYPE_AFTER = "after";


private final GraphDatabaseService db;

private final Log log;
Expand Down Expand Up @@ -73,12 +73,19 @@ public Stream<PostResult> forUser(String username, String cursorType, String sin
cursorType = CURSOR_TYPE_BEFORE;
}

List<Node> output;

if ( cursorType.equals(CURSOR_TYPE_AFTER) ) {
// return getPostsAfter(following, dateTime);
output = getPostsAfter(following, dateTime, limit);
}
else {
output = getPostsBefore(following, dateTime, limit);
}

// Get the next X posts before this post
return getPostsBefore(following, dateTime, limit);
return output
.stream()
.map(e -> new PostResult( Decorator.decoratePost( e ) ));
}

private Node getUser(String username) {
Expand Down Expand Up @@ -113,11 +120,52 @@ private ZonedDateTime getPostTime(String postId) {
return ZonedDateTime.now();
}

// TODO: getPostsAfter
// Same as below, but run in reverse order
// Get all posts from each day in ascending order from cursor, up until the limit
// Take the first x rows
// Reverse for display
// Return

private List<Node> getPostsAfter(Set users, ZonedDateTime dateTime, Double limit) {
final List<Node> output = new ArrayList<>( );

ZonedDateTime originalDateTime = dateTime;

// Set a maximum date to stop the code running forever
ZonedDateTime ceiling = ZonedDateTime.now();

// Get all posts from each day in ascending order from the cursor, up until the limit has been reached
while ( output.size() < limit && dateTime.isBefore( ceiling ) ) {
List<Node> posts = getPostsOnDate(users, dateTime, naturalOrder());

posts.forEach( n -> {
ZonedDateTime postCreatedAt = (ZonedDateTime) ((Node) n).getProperty( Properties.postCreatedAt );

// Add to the
if ( postCreatedAt.isAfter( originalDateTime ) ) {
output.add( n );
}
} );

// Try again with the day after
dateTime = dateTime.plusDays(1);
}

// Trim to size and return
List<Node> sortedOutput = output.subList( 0, Math.min(limit.intValue(), output.size()) );

// Sort into descending order for the UI
sortedOutput.sort(
Comparator.comparing(
n -> ( (ZonedDateTime) ((Node) n).getProperty( Properties.postCreatedAt ) ).toEpochSecond(),
reverseOrder()
)
);

return sortedOutput;
}

private Stream<PostResult> getPostsBefore(Set users, ZonedDateTime dateTime, Double limit) {
List<PostResult> output = new ArrayList<>( );
private List<Node> getPostsBefore(Set users, ZonedDateTime dateTime, Double limit) {
final List<Node> output = new ArrayList<>( );

ZonedDateTime originalDateTime = dateTime;

Expand All @@ -132,7 +180,7 @@ private Stream<PostResult> getPostsBefore(Set users, ZonedDateTime dateTime, Dou

// Add to the
if ( postCreatedAt.isBefore( originalDateTime ) ) {
output.add( new PostResult( n ) );
output.add( n );
}
} );

Expand All @@ -141,7 +189,7 @@ private Stream<PostResult> getPostsBefore(Set users, ZonedDateTime dateTime, Dou
}

// Trim to size and return
return output.subList( 0, Math.min(limit.intValue(), output.size()) ).stream();
return output.subList( 0, Math.min(limit.intValue(), output.size()) );
}

private List<Node> getPostsOnDate(Set<Node> users, ZonedDateTime date, Comparator comparator) {
Expand Down
19 changes: 2 additions & 17 deletions src/main/java/ac/owley/social/feed/result/PostResult.java
Expand Up @@ -2,28 +2,13 @@

import java.util.Map;

import org.neo4j.graphdb.Node;

public class PostResult
{

public Node post;

public Node author;

// POSTED | REPOSTED | COMMENTED

public Map<String, Object> post;

public PostResult(Node post) {
public PostResult(Map<String, Object> post) {
this.post = post;
}

public PostResult(Node post, Node author) {
this.post = post;
this.author = author;
}

public Node getPost() {
return post;
}
}
52 changes: 36 additions & 16 deletions src/test/java/ac/owley/social/feed/FeedTest.java
Expand Up @@ -9,6 +9,7 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
Expand Down Expand Up @@ -38,21 +39,21 @@ public class FeedTest
" CREATE (u1)-[:FOLLOWS]->(u4) " +
" CREATE (u1)-[:FOLLOWS]->(u5) " +

" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '7', createdAt: datetime('2019-06-10T13:40:00.0000Z')})" +
" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '6', createdAt: datetime('2019-06-10T13:30:00.0000Z')})" +
" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '5', createdAt: datetime('2019-06-10T12:00:00.0000Z')})" +
" CREATE (u3)-[:POSTED_ON_2019_06_10]->( :Post {id: '4', createdAt: datetime('2019-06-10T11:00:00.0000Z')})" +
" CREATE (u4)-[:POSTED_ON_2019_06_10]->( :Post {id: '3', createdAt: datetime('2019-06-10T10:00:00.0000Z')})" +
" CREATE (u5)-[:POSTED_ON_2019_06_10]->( :Post {id: '2', createdAt: datetime('2019-06-10T09:00:00.0000Z')})" +
" CREATE (u5)-[:POSTED_ON_2019_06_09]->(p1:Post {id: '1', createdAt: datetime('2019-06-09T13:00:00.0000Z')})" +
" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '7', body: 'post 7', createdAt: datetime('2019-06-10T13:40:00.0000Z')})" +
" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '6', body: 'post 6', createdAt: datetime('2019-06-10T13:30:00.0000Z')})" +
" CREATE (u2)-[:POSTED_ON_2019_06_10]->( :Post {id: '5', body: 'post 5', createdAt: datetime('2019-06-10T12:00:00.0000Z')})" +
" CREATE (u3)-[:POSTED_ON_2019_06_10]->( :Post {id: '4', body: 'post 4', createdAt: datetime('2019-06-10T11:00:00.0000Z')})" +
" CREATE (u4)-[:POSTED_ON_2019_06_10]->( :Post {id: '3', body: 'post 3', createdAt: datetime('2019-06-10T10:00:00.0000Z')})" +
" CREATE (u5)-[:POSTED_ON_2019_06_10]->( :Post {id: '2', body: 'post 2', createdAt: datetime('2019-06-10T09:00:00.0000Z')})" +
" CREATE (u5)-[:POSTED_ON_2019_06_09]->(p1:Post {id: '1', body: 'post 1', createdAt: datetime('2019-06-09T13:00:00.0000Z')})" +
" CREATE (u3)-[:COMMENTED]->(p1)" +
" CREATE (u2)-[:COMMENTED]->(p1)" +
" CREATE (u4)-[:COMMENTED]->(p1)"
);

@Test
public void shouldGetLatestPostsAndLimitNumberOfResults() {
List<String> ids = runAndGetIds("CALL social.feed('adam', 1) YIELD post, author RETURN *");
List<String> ids = runAndGetIds("CALL social.feed('adam', 1)");

assertEquals(1, ids.size());

Expand All @@ -61,14 +62,14 @@ public void shouldGetLatestPostsAndLimitNumberOfResults() {

@Test
public void shouldReturnResultsOverMultipleDays() {
List<String> ids = runAndGetIds("CALL social.feed('adam', 10) YIELD post, author RETURN *");
List<String> ids = runAndGetIds("CALL social.feed('adam', 10)");

assertEquals(7, ids.size());
}

@Test
public void shouldCutOffResultsInDayForBeforeCursor() {
List<String> ids = runAndGetIds("CALL social.feed('adam', 3, 'before', '7') YIELD post, author RETURN *");
List<String> ids = runAndGetIds("CALL social.feed('adam', 3, 'before', '7')");

assertEquals(3, ids.size());

Expand Down Expand Up @@ -96,23 +97,42 @@ public void shouldPaginateBeforeCursor() {
}
}

private List<String> runAndGetIds(String query) {
@Test
public void shouldPaginateAfterCursor() {
GraphDatabaseService db = neo4j.getGraphDatabaseService();

try ( Transaction tx = db.beginTx() ) {
Result all = db.execute("CALL social.feed('adam', 4, 'after', '1') YIELD post RETURN collect(post.id) AS ids");
List<String> allIds = (List) all.next().get("ids");

Result batches = db.execute(
"CALL social.feed('adam', 2, 'after', '1') YIELD post WITH collect(post.id) AS first " +
"CALL social.feed('adam', 2, 'after', first[0]) YIELD post WITH first, collect(post.id) AS second " +
"RETURN second + first AS ids"
);
List<String> batchIds = (List) batches.next().get("ids");

assertEquals(allIds, batchIds);
}
}

private List<String> runAndGetIds(String call) {
GraphDatabaseService db = neo4j.getGraphDatabaseService();

List<String> ids = new ArrayList<>( );

try ( Transaction tx = db.beginTx() ) {
Result res = db.execute(query);
Result res = db.execute(call + " YIELD post RETURN *");

while ( res.hasNext() ) {
Node post = (Node) res.next().get("post");
Map post = (Map) res.next().get("post");

ids.add( (String) post.getProperty("id") );
ids.add( (String) post.get("id") );

System.out.println(
post.getProperty("id")
post.get("id")
+ " -- "
+ post.getProperty("createdAt")
+ post.get("createdAt")
);
}

Expand Down

0 comments on commit 41aac99

Please sign in to comment.