Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Neo4j Stored Procedure Training Part 2

1,533 views

Published on

How to build Neo4j Stored Procedures, Part 2

Published in: Education
  • Be the first to comment

Neo4j Stored Procedure Training Part 2

  1. 1. PERFORMANCETESTING Using Gatling
  2. 2. GATLING.IO https://gatling.io/docs/current/cheat-sheet/
  3. 3. NEW SCALA CLASS Right Click on “scala” directory.
  4. 4. NAME IT. Go with “NetworkCount”.
  5. 5. import io.gatling.core.Predef._ import io.gatling.core.scenario.Simulat ion import io.gatling.http.Predef._ class NetworkCount extends Simulation { // Setup server, username and password val httpProtocol = http Your password.
  6. 6. // Use a data file for our requests and repeat values if we get to the end. val feeder = csv("data/usernames.csv").circular // The cypher queries we will test val count = """CALL com.maxdemarzi.network.count($username, 4)""" val cypherQuery = """{"statements" : [{"statement" : "%s", "parameters" : { "username": "${username}" }}]}""" .format(count)
  7. 7. val scn = scenario("NetworkCounts") .during(30 ) { feed(feeder) .exec( http("network count") .post("/db/data/ transaction/commit") .body(StringBody(cypherQu ery)) .asJson .check(status.is(200)) )
  8. 8. setUp( scn.inject(atOnceUsers (4)), ).protocols(httpProtoc ol)
  9. 9. /* If you want to see the response from the server, add the following to the .check .check(bodyString.saveAs("BODY")) ) .exec(session => { val response = session("BODY").as[String] println(s"Response body: n$response") session } */
  10. 10. MATCH (n:User) RETURN n.username LIMIT 10000
  11. 11. EXPORT CSV The one time this is useful.
  12. 12. COPY IT Paste it in your “src/test/resources/data/“ directory of the new project.
  13. 13. BACKTO INTELLIJ You see it right?
  14. 14. RENAME IT Right Click on it, choose “Refactor”, choose “Rename…”.
  15. 15. NAME IT Call it “usernames.csv”. Stick with me this time.
  16. 16. OPEN IT, EDIT IT Remove the “n.” before “username”.
  17. 17. RUN IT Right Click on “Engine”, Choose “Run ‘Engine’ ”.
  18. 18. GIVE IT A DESCRIPTION It will wait until you hit “Enter”.
  19. 19. 30 SECONDS LATER Highlight the filename, and Control-C.
  20. 20. NUMBERS AND CHARTS 5.677 Requests/Second…Not bad.
  21. 21. COPYTHE SIMULATION Right Click on “Network Count”.
  22. 22. PASTE IT In the same directory.
  23. 23. RENAME IT Skip 2, have it end in 3.
  24. 24. // The cypher queries we will test val count = """CALL com.maxdemarzi.network.count3($username, 4)""" val cypherQuery = """{"statements" : [{"statement" : "%s", "parameters" : { "username": "$ {username}" }}]}""" .format(count) val scn = scenario("NetworkCounts") .during(30 ) { feed(feeder) .exec( http("network count 3")
  25. 25. REPEAT FOR 4 AND 5.
  26. 26. RUN IT AGAIN You got this.
  27. 27. CHOICES 0-3 are given as prompts for which test to run.
  28. 28. CHOOSE 3 NetworkCount5 FTW. Give it a description and hit “Enter”.
  29. 29. 30 SECONDS LATER Highlight the filename, and Control-C.
  30. 30. FASTER 17.5 Requests/Second…Nicer.
  31. 31. PROFILING What is this thing doing?
  32. 32. YOURKIT https://www.yourkit.com/ comes with 15 day trial.
  33. 33. GET IT Windows, MacOS or this is not the year of linux on the desktop.
  34. 34. ALSO GETTHE ZIP From the Linux option. https://www.yourkit.com/java/profiler/download/
  35. 35. GET STARTED WITH PROFILER It should look like this.
  36. 36. WHICH APP? I don’t see Neo4j…
  37. 37. COMMERCIAL ENTRY POINT Don’t worry about the error message.
  38. 38. ONCE IT CONNECTS Click on CPU usage telemetry.
  39. 39. XRAYVISION Highlight a section of time.
  40. 40. MAKE IT WORK Run the query without the arrow.
  41. 41. MATCH (u:User)- [*1..4]-(c) WHERE u.username = 'Khloe17' RETURN count(DISTINCT c)
  42. 42. GREEN AREA Highlight the green area.
  43. 43. EXPAND IT Right Click on “AllThreads”.
  44. 44. DOWN ANDTOTHE RIGHT Scroll until you see PruningVarLengthExpandPipe.scala
  45. 45. STORED PROC Run the stored procedure now.
  46. 46. SIZE MATTERS The green area is too small, can’t really tell from a single call.
  47. 47. GATLINGTOTHE RESCUE Run the Engine on the PerformanceTest project.
  48. 48. MORE GREEN Ok, this we can work with.
  49. 49. MOAR DETAILS Click on the Clock with a green Play button.
  50. 50. LONG PAUSE There are ways to avoid this, just deal with it today.
  51. 51. ERROR If you get this, don’t worry, just keep trying.
  52. 52. RECONNECT You may have to reconnect a couple of times.
  53. 53. RED LINE Once you see the red line it means you are good to go.
  54. 54. GATLING AGAIN Run the Engine on the PerformanceTest project again.
  55. 55. LINE NUMBERS Now you know what it is doing.
  56. 56. GATLING AGAIN Run the Engine on the PerformanceTest project again. Choose NetworkCount5.
  57. 57. SAMPLING Doesn’t always tell the whole story.
  58. 58. TRYTHE OTHER ONE Stop the profiling, switch to “Tracing”, restart it.
  59. 59. GATLING AGAIN Run the Engine on the PerformanceTest project again. Choose NetworkCount5.
  60. 60. TRACING Better at telling the whole story?
  61. 61. MEMORY Check your Object Allocations. 500k relationships, 100M relationship objects.
  62. 62. READTHE DOCS https://www.yourkit.com/docs/java/help/cpu_intro.jsp
  63. 63. REFERENCE ONLY Don’t do this now.
  64. 64. GETTHE ZIP IFYOU DIDN’T EARLIER From the Linux option. https://www.yourkit.com/java/profiler/download/
  65. 65. ENABLE IT Use the line for your particular Operating System. https://www.yourkit.com/docs/java/help/agent.jsp
  66. 66. dbms.jvm.additional=-agentpath:/Users/ maxdemarzi/Downloads/YourKit- JavaProfiler-2018.04/bin/mac/libyjpagent.jnilib Edit your neo4j.conf file, and restart it.
  67. 67. GREEN No more agent connection troubles.
  68. 68. TEALTO PINK How do we reach the pink node from the teal node?
  69. 69. MATCH p=(u:User)-[*1..2]-(u2:User) WHERE u.username = 'Khloe17' AND u2.username = 'Michelle21' RETURN p, REDUCE(weight = 0.0, r in relationships(p) | weight + r.weight) / length(p) AS weight ORDER BY weight DESC LIMIT 100
  70. 70. PROFILE IT VarLengthExpand… hum.
  71. 71. 3Let’s go up a notch.
  72. 72. MATCH p=(u:User)-[*1..3]-(u2:User) WHERE u.username = 'Khloe17' AND u2.username = 'Michelle21' RETURN p, REDUCE(weight = 0.0, r in relationships(p) | weight + r.weight) / length(p) AS weight ORDER BY weight DESC LIMIT 100
  73. 73. 492,448,313 DB HITS OMGWTFBBQ
  74. 74. 4Let’s go up another notch.
  75. 75. MATCH p=(u:User)-[*1..4]-(u2:User) WHERE u.username = 'Khloe17' AND u2.username = 'Michelle21' RETURN p, REDUCE(weight = 0.0, r in relationships(p) | weight + r.weight) / length(p) AS weight ORDER BY weight DESC LIMIT 100
  76. 76. DON’T RUN IT If you jumped the gun and did, restart neo4j.
  77. 77. GOOD FRIENDS [:FRIENDS {weight:0.80+}] and sort by length
  78. 78. MATCH p=(u:User)-[*1..4]-(u2:User) WHERE u.username = 'Khloe17' AND u2.username = 'Michelle21' AND ALL (r IN relationships(p) WHERE r.weight >= 0.80) RETURN p, REDUCE(weight = 0.0, r in relationships(p) | weight + r.weight) / length(p) AS weight ORDER BY length(p), weight DESC LIMIT 100
  79. 79. 80 SECONDS Much better.Why?
  80. 80. FILTER IN EXPAND Checks the weight property as it goes.
  81. 81. TRAVERSAL API - PART 2 Evaluators and Expanders.
  82. 82. ADD A NEW CLASS Right Click on “results” and add it.
  83. 83. CALL IT WEIGHTEDPATHRESULT It will return a Path and a weight.
  84. 84. package com.maxdemarzi.results; import org.neo4j.graphdb.Path; public class WeightedPathResult { public Path path; public double weight; public WeightedPathResult(Path path, double weight) { this.path = path; this.weight = weight; } public int getLength() { return path.length(); } public double getWeight() {
  85. 85. COMPARATOR Which paths are better?
  86. 86. private static final Comparator<WeightedPathResult> comparator = Comparator.comparingInt(WeightedPathResult::getLength) .thenComparing(Comparator.comparingDouble(WeightedPath Result::getWeight) .reversed()); This goes in Procedures.java inside the class, outside any methods.
  87. 87. @Procedure(name="com.maxdemarzi.reach.after", mode = Mode.READ) @Description("com.maxdemarzi.reach.after(String username1, String username2)") public Stream<WeightedPathResult> reachAfter(@Name("username1") String username1, @Name("username2") String username2) { Node user1 = db.findNode(Label.label("User"), "username", username1); Node user2 = db.findNode(Label.label("User"), "username", username2); if (user1 == null || user2 == null) { return Stream.empty(); }
  88. 88. TraversalDescription leftSide = db.traversalDescription() .breadthFirst() .expand(PathExpanders.allTypesAndDirection s()) .evaluator(Evaluators.toDepth(2)) .uniqueness(Uniqueness.RELATIONSHIP_PATH); TraversalDescription rightSide = db.traversalDescription() .breadthFirst() .expand(PathExpanders.allTypesAndDirection s()) .evaluator(Evaluators.toDepth(2)) .uniqueness(Uniqueness.RELATIONSHIP_PATH); BidirectionalTraversalDescription bidirtd =
  89. 89. outerloop: for (Path path : bidirtd.traverse(user1, user2)) { double weight = 0.0; for (Relationship r : path.relationships()) { double each = (double) r.getProperty("weight"); if (each >= 0.80) { weight += each; } else { continue outerloop; } } list.add(new WeightedPathResult(path, weight / path.length())); } list.sort(comparator);
  90. 90. CALL com.maxdemarzi.reach.after('Khloe17', 'Michelle21')
  91. 91. DON’T RUN IT If you jumped the gun and did, restart neo4j.
  92. 92. ADD A NEW JAVA CLASS Make sure you are on your Package folder and Right-Click.
  93. 93. CALL IT GOODFRIENDEVALUATOR It will evaluate Paths.
  94. 94. public class GoodFriendEvaluator implements Evaluator { @Override public Evaluation evaluate(Path path) { for (Relationship r : path.relationships()) { if ((double)r.getProperty("weight") < 0.80) { return Evaluation.EXCLUDE_AND_PRUNE; } } return Evaluation.INCLUDE_AND_CONTINUE; } }
  95. 95. private static final GoodFriendEvaluator evaluator = new GoodFriendEvaluator(); @Procedure(name="com.maxdemarzi.reach.evaluator", mode = Mode.READ) @Description("com.maxdemarzi.reach.evaluator(String username1, String username2)") public Stream<WeightedPathResult> reachEvaluator(@Name("username1") String username1, @Name("username2") String username2) { Node user1 = db.findNode(Label.label("User"), "username", username1); Node user2 = db.findNode(Label.label("User"), "username", username2); if (user1 == null || user2 == null) { return Stream.empty(); }
  96. 96. TraversalDescription eachSide = db.traversalDescription() .breadthFirst() .expand(PathExpanders.allTypesAndDire ctions()) .evaluator(Evaluators.toDepth(2)) .evaluator(evaluator) .uniqueness(Uniqueness.RELATIONSHIP_P ATH);
  97. 97. EVALUATETHE COLLISION What do you do when two traversals collide?
  98. 98. BidirectionalTraversalDescription bidirtd = db.bidirectionalTraversalDescript ion() .mirroredSides(eachSide) .collisionEvaluator(evalu ator);
  99. 99. ArrayList<WeightedPathResult> list = new ArrayList<>(); for (Path path : bidirtd.traverse(user1, user2)) { double weight = 0.0; for (Relationship r : path.relationships()) { weight += (double) r.getProperty("weight"); } list.add(new WeightedPathResult(path, weight / path.length())); } list.sort(comparator); return list.stream().limit(100);
  100. 100. CALL com.maxdemarzi.reach.evaluator('Khloe17', 'Michelle21')
  101. 101. 7 SECONDS Not bad. Better than the 80 seconds we started with.
  102. 102. ADD A NEW JAVA CLASS Make sure you are on your Package folder and Right-Click.
  103. 103. CALL IT GOODFRIENDEXPANDER It will expand Paths.
  104. 104. public class GoodFriendExpander implements PathExpander { @Override public Iterable<Relationship> expand(Path path, BranchState branchState) { List<Relationship> rels = new ArrayList<>(); for (Relationship r : path.endNode() .getRelationships(RelationshipType.wit hName("FRIENDS"))) { if ((double)r.getProperty("weight") >= 0.80) { rels.add(r); } } return rels; } @Override public PathExpander reverse() { // Doesn't matter, do the same thing. return this; } }
  105. 105. private static final GoodFriendExpander expander = new GoodFriendExpander(); @Procedure(name="com.maxdemarzi.reach.expander", mode = Mode.READ) @Description("com.maxdemarzi.reach.expander(String username1, String username2)") public Stream<WeightedPathResult> reachExpander(@Name("username1") String username1, @Name("username2") String username2) { Node user1 = db.findNode(Label.label("User"), "username", username1); Node user2 = db.findNode(Label.label("User"), "username", username2); if (user1 == null || user2 == null) { return Stream.empty(); }
  106. 106. TraversalDescription eachSide = db.traversalDescription() .breadthFirst() .expand(expander) .evaluator(Evaluators.toDepth(2)) .uniqueness(Uniqueness.RELATIONSHIP_P ATH); BidirectionalTraversalDescription bidirtd = db.bidirectionalTraversalDescription() .mirroredSides(eachSide);
  107. 107. ArrayList<WeightedPathResult> list = new ArrayList<>(); for (Path path : bidirtd.traverse(user1, user2)) { double weight = 0.0; for (Relationship r : path.relationships()) { weight += (double) r.getProperty("weight"); } list.add(new WeightedPathResult(path, weight / path.length())); } list.sort(comparator); return list.stream().limit(100);
  108. 108. CALL com.maxdemarzi.reach.expander('Khloe17', 'Michelle21')
  109. 109. 211 MILLISECONDS Better than 7 seconds and way better than 80 seconds.
  110. 110. EXPANDER > EVALUATOR Check before traverse is better than check after traverse.
  111. 111. BOTH? Sure, why not.
  112. 112. ADD A NEW JAVA CLASS Make sure you are on your Package folder and Right-Click.
  113. 113. TOO MANY FRIENDS Another Evaluator.
  114. 114. public class TooManyFriendsEvaluator implements Evaluator { private PriorityQueue<WeightedPathResult> paths; TooManyFriendsEvaluator(PriorityQueue<WeightedPathResult> paths) { this.paths = paths; } @Override public Evaluation evaluate(Path path) { double weight = 0.0; for (Relationship r : path.relationships()) { weight += (double) r.getProperty("weight"); } if (paths.size() < 100) { paths.add(new WeightedPathResult(path, weight / path.length())); } else { WeightedPathResult last = paths.peek(); if(last.getLength() >= path.length() && last.weight < weight/ path.length()) { paths.poll(); paths.add(new WeightedPathResult(path, weight / path.length())); } }
  115. 115. @Procedure(name =“com.maxdemarzi.reach.both", mode = Mode.READ) @Description("com.maxdemarzi.reach.both(String username1, String username2)") public Stream<WeightedPathResult> reachBoth(@Name("username1") String username1, @Name("username2") String username2) { Node user1 = db.findNode(Label.label("User"), "username", username1); Node user2 = db.findNode(Label.label("User"), "username", username2); if (user1 == null || user2 == null) { return Stream.empty(); } PriorityQueue<WeightedPathResult> paths = new PriorityQueue<>(100, comparator);
  116. 116. TraversalDescription eachSide = db.traversalDescription() .breadthFirst() .expand(expander) .evaluator(Evaluators.toDepth(2)) .uniqueness(Uniqueness.NODE_PATH); BidirectionalTraversalDescription bidirtd = db.bidirectionalTraversalDescription() .mirroredSides(eachSide) .collisionEvaluator(new TooManyFriendsEvaluator(paths));
  117. 117. int count = 0; for (Path ignore : bidirtd.traverse(user1, user2)) { count++; } return paths.stream();
  118. 118. 168 MILLISECONDS Nice…
  119. 119. CA$HING Great for immutable properties and slow changing traversals.
  120. 120. LOOK AT POM.XML Look at it.
  121. 121. JUST KIDDING Caffeine is already included by Neo4j, so we don’t need to add it.
  122. 122. CLOSE POM.XML Close it.
  123. 123. private static GraphDatabaseService graph; private static final LoadingCache<Integer, String> usernames = Caffeine.newBuilder() .maximumSize(100001L) .build(Procedures::getNodeUsername); private static String getNodeUsername(Integer key) { return (String)graph.getNodeById(key).getProperty("userna me"); }
  124. 124. private static final LoadingCache<Integer, RoaringBitmap> nodeFriends = Caffeine.newBuilder() .maximumSize(100001L) .expireAfterWrite(1L, TimeUnit.HOURS) .build(Procedures::getNodeFriends); private static RoaringBitmap getNodeFriends(Integer key) { Node user = graph.getNodeById(key); RoaringBitmap friends = new RoaringBitmap(); for (Relationship r : user.getRelationships(RelationshipType.withName("FRIENDS"))) { friends.add((int)r.getOtherNodeId(key)); } return friends; }
  125. 125. ANALYTICS Writing to Disk, Logging progress, more…
  126. 126. HATEDTHAT SHOW Seriously.
  127. 127. MATCH (u:User)-[:FRIENDS]-(u2:User)- [:FRIENDS]-(u3:User) RETURN u.username, u2.username, u3.username LIMIT 100
  128. 128. 7 MS That’s fast.
  129. 129. SMART Find u2, expand.
  130. 130. DUMB Why you expand again?
  131. 131. IN ORDER Don’t run this next query.
  132. 132. MATCH (u:User)-[:FRIENDS]-(u2:User)- [:FRIENDS]-(u3:User) RETURN u.username, u2.username, u3.username ORDER BY u.username, u2.username, u3.username LIMIT 100
  133. 133. EXPLAIN MATCH (u:User)-[:FRIENDS]-(u2:User)- [:FRIENDS]-(u3:User) RETURN u.username, u2.username, u3.username ORDER BY u.username, u2.username, u3.username LIMIT 100
  134. 134. DUMB Top is at the bottom.
  135. 135. REPORT I want to know for every friend of friends, how many friends they have in common….Oh and I want it in order by username alphabetically and then the count in descending order.
  136. 136. EXPLAIN MATCH (u:User)-[:FRIENDS]-(u2:User)- [:FRIENDS]-(u3:User) RETURN u.username, u3.username, COUNT(u2) ORDER BY u.username, COUNT(u2) DESC
  137. 137. THIS IS GOINGTO SUCK Let’s go.
  138. 138. LOOK AT POM.XML Look at it.
  139. 139. <properties> <neo4j.version>3.5.1</neo4j.version> <neo4j.driver.version>1.7.2</neo4j.driver.version> <roaring.version>0.7.30</roaring.version> <fastcsv.version>1.0.3</fastcsv.version> <project.build.sourceEncoding>UTF-8</ project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>de.siegmar</groupId> <artifactId>fastcsv</artifactId> <version>${fastcsv.version}</version> </dependency>
  140. 140. CLOSE POM.XML Close it.
  141. 141. @Procedure(name="com.maxdemarzi.fic", mode = Mode.READ) @Description("com.maxdemarzi.fic(String path)") public Stream<StringResult> friendsInCommon(@Name(value="path") String path) throws IOException { if (graph == null) { graph = this.db; } ResourceIterator userIterator = db.findNodes(Label.label("User")); ArrayList<Pair<String, Node>> users = new ArrayList<>(); while (userIterator.hasNext()) { Node user = (Node)userIterator.next(); String username = usernames.get((int)user.getId()); users.add(Pair.of(username, user)); } users.sort(Comparator.comparing(Pair<String, Node>::first)); RoaringBitmap fofs; RoaringBitmap friends; IntIterator intIterator;
  142. 142. File file = new File(path); CsvWriter csvWriter = new CsvWriter(); try (CsvAppender csvAppender = csvWriter.append(file, StandardCharsets.UTF_8)){ csvAppender.appendLine("user", "fof", "fic_count"); int userCount = 0; for (Pair<String, Node> pair : users) { int fof; if (userCount++ > 1000) break; // short circuit for class fofs = new RoaringBitmap(); friends = nodeFriends.get((int)pair.other().getId()); intIterator = friends.getIntIterator(); while (intIterator.hasNext()) { fofs.or(nodeFriends.get(intIterator.next())); } fofs.remove((int) pair.other().getId());
  143. 143. ArrayList<Pair<String, Integer>> counts = new ArrayList<>(); intIterator = fofs.getIntIterator(); while (intIterator.hasNext()) { fof = intIterator.next(); int cardinality = RoaringBitmap.and(friends, nodeFriends.get(fof)).getCardinality(); if (cardinality <= 5) continue; // short circuit for class counts.add(Pair.of(usernames.get(fof), cardinality)); } counts.sort(Comparator.comparingInt(Pair<String, Integer>::other).reversed()); for (Pair<String, Integer> count : counts) { csvAppender.appendLine(pair.first(), count.first(), String.valueOf(count.other())); } } } return Stream.of(new StringResult("Report written to " + path));
  144. 144. CALL IT AND WAIT CALL com.maxdemarzi.fic('/Users/maxdemarzi/Documents/Projects/ procedures/fic.csv')
  145. 145. OPEN IT If you remove the short circuits, you will get gigs of data.
  146. 146. YOU CAN’T DOTHIS IN NEPTUNE Or in CosmosDB for that matter.
  147. 147. ANALYTICS What is the distribution of mutual friends?
  148. 148. EXPLAIN MATCH (u:User)-[:FRIENDS]-(u2:User)- [:FRIENDS]-(u3:User) WITH u, u3, COUNT(u2) AS counts RETURN counts, COUNT(*) ORDER BY counts
  149. 149. ADD A NEW CLASS Right Click on “results” and add it.
  150. 150. CALL IT COUNTVALUERESULT It will return a Count and aValue.
  151. 151. package com.maxdemarzi.results; public class CountValueResult { public final Long count; public final Long value; public CountValueResult(Long count, Long value) { this.count = count; this.value = value; } }
  152. 152. @Procedure(name = "com.maxdemarzi.fic.distribution", mode = Mode.READ) @Description("com.maxdemarzi.fic.distribution") public Stream<CountValueResult> friendsInCommonDistribution() { long start = System.nanoTime(); if (graph == null) { graph = this.db; } ResourceIterator userIterator = db.findNodes(Label.label("User")); Map<Long, long[]> instances = new HashMap<>(); Roaring64NavigableMap seen = new Roaring64NavigableMap();
  153. 153. int count = 0; while (userIterator.hasNext()) { Node user = (Node) userIterator.next(); RoaringBitmap friendsMap = nodeFriends.get((int) user.getId()); int[] friends = friendsMap.toArray(); for (int i = 0; i < friends.length; i++) { for (int j = i + 1; j < friends.length; j++) { long key = (((long)friends[i]) << 32) | (friends[j] & 0xffffffffL); seen.add(key); } } if(count++ % 1000 == 0) { log.warn("On User # " + count + " at " + TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start)); } }
  154. 154. count = 0; Iterator<Long> iterator = seen.iterator(); long size = seen.getLongCardinality(); while(iterator.hasNext()) { long l = iterator.next(); int firstNodeId = (int)(l >> 32); int secondNodeId = (int)l; long common = RoaringBitmap .and(nodeFriends.get(firstNodeId), nodeFriends.get(secondNodeId)) .getCardinality(); instances.putIfAbsent(common, new long[]{0L}); instances.get(common)[0]++; if(count++ % 50_000_000 == 0) { log.warn("On Combination # " + count + " of " + size + " at " + TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start));
  155. 155. return instances.entrySet() .stream() .sorted(Comparator.comparingLong(Map.Entry<Long, long[]>::getKey)) .map(entry -> new CountValueResult(entry.getKey(), entry.getValue()[0]));
  156. 156. CALL IT CALL com.maxdemarzi.fic.distribution
  157. 157. 15 MINUTES tail -f ./neo4j-enterprise-3.5.1/logs/neo4j.log
  158. 158. Q & A What is on your mind?

×