{"id":13301,"date":"2017-08-02T15:28:33","date_gmt":"2017-08-02T15:28:33","guid":{"rendered":"http:\/\/www.doanduyhai.com\/blog\/?p=13301"},"modified":"2017-08-08T21:01:15","modified_gmt":"2017-08-08T21:01:15","slug":"gremlin-recipes-4-recursive-traversals","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=13301","title":{"rendered":"Gremlin recipes: 4 &#8211; Recursive traversals"},"content":{"rendered":"<p>This blog post is the 4<sup>th<\/sup> from the series <strong>Gremlin Recipes<\/strong>. It is recommended to read the previous blog posts first: <\/p>\n<ol>\n<li><a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13224\" target=\"_blank\"><strong>Gremlin as a Stream<\/strong><\/a><\/li>\n<li><a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13260\" target=\"_blank\"><strong>SQL to Gremlin<\/strong><\/a><\/li>\n<li><a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13285\" target=\"_blank\"><strong>Recommendation Engine traversal<\/strong><\/a><\/li>\n<\/ol>\n<p><!--more--><\/p>\n<h1>I KillrVideo dataset<\/h1>\n<p>To illustrate this series of recipes, you need first to create the schema for <strong>KillrVideo<\/strong> and import the data. See <a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13224#killrvideo_dataset\" target=\"_blank\"><strong>here<\/strong><\/a> for more details.<\/p>\n<p>The graph schema of this dataset is :<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/s3.amazonaws.com\/datastax-graph-schema-viewer\/index.html#\/?schema=killr_video_small.json\" height=\"600px\" width=\"100%\"><\/iframe><\/p>\n<h1>II Graph expansion<\/h1>\n<p>In this post we will explore all the techniques for recursive traversal with <strong>Gremlin<\/strong>. We will focus on mainly on this part of the schema:<\/p>\n<div id=\"attachment_13304\" style=\"width: 850px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-13304\" loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows-1024x378.png\" alt=\"Users-Knows\" width=\"840\" height=\"310\" class=\"size-large wp-image-13304\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows-1024x378.png 1024w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows-300x111.png 300w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows-768x283.png 768w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows-1170x432.png 1170w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Users-Knows.png 1350w\" sizes=\"(max-width: 840px) 100vw, 840px\" \/><p id=\"caption-attachment-13304\" class=\"wp-caption-text\">Users-Knows<\/p><\/div>\n<p>An user knows other users who knows other users etc. This is typical of a recursive traversal. First we want to know which user in our dataset knows the most other users:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                       \/\/ Iterator&lt;Vertex&gt;\r\n  hasLabel(&quot;user&quot;).                  \/\/ Iterator&lt;User&gt;\r\n  order().                      \r\n    by(outE(&quot;knows&quot;).count(), decr). \/\/ Order the users by their outgoing\/incoming edge &quot;knows&quot;, DESCENDING\r\n  values(&quot;userId&quot;).                  \/\/ Only display userId \r\n  limit(1)                           \/\/ Take the 1st matching user\r\n==&gt;u861\r\n\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  outE(&quot;knows&quot;).                 \/\/ Iterator&lt;Knows_Edge&gt;\r\n  count()\r\n==&gt;13  \r\n<\/pre>\n<p>So user <strong>u861<\/strong> seems to knows 13 other users as his\/her 1<sup>st<\/sup> degree connection.<\/p>\n<p>Now let&#8217;s list all those 13 1<sup>st<\/sup> degree friends of user <strong>u861<\/strong>:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  out(&quot;knows&quot;).                  \/\/ Iterator&lt;User&gt;\r\n  values(&quot;userId&quot;).              \/\/ Iterator&lt;String&gt;\r\n  fold()                         \/\/ Iterator&lt;Collection&lt;String&gt;&gt;\r\n==&gt;[u751, u868, u778, u887, u713, u733, u752, u548, u892, u657, u841, u150, u154]\r\n<\/pre>\n<h1>III Repeating traversal<\/h1>\n<p>Now, let&#8217;s display all 2<sup>nd<\/sup> degree friends of user <strong>u861<\/strong>:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  as(&quot;u861&quot;).                    \/\/ Label u861    \r\n  out(&quot;knows&quot;).                  \/\/ 1st degree friends\r\n  out(&quot;knows&quot;).                  \/\/ 2nd degree friends\r\n  dedup().                       \/\/ Remove duplicates\r\n  where(neq(&quot;u861&quot;)).            \/\/ Exclude u861   \r\n  values(&quot;userId&quot;).              \/\/ Iterator&lt;String&gt;\r\n  fold()                         \/\/ Iterator&lt;Collection&lt;String&gt;&gt;\r\n==&gt;[u747, u719, u755, u756, u886, u880, u819, u831, u810, u832, u869, u718, u875, u830, u723, u727, u207, u744, u860, u704, u730, u829, u620, u882, u1035, u724, u800, u703, u705, u416, u717, u814, u767, u895, u804, u118, u263, u900, u887, u766, u851, u757, u606, u550, u155, u897, u750, u792, u797, u776, u702, u873, u855, u131, u196, u707, u103, u152]\r\n<\/pre>\n<p>As we can see, the combinatory explosion occurs right after the 2<sup>nd<\/sup> degree connection and will go on exponentially. We also notice the repetition of the step <code><strong>out(\"knows\")<\/strong><\/code>. If we were looking at N<sup>th<\/sup> degree connection, we would have to repeat the same step N times, which is quite verbose. To avoid this <strong>Gremlin<\/strong> exposes a convenient step: <code><strong>repeat(... innner traversal)<\/strong><\/code>. The above traversal can be rewritten as:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  as(&quot;u861&quot;).                    \/\/ Label u861    \r\n  repeat(out(&quot;knows&quot;)).times(2). \/\/ 2nd degree friends\r\n  dedup().                       \/\/ Remove duplicates\r\n  where(neq(&quot;u861&quot;)).            \/\/ Exclude u861   \r\n  values(&quot;userId&quot;).              \/\/ Iterator&lt;String&gt;\r\n  fold()                         \/\/ Iterator&lt;Collection&lt;String&gt;&gt;\r\n==&gt;[u747, u719, u755, u756, u886, u880, u819, u831, u810, u832, u869, u718, u875, u830, u723, u727, u207, u744, u860, u704, u730, u829, u620, u882, u1035, u724, u800, u703, u705, u416, u717, u814, u767, u895, u804, u118, u263, u900, u887, u766, u851, u757, u606, u550, u155, u897, u750, u792, u797, u776, u702, u873, u855, u131, u196, u707, u103, u152]\r\n<\/pre>\n<p>So far so good. There are <strong>58<\/strong> users connected to <strong>u861<\/strong> by 2<sup>nd<\/sup> degree connection.<\/p>\n<h1>IV Emitting traversed vertices<\/h1>\n<p>Now, what if we want to display 1<sup>st<\/sup> <strong>and<\/strong> 2<sup>nd<\/sup> degree connections together ? For this <strong>Gremlin<\/strong> provides the <code><strong>emit()<\/strong><\/code> modulator to &#8220;output&#8221; all the traversed vertices:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  as(&quot;u861&quot;).                    \/\/ Label u861    \r\n  repeat(out(&quot;knows&quot;)).times(2). \/\/ 2nd degree friends\r\n  emit().                        \/\/ Output the traversed User vertices   \r\n  dedup().                       \/\/ Remove duplicates\r\n  where(neq(&quot;u861&quot;))\r\n==&gt;...\r\n<\/pre>\n<p>Using <a href=\"https:\/\/www.datastax.com\/products\/datastax-studio-and-development-tools#DataStax-Studio\" target=\"_blank\"><strong>Datastax Studio<\/strong><\/a> for a better visualisation of the results, here is the web of all connections up to 2<sup>nd<\/sup> degree for user <strong>u861<\/strong><\/p>\n<div id=\"attachment_13308\" style=\"width: 894px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-13308\" loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/2nd_degree_connections.png\" alt=\"2nd_degree_connections\" width=\"884\" height=\"718\" class=\"size-full wp-image-13308\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/2nd_degree_connections.png 884w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/2nd_degree_connections-300x244.png 300w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/2nd_degree_connections-768x624.png 768w\" sizes=\"(max-width: 884px) 100vw, 884px\" \/><p id=\"caption-attachment-13308\" class=\"wp-caption-text\">2nd_degree_connections<\/p><\/div>\n<p>The visualisation reveals the very fast graph expansion when using recursive traversals. We can restrict ourselves to the 1<sup>st<\/sup> degree connections:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  repeat(out(&quot;knows&quot;)).times(1). \/\/ 1st degree friends\r\n  emit()\r\n==&gt;...\r\n<\/pre>\n<div id=\"attachment_13310\" style=\"width: 738px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-13310\" loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/1st_degree_connections.png\" alt=\"1st_degree_connections\" width=\"728\" height=\"698\" class=\"size-full wp-image-13310\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/1st_degree_connections.png 728w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/1st_degree_connections-300x288.png 300w\" sizes=\"(max-width: 728px) 100vw, 728px\" \/><p id=\"caption-attachment-13310\" class=\"wp-caption-text\">1st_degree_connections<\/p><\/div>\n<p>Not only the users are displayed but <a href=\"https:\/\/www.datastax.com\/products\/datastax-studio-and-development-tools#DataStax-Studio\" target=\"_blank\"><strong>Datastax Studio<\/strong><\/a> conveniently shows all the edges that exist between the users and we just realize that <strong>u892<\/strong> and <strong>u887<\/strong> know each other !<\/p>\n<p>But there is still a missing piece in our picture, where is <strong>u861<\/strong> ??? Indeed, there are 2 possible placements for the <code><strong>emit()<\/strong><\/code> modulator. If <code><strong>emit()<\/strong><\/code> is placed after <code><strong>repeat()<\/strong><\/code>, it will &#8220;output&#8221; all vertices leaving the repeat-traversal. If <code><strong>emit()<\/strong><\/code> is placed before <code><strong>repeat()<\/strong><\/code>, it will &#8220;output&#8221; the vertices prior to entering the repeat-traversal. <\/p>\n<p>In our example we just need to put <code><strong>emit()<\/strong><\/code> right before <code><strong>repeat(out(\"knows\")).times(1)<\/strong><\/code>:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().                   \/\/ Iterator&lt;Vertex&gt;\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;). \/\/ Iterator&lt;User&gt;\r\n  emit().                        \/\/ Output u861 and other users\r\n  repeat(out(&quot;knows&quot;)).times(1)  \/\/ 1st degree friends   \r\n==&gt;...\r\n<\/pre>\n<div id=\"attachment_13312\" style=\"width: 772px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-13312\" loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Web_of_connections.png\" alt=\"Web_of_connections\" width=\"762\" height=\"704\" class=\"size-full wp-image-13312\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Web_of_connections.png 762w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/07\/Web_of_connections-300x277.png 300w\" sizes=\"(max-width: 762px) 100vw, 762px\" \/><p id=\"caption-attachment-13312\" class=\"wp-caption-text\">Web_of_connections<\/p><\/div>\n<p>Now, we have a beautiful web of connections outgoing from <strong>u861<\/strong> <\/p>\n<h1>V Controlling the recursion<\/h1>\n<p>Until now, we were just using <code><strong>times(x)<\/strong><\/code> to control how far we will deep dive into recursion. What you should now is that <code><strong>times(x)<\/strong><\/code> is just a shorthand for a more generalized form of recursion control : <code><strong>until(loops().is(eq(1)))<\/strong><\/code>. <\/p>\n<p><code><strong>until()<\/strong><\/code> is controlling <strong>when to break out of the recursion<\/strong> based on a condition. As with <code><strong>emit()<\/strong><\/code> you can place <code><strong>until()<\/strong><\/code> before or after the <code><strong>repeat()<\/strong><\/code> step.<\/p>\n<p><code><strong>until(...).repeat(...)<\/strong><\/code> is equivalent to a <code><strong>while(...) do{  } <\/strong><\/code loop whereas <code><strong>repeat(&#8230;.).until(&#8230;)<\/strong><\/code> is equivalent to <code><strong>do{  } while(...)<\/strong><\/code><\/p>\n<p>Suppose we want to know all friends of <strong>u861<\/strong> having age 32:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt; :remote config timeout 10000\r\n==&gt;Set remote timeout to 10000ms\r\ngremlin&gt; g.V().\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;).\r\n  until(has(&quot;age&quot;, 32)).\r\n  repeat(out(&quot;knows&quot;))\r\nRequest timed out while processing - increase the timeout with the :remote command\r\nType ':help' or ':h' for help.\r\nDisplay stack trace? [yN]\r\n<\/pre>\n<p>We just ran into a timeout. Why so ? Doesn&#8217;t use <strong>u861<\/strong> have any connected friend with 32 years old ? We can run a quick check:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt; g.V().\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;).\r\n  out(&quot;knows&quot;).\r\n  has(&quot;age&quot;, 32).\r\n  valueMap(&quot;userId&quot;, &quot;age&quot;)\r\n==&gt;{userId=[u887], age=[32]}\r\n<\/pre>\n<p>User <strong>u887<\/strong> is indeed a direct connection of <strong>u861<\/strong> having the matching age. So why did our traversal timeout ???<\/p>\n<p>One problem with our traversal is that we may run into <strong>cyclic sub-graphs<\/strong> e.g <em>user xxx which knows user yyy which knows in return user xxx<\/em>. To shield ourselves from cyclic graph we can use <code><strong>repeat(out(\"knows\").simplePath()))<\/strong><\/code> to consider only acyclic paths.<\/p>\n<p>But the fundamental reason of the timeout is because <strong>Gremlin<\/strong> OLTP query engine is optimized for depth-first search. We will explain this concept is another post but long story shot, in the traversal <code><strong>until(has(\"age\", 32)).repeat(out(\"knows\"))<\/strong><\/code>,  <strong>Gremlin<\/strong> will select one vertex and expand on the <em>knows<\/em> edge in multiple direction (graph explosion). Some branches may be stopped because the traverser encounters an user with age==32 but other branches may continue further to N hops.  Using <strong>repeat(out(&#8220;knows&#8221;).simplePath()))<\/strong><\/code> helps reducing the combinatory explosion due to cyclic loops but we still have a rapid graph expansion.<\/p>\n<div id=\"attachment_13332\" style=\"width: 850px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-13332\" loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion-1024x650.png\" alt=\"Graph_Expansion\" width=\"840\" height=\"533\" class=\"size-large wp-image-13332\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion-1024x650.png 1024w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion-300x191.png 300w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion-768x488.png 768w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion-1170x743.png 1170w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2017\/08\/Graph_Expansion.png 1277w\" sizes=\"(max-width: 840px) 100vw, 840px\" \/><p id=\"caption-attachment-13332\" class=\"wp-caption-text\">Graph_Expansion<\/p><\/div>\n<p>The only way to limit depth-first expansion is to control the depth of each traversed branch using the <code><strong>loops()<\/strong><\/code> step: <code><strong>until(has(\"age\", 32).or().loops().is(eq(3)))<\/strong><\/code><\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;).\r\n  until(has(&quot;age&quot;, 32).or().loops().is(eq(3))).\r\n  repeat(out(&quot;knows&quot;).simplePath()).\r\n  valueMap(&quot;userId&quot;, &quot;age&quot;)\r\n==&gt;{userId=[u887], age=[32]}\r\n==&gt;{userId=[u719], age=[32]}\r\n==&gt;{userId=[u755], age=[32]}\r\n==&gt;{userId=[u800], age=[32]}\r\n==&gt;{userId=[u887], age=[32]}\r\n==&gt;{userId=[u361], age=[23]}\r\n==&gt;{userId=[u740], age=[21]}\r\n...\r\n<\/pre>\n<p>Here we are ! The condition <code><strong>until(has(\"age\", 32).or().loops().is(eq(3)))<\/strong><\/code> means that we will stop the loop whenever one of those conditions are met<\/p>\n<ul>\n<li>either an user with age == 32<\/li>\n<li>or the depth of the branch (materialized by <code><strong>loops()<\/strong><\/code>) has reached 3<\/li>\n<\/ul>\n<p>That explains why in the results we can see some users with age == 32 (<strong>u887<\/strong>, <strong>u719<\/strong> &#8230;) but other having age != 32 (<strong>u361<\/strong>, <strong>u740<\/strong>, &#8230;). They are still returned as a result because they match the 2<sup>nd<\/sup> condition of our <code><strong>until()<\/strong><\/code><\/p>\n<h1>VI Time-limiting the graph exploration<\/h1>\n<p>We have just seen that one technique to control the recursive combinatory explosion is to put a limit on the depth of each explored branch. Another powerful technique is to simply cap the computation time of graph expansion. For this we can use <code><strong>timeLimit(time_in_millisecs)<\/strong><\/code>. All the computation time, including the time taken by the loops and recursive traversal.<\/p>\n<p>The previous traversal can be rewritten as:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;).\r\n  until(has(&quot;age&quot;, 32)).\r\n  repeat(out(&quot;knows&quot;).simplePath()).\r\n  timeLimit(100).\r\n  valueMap(&quot;userId&quot;, &quot;age&quot;)\r\n==&gt;{userId=[u887], age=[32]}\r\n==&gt;{userId=[u719], age=[32]}\r\n==&gt;{userId=[u755], age=[32]}\r\n==&gt;{userId=[u800], age=[32]}\r\n==&gt;{userId=[u887], age=[32]}\r\n<\/pre>\n<p>In the above traversal, we want the computation of all the block <code><strong>until(has(\"age\", 32)).repeat(out(\"knows\").simplePath())<\/strong><\/code> will take at most 100ms. If we move the <code><strong>timeLimit(100)<\/strong><\/code> inside the <code><strong>repeat(...)<\/strong><\/code> step:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;user&quot;, &quot;userId&quot;, &quot;u861&quot;).\r\n  until(has(&quot;age&quot;, 32)).\r\n  repeat(out(&quot;knows&quot;).simplePath().timeLimit(100)).\r\n  valueMap(&quot;userId&quot;, &quot;age&quot;).\r\n  dedup()\r\n==&gt;{userId=[u887], age=[32]}\r\n==&gt;{userId=[u719], age=[32]}\r\n==&gt;{userId=[u755], age=[32]}\r\n==&gt;{userId=[u800], age=[32]}\r\n==&gt;{userId=[u885], age=[32]}\r\n==&gt;{userId=[u877], age=[32]}\r\n==&gt;{userId=[u807], age=[32]}\r\n==&gt;{userId=[u771], age=[32]}\r\n==&gt;{userId=[u783], age=[32]}\r\n==&gt;{userId=[u813], age=[32]}\r\n==&gt;{userId=[u431], age=[32]}\r\n==&gt;{userId=[u775], age=[32]}\r\n==&gt;{userId=[u839], age=[32]}\r\n==&gt;{userId=[u988], age=[32]}\r\n==&gt;{userId=[u936], age=[32]}\r\n==&gt;{userId=[u970], age=[32]}\r\n==&gt;{userId=[u1088], age=[32]}\r\n==&gt;{userId=[u1089], age=[32]}\r\n<\/pre>\n<p>Now we have more result, in fact <code><strong>repeat(out(\"knows\").simplePath().timeLimit(100))<\/strong><\/code> means that each of the repetition (loop) should take maximum 100ms<\/p>\n<p>This time limiting step is quite powerful because when you don&#8217;t know how fast your graph will expand because each vertex may have a very different adjacency degree, using <code><strong>timeLimit(...)<\/strong><\/code> will keep your computation resources under control.<\/p>\n<p>And that&#8217;s all folks! <strong>Do not miss the other Gremlin recipes in this series<\/strong>.<\/p>\n<p>If you have any question about <strong>Gremlin<\/strong>, find me on the <strong><a href=\"http:\/\/datastaxacademy.slack.com\" target=\"_blank\">datastaxacademy.slack.com<\/a><\/strong>, channel <strong>dse-graph<\/strong>. My id is <em>@doanduyhai<\/em>   <\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post is the 4th from the series Gremlin Recipes. It is recommended to read the previous blog posts first: Gremlin as a Stream SQL to Gremlin Recommendation Engine traversal<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[58,10],"tags":[],"_links":{"self":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/13301"}],"collection":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=13301"}],"version-history":[{"count":18,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/13301\/revisions"}],"predecessor-version":[{"id":13351,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/13301\/revisions\/13351"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=13301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=13301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}