Skip to content

Commit

Permalink
cache results
Browse files Browse the repository at this point in the history
  • Loading branch information
maxdemarzi committed Apr 18, 2017
1 parent fd99989 commit d202602
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 57 deletions.
87 changes: 52 additions & 35 deletions src/main/java/com/maxdemarzi/search/Search.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.maxdemarzi.search;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.maxdemarzi.Labels;
import com.maxdemarzi.RelationshipTypes;
import com.maxdemarzi.users.Users;
Expand All @@ -26,9 +28,8 @@
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.maxdemarzi.Properties.*;
import static com.maxdemarzi.Time.utc;
Expand All @@ -45,6 +46,29 @@ public class Search {
private static int postLabelId;
private static int statusPropertyId;

// Cache
private static LoadingCache<String, ArrayList<Long>> searches = Caffeine.newBuilder()
.maximumSize(50_000)
.expireAfterWrite(1, TimeUnit.DAYS)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(Search::performSearch);

private static ArrayList<Long> performSearch(String term) throws SchemaRuleNotFoundException, IndexNotFoundKernelException {
ThreadToStatementContextBridge ctx = dbapi.getDependencyResolver().resolveDependency(ThreadToStatementContextBridge.class);
KernelStatement st = (KernelStatement)ctx.get();
ReadOperations ops = st.readOperations();
IndexDescriptor descriptor = ops.indexGetForLabelAndPropertyKey(postLabelId, statusPropertyId);
IndexReader reader = st.getStoreStatement().getIndexReader(descriptor);

PrimitiveLongIterator hits = reader.containsString(term);
ArrayList<Long> results = new ArrayList<>();
while(hits.hasNext()) {
results.add(hits.next());
}
return results;
}


public Search(@Context GraphDatabaseService db) throws NoSuchMethodException {
this.dbapi = (GraphDatabaseAPI) db;
try (Transaction tx = db.beginTx()) {
Expand All @@ -54,9 +78,6 @@ public Search(@Context GraphDatabaseService db) throws NoSuchMethodException {
statusPropertyId = ops.propertyKeyGetForName(STATUS);
tx.success();
}

// Method method = IndexReader.class.getDeclaredMethod("getIndexSearcher");
// method.setAccessible(true);
}

@GET
Expand All @@ -75,43 +96,39 @@ public Response getSearch(@QueryParam("q") final String q,
Long latest = dateTime.toEpochSecond(ZoneOffset.UTC);

try (Transaction tx = db.beginTx()) {
Node user = null;
if (username != null) {
user = Users.findUser(username, db);
}
final Node user = Users.findUser(username, db);
ArrayList<Long> postIds = searches.get(q);
Queue<Node> posts = new PriorityQueue<>(Comparator.comparing(m -> (Long) m.getProperty(TIME), reverseOrder()));

ThreadToStatementContextBridge ctx = dbapi.getDependencyResolver().resolveDependency(ThreadToStatementContextBridge.class);
KernelStatement st = (KernelStatement)ctx.get();
ReadOperations ops = st.readOperations();
IndexDescriptor descriptor = ops.indexGetForLabelAndPropertyKey(postLabelId, statusPropertyId);
IndexReader reader = st.getStoreStatement().getIndexReader(descriptor);
PrimitiveLongIterator hits = reader.containsString(q);
while(hits.hasNext()) {
Node post = db.getNodeById(hits.next());
postIds.forEach(postId -> {
Node post = db.getNodeById(postId);
Long time = (Long)post.getProperty("time");
if(time < latest) {
Map<String, Object> properties = post.getAllProperties();
Node author = getAuthor(post, (Long) properties.get(TIME));
properties.put(USERNAME, author.getProperty(USERNAME));
properties.put(NAME, author.getProperty(NAME));
properties.put(HASH, author.getProperty(HASH));
properties.put(LIKES, post.getDegree(RelationshipTypes.LIKES));
properties.put(REPOSTS, post.getDegree() - 1 - post.getDegree(RelationshipTypes.LIKES));
if (user != null) {
properties.put(LIKED, userLikesPost(user, post));
properties.put(REPOSTED, userRepostedPost(user, post));
}
results.add(properties);
posts.add(post);
}
});

}
int count = 0;
while (count < limit && !posts.isEmpty()) {
count++;
Node post = posts.poll();
Map<String, Object> properties = post.getAllProperties();
Node author = getAuthor(post, (Long) properties.get(TIME));

properties.put(USERNAME, author.getProperty(USERNAME));
properties.put(NAME, author.getProperty(NAME));
properties.put(HASH, author.getProperty(HASH));
properties.put(LIKES, post.getDegree(RelationshipTypes.LIKES));
properties.put(REPOSTS, post.getDegree() - 1 - post.getDegree(RelationshipTypes.LIKES));
if (user != null) {
properties.put(LIKED, userLikesPost(user, post));
properties.put(REPOSTED, userRepostedPost(user, post));
}
results.add(properties);
}
}
results.sort(Comparator.comparing(m -> (Long) m.get(TIME), reverseOrder()));

return Response.ok().entity(objectMapper.writeValueAsString(
results.subList(0, Math.min(results.size(), limit))))
.build();
return Response.ok().entity(objectMapper.writeValueAsString(results)).build();
}

}
63 changes: 41 additions & 22 deletions src/main/java/com/maxdemarzi/tags/Tags.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.maxdemarzi.tags;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.maxdemarzi.Labels;
import com.maxdemarzi.RelationshipTypes;
import org.codehaus.jackson.map.ObjectMapper;
Expand All @@ -14,6 +16,7 @@
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -29,6 +32,41 @@ public class Tags {
private static final Pattern hashtagPattern = Pattern.compile("#(\\S+)");

private static final ObjectMapper objectMapper = new ObjectMapper();
private static GraphDatabaseService db;

public Tags(@Context GraphDatabaseService graphDatabaseService) {
db = graphDatabaseService;
}

// Cache
private static LoadingCache<String, List<Map<String, Object>>> trends = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.DAYS)
.refreshAfterWrite(5, TimeUnit.MINUTES)
.build(Tags::getTrends);

private static List<Map<String, Object>> getTrends(String key) {
ArrayList<Map<String, Object>> results = new ArrayList<>();
LocalDateTime dateTime = LocalDateTime.now(utc);
RelationshipType tagged = RelationshipType.withName("TAGGED_ON_" +
dateTime.format(dateFormatter));
try (Transaction tx = db.beginTx()) {
ResourceIterator<Node> tags = db.findNodes(Labels.Tag);
while (tags.hasNext()) {
Node tag = tags.next();
int taggings = tag.getDegree(tagged, Direction.INCOMING);
if ( taggings > 0) {
HashMap<String, Object> result = new HashMap<>();
result.put(NAME, tag.getProperty(NAME));
result.put(COUNT, taggings);
results.add(result);
}
}
tx.success();
}

results.sort(Comparator.comparing(m -> (Integer) m.get(COUNT), reverseOrder()));
return results.subList(0, Math.min(results.size(), 10));
}

@GET
@Path("/{hashtag}")
Expand Down Expand Up @@ -112,31 +150,12 @@ public static void createTags(Node post, HashMap<String, Object> input, LocalDa

@GET
public Response getTrends(@Context GraphDatabaseService db) throws IOException {
ArrayList<Map<String, Object>> results = new ArrayList<>();
LocalDateTime dateTime = LocalDateTime.now(utc);
RelationshipType tagged = RelationshipType.withName("TAGGED_ON_" +
dateTime.format(dateFormatter));
List<Map<String, Object>> results;
try (Transaction tx = db.beginTx()) {
ResourceIterator<Node> tags = db.findNodes(Labels.Tag);
while (tags.hasNext()) {
Node tag = tags.next();
int taggings = tag.getDegree(tagged, Direction.INCOMING);
if ( taggings > 0) {
HashMap<String, Object> result = new HashMap();
result.put(NAME, tag.getProperty(NAME));
result.put(COUNT, taggings);
results.add(result);
}

}
tx.success();
results = trends.get("trends");
}

results.sort(Comparator.comparing(m -> (Integer) m.get(COUNT), reverseOrder()));

return Response.ok().entity(objectMapper.writeValueAsString(
results.subList(0, Math.min(results.size(), 10))))
.build();
return Response.ok().entity(objectMapper.writeValueAsString(results)).build();

}
}
1 change: 1 addition & 0 deletions src/main/java/com/maxdemarzi/users/Users.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public Response removeFollows(@PathParam("username") final String username,
}

public static Node findUser(String username, @Context GraphDatabaseService db) {
if (username == null) { return null; }
Node user = db.findNode(Labels.User, USERNAME, username);
if (user == null) { throw UserExceptions.userNotFound; }
return user;
Expand Down

0 comments on commit d202602

Please sign in to comment.