{"id":13352,"date":"2017-08-04T12:31:55","date_gmt":"2017-08-04T12:31:55","guid":{"rendered":"http:\/\/www.doanduyhai.com\/blog\/?p=13352"},"modified":"2017-08-08T21:01:25","modified_gmt":"2017-08-08T21:01:25","slug":"gremlin-recipes-6-projection-and-selection","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=13352","title":{"rendered":"Gremlin recipes: 6 &#8211; Projection and selection"},"content":{"rendered":"<p>This blog post is the 6<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<li><a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13301\" target=\"_blank\"><strong>Recursive traversals<\/strong><\/a><\/li>\n<li><a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13320\" target=\"_blank\"><strong>Path object<\/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 Projection<\/h1>\n<p>By projection we mean <strong>SQL projection<\/strong> e.g. which properties we want to pick for display. For this <strong>Gremlin<\/strong> exposes several operators:<\/p>\n<ul>\n<li><code><strong>values(properties...)<\/strong><\/code><\/li>\n<li><code><strong>valuesMap(properties...)<\/strong><\/code><\/li>\n<li><code><strong>project(labels...).by(...)<\/strong><\/code><\/li>\n<li><code><strong>select(aliases...).by(...)<\/strong><\/code><\/li>\n<li><code><strong>path().by(...)<\/strong><\/code><\/li>\n<li><code><strong>group(...).by(...).by(...)<\/strong><\/code><\/li>\n<li><code><strong>where(...).by(...).by(...)<\/strong><\/code><\/li>\n<li><code><strong>dedup(...).by(...)<\/strong><\/code><\/li>\n<\/ul>\n<h3>A. values(properties&#8230;)<\/h3>\n<p><code><strong>values(properties...)<\/strong><\/code> will extract the properties of the vertices\/edges for display:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;). \/\/ Iterator&lt;Movie&gt; with single element\r\n  values(&quot;title&quot;, &quot;country&quot;, &quot;year&quot;)     \/\/ Iterator&lt;Object&gt; all requested properties\r\n==&gt;Blade Runner\r\n==&gt;1982\r\n==&gt;United States\r\n<\/pre>\n<p>As you can see, each property is put in the stream and output to the console sequentially. If we had 2 movies in our iterator, we would see:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).                  \/\/ Iterator&lt;Movie&gt;\r\n  limit(2).                           \/\/ Iterator&lt;Movie&gt; with 2 movies\r\n  values(&quot;title&quot;, &quot;country&quot;, &quot;year&quot;)  \/\/ Iterator&lt;Object&gt; all requested properties\r\n==&gt;The Italian Job\r\n==&gt;2003\r\n==&gt;United States\r\n==&gt;Great Expectations\r\n==&gt;1998\r\n==&gt;United States\r\n<\/pre>\n<p>All the properties of both movies are mixed together. If you want to separate them clearly, use <code><strong>valueMap(properties ...)<\/strong><\/code><\/p>\n<h3>B. valueMap(properties&#8230;)<\/h3>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).                     \/\/ Iterator&lt;Movie&gt; \r\n  limit(2).                              \/\/ Iterator&lt;Movie&gt; with 2 elements\r\n  valueMap(&quot;title&quot;, &quot;country&quot;, &quot;year&quot;)   \/\/ Iterator&lt;Map&lt;String,Object&gt;&gt;\r\n==&gt;{country=[United States], year=[2003], title=[The Italian Job]}\r\n==&gt;{country=[United States], year=[1998], title=[Great Expectations]}\r\n<\/pre>\n<p>Now the properties for each movie are nicely isolated with their label. The only limitation of <code><strong>values(properties...)<\/strong><\/code> and <code><strong>valueMap(properties...)<\/strong><\/code> is that you can only project on the intrinsic properties of the vertices\/edges, no inner traversal is allowed.<\/p>\n<h3>C. project(labels&#8230;).by(&#8230;)<\/h3>\n<p>With <code><strong>project(labels...).by(...)<\/strong><\/code> the projection can be achieved on any axis. Let&#8217;s say we want to display a movie title, country, released year and genres. The first 3 properties are movie&#8217;s intrinsic properties, the last one requires a traversal:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n   has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).         \/\/ Iterator&lt;Movie&gt; with a single element\r\n   project(&quot;title&quot;, &quot;country&quot;, &quot;year&quot;, &quot;genres&quot;). \/\/ Projection\r\n     by(&quot;title&quot;).                                 \/\/   on &quot;title&quot; property\r\n     by(&quot;country&quot;).                               \/\/   on &quot;country&quot; property\r\n     by(&quot;year&quot;).                                  \/\/   on &quot;year&quot; property\r\n     by(out(&quot;belongsTo&quot;).values(&quot;name&quot;).fold())   \/\/   on concatenation of movie &quot;genres&quot; \r\n==&gt;{title=Blade Runner, country=United States, year=1982, genres=[Sci-Fi, Action]}\r\n<\/pre>\n<p>The <code><strong>project(labels...)<\/strong><\/code> step accepts column labels as input. There you can set the labels for your projection columns. It works similar to SQL aliases (<code><strong>SELECT xxx AS alias<\/strong><\/code>).<\/p>\n<p>The <code><strong>by(...)<\/strong><\/code> modulator will allow you to project on any axis, including complex ones using inner traversal. <code><strong>by(property_name)<\/strong><\/code> is just a shorthand for the more general form <code><strong>by(values(property_name))<\/strong><\/code>.<\/p>\n<p>There is an implicit rule when using <code><strong>project()<\/strong><\/code>: the number of <code><strong>by()<\/strong> <\/code> modulators should correspond to the number of labels provided as argument of the preceding <code><strong>project()<\/strong><\/code>. <\/p>\n<h3>D. select(aliases&#8230;).by(&#8230;)<\/h3>\n<p>The <code><strong>select()<\/strong><\/code> step can achieved the same projection as <code><strong>project()<\/strong><\/code> but it only accepts previously labeled traversals as parameters.<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).     \/\/ Iterator&lt;Movie&gt;\r\n  as(&quot;blade_runner&quot;).                        \/\/ &quot;blade_runner&quot; label\r\n  out(&quot;belongsTo&quot;).                          \/\/ Iterator&lt;Genre&gt; \r\n  as(&quot;genres&quot;).                              \/\/ &quot;genres&quot; label\r\n  select(&quot;blade_runner&quot;, &quot;genres&quot;).          \/\/ Projection \r\n    by(valueMap(&quot;title&quot;, &quot;country&quot;,&quot;year&quot;)). \/\/  on &quot;title&quot;, &quot;country&quot; &amp; &quot;year&quot;   \r\n    by(values(&quot;name&quot;).fold())                \/\/  on concatenation of genres' name\r\n==&gt;{blade_runner={country=[United States], year=[1982], title=[Blade Runner]}, genres=[Sci-Fi]}\r\n==&gt;{blade_runner={country=[United States], year=[1982], title=[Blade Runner]}, genres=[Action]}\r\n<\/pre>\n<p>We have set <em>&#8220;blade_runner&#8221;<\/em> label on the movie and <em>&#8220;genres&#8221;<\/em> label on its genres then we applied <code><strong>select()<\/strong><\/code> on them to perform projection. Again the <code><strong>by()<\/strong><\/code> modulator define the axis for each projection.<\/p>\n<p>Surprisingly we have 2 results, the <em>&#8220;blade runner&#8221;<\/em> properties are repeated for each distinct genre&#8230; This is because the label <em>&#8220;blade runner&#8221;<\/em> has a single matching vertex but <em>&#8220;genres&#8221;<\/em> has 2 matching vertices. In this case <strong>Gremlin<\/strong> performs a cartesian product and thus we end up having 2 results.<\/p>\n<p>If we wanted only 1 result, we should transform our traversal to label as <em>&#8220;genres&#8221;<\/em> all the genres of <em>&#8220;blade runner&#8221;<\/em> as a collection and not as individual vertex.<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).      \/\/ Iterator&lt;Movie&gt;\r\n  as(&quot;blade_runner&quot;).                         \/\/ &quot;blade_runner&quot; label\r\n  out(&quot;belongsTo&quot;).values(&quot;name&quot;).fold().     \/\/ Iterator&lt;Collection&lt;String&gt;&gt; with a single collection\r\n  as(&quot;genres&quot;).                               \/\/ &quot;genres&quot; label\r\n  select(&quot;blade_runner&quot;, &quot;genres&quot;).           \/\/ Projection\r\n    by(valueMap(&quot;title&quot;, &quot;country&quot;,&quot;year&quot;)).  \/\/  on &quot;title&quot;, &quot;country&quot; &amp; &quot;year&quot;\r\n    by()                                      \/\/  on &quot;genres&quot; by value\r\ngremlin&gt;\r\n<\/pre>\n<p>So we concatenate all the genres into a collection using <code><strong>out(\"belongsTo\").values(\"name\").fold()<\/strong><\/code> and label it as <em>&#8220;genres&#8221;<\/em> and then project on it with simply <code><strong>by()<\/strong><\/code> because there is no more transformation required.<\/p>\n<p><strong>However we get no result, how comes ????<\/strong><\/p>\n<blockquote><p>This is a well-known caveat when using <strong>Gremlin<\/strong>. <strong>Whenever you reach a reducing barrier (<code>fold()<\/code>, <code>sum()<\/code>, <code>count()<\/code> &#8230;) all the path history leading to this barrier is destroyed<\/strong>. Since all the labels are contained in the path object (see previous blog post on <a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13320\"><strong>path object<\/strong><\/a> for more details) and they are wiped out, the <code><strong>select()<\/strong><\/code> step now returns no result<\/p><\/blockquote>\n<p>To fix the issue, we use a nice trick, we perform a the reduction inside a <code><strong>map()<\/strong><\/code> step and label the result of the map operation. The inner traversal inside <code><strong>map()<\/strong><\/code> hits the reducing barrier but does not impact our outer traversal and it helps preserving all the path history:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).       \/\/ Iterator&lt;Movie&gt;\r\n  as(&quot;blade_runner&quot;).                          \/\/ &quot;blade_runner&quot; label\r\n  map(out(&quot;belongsTo&quot;).values(&quot;name&quot;).fold()). \/\/ Iterator&lt;Collection&lt;String&gt;&gt; with a single collection\r\n  as(&quot;genres&quot;).                                \/\/ &quot;genres&quot; label\r\n  select(&quot;blade_runner&quot;, &quot;genres&quot;).            \/\/ Projection\r\n    by(valueMap(&quot;title&quot;, &quot;country&quot;,&quot;year&quot;)).   \/\/  on &quot;title&quot;, &quot;country&quot; &amp; &quot;year&quot;\r\n    by()                                       \/\/  on &quot;genres&quot; by value\r\n==&gt;{blade_runner={country=[United States], year=[1982], title=[Blade Runner]}, genres=[Sci-Fi, Action]}\r\n<\/pre>\n<p>And we&#8217;re done!<\/p>\n<h3>D. path().by(&#8230;)<\/h3>\n<p>We have seen in the previous post some usage of <strong>Gremlin<\/strong> path object. In this section we&#8217;ll take a closer look at some  projection techniques on the path object.<\/p>\n<p>For Blade Runner, we want to display the rating the movie got and the user who gave this rating, for this:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).  \/\/ Iterator&lt;Movie&gt;\r\n  inE(&quot;rated&quot;).                           \/\/ Iterator&lt;Rated_Edge&gt;\r\n  filter(values(&quot;rating&quot;).is(gte(8.0))).  \/\/ iterator.filter(rated -&gt; rated.getRating() &gt;= 8.0))\r\n  outV().                                 \/\/ Iterator&lt;User&gt;\r\n  path().                                 \/\/ Show path projecting on\r\n    by(&quot;rating&quot;).                         \/\/ &quot;rating&quot; property of rated edge\r\n    by(&quot;title&quot;).                          \/\/ &quot;title&quot; property of Movie\r\n    by(&quot;userId&quot;)                          \/\/ &quot;userId&quot; property of User\r\nThe property does not exist as the key has no associated value for the provided element: v[{~label=movie, community_id=285248128, member_id=2}]:rating\r\nType ':help' or ':h' for help.\r\nDisplay stack trace? [yN]\r\n<\/pre>\n<p>We got an error. Indeed <strong>Gremlin<\/strong> cannot find the property <em>&#8220;rating&#8221;<\/em> on the vertex Movie. How did <strong>Gremlin<\/strong> proceed to the property lookup within each <code><strong>by()<\/strong><\/code> modulator ? <strong>By following the order of the elements collected in the traversal history (path)<\/strong>. <\/p>\n<p>In our path <\/p>\n<ul>\n<li>we first collected Blade Runner Movie vertex<\/li>\n<li>then <em>&#8220;rated&#8221;<\/em> edge<\/li>\n<li>then User vertex<\/li>\n<\/ul>\n<p>So <strong>Gremlin<\/strong> expects to find property <em>&#8220;rating&#8221;<\/em> on Movie vertex, property <em>&#8220;title&#8221;<\/em> on <em>&#8220;rated&#8221;<\/em> edge and property <em>&#8220;userId&#8221;<\/em> on User vertex in this order.<\/p>\n<p>To fix the traversal we just need to re-order the <code><strong>by()<\/strong><\/code> modulators<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).  \/\/ Iterator&lt;Movie&gt;\r\n  inE(&quot;rated&quot;).                           \/\/ Iterator&lt;Rated_Edge&gt;\r\n  filter(values(&quot;rating&quot;).is(gte(8.0))).  \/\/ iterator.filter(rated -&gt; rated.getRating() &gt;= 8.0))\r\n  outV().                                 \/\/ Iterator&lt;User&gt;\r\n  path().                                 \/\/ Show path projecting on\r\n    by(&quot;title&quot;).                          \/\/ &quot;title&quot; property of Movie\r\n    by(&quot;rating&quot;).                         \/\/ &quot;rating&quot; property of rated edge\r\n    by(&quot;userId&quot;)                          \/\/ &quot;userId&quot; property of User\r\n==&gt;[Blade Runner, 8, u751]\r\n==&gt;[Blade Runner, 8, u310]\r\n==&gt;[Blade Runner, 8, u318]\r\n==&gt;[Blade Runner, 8, u672]\r\n==&gt;[Blade Runner, 9, u622]\r\n==&gt;[Blade Runner, 9, u628]\r\n==&gt;[Blade Runner, 8, u651]\r\n==&gt;[Blade Runner, 8, u590]\r\n...\r\n<\/pre>\n<p>So far so good. But implicit property lookup can be annoying. To make things explicit, we can use the <code><strong>choose(...).option(...)<\/strong><\/code> steps to simulate an <em>if\/else\/elseif\/then<\/em> logic<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;). \/\/ Iterator&lt;Movie&gt;\r\n  inE(&quot;rated&quot;).                          \/\/ Iterator&lt;Rated_Edge&gt;\r\n  filter(values(&quot;rating&quot;).is(gte(8.0))). \/\/ iterator.filter(rated -&gt; rated.getRating() &gt;= 8.0))\r\n  outV().                                \/\/ Iterator&lt;User&gt;\r\n  path().by(                             \/\/ Show path projecting on\r\n    choose(label()).                     \/\/ If vertex\/edge label\r\n    option(&quot;movie&quot;, values(&quot;title&quot;)).    \/\/   == &quot;movie&quot; then pick &quot;title&quot; property\r\n    option(&quot;user&quot;, values(&quot;userId&quot;)).    \/\/   == &quot;user&quot; then pick &quot;userId&quot; property\r\n    option(&quot;rated&quot;, values(&quot;rating&quot;)))   \/\/   == &quot;rated&quot; then pick &quot;rating&quot; property\r\n==&gt;[Blade Runner, 8, u751]\r\n==&gt;[Blade Runner, 8, u310]\r\n==&gt;[Blade Runner, 8, u318]\r\n==&gt;[Blade Runner, 8, u672]\r\n==&gt;[Blade Runner, 9, u622]\r\n==&gt;[Blade Runner, 9, u628]\r\n==&gt;[Blade Runner, 8, u651]\r\n==&gt;[Blade Runner, 8, u590]\r\n...\r\n<\/pre>\n<h3 id=\"group_by\">E. group(&#8230;).by(&#8230;).by(&#8230;)<\/h3>\n<p>Although grouping has been discussed in the <a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=13224\" target=\"_blank\"><strong>1<sup>st<\/sup> blog post<\/strong><\/a>, I&#8217;m going more into detail here about the 2 <code><strong>by()<\/strong><\/code> modulators.<\/p>\n<p>The 1<sup>st<\/sup> <code><strong>by(...)<\/strong><\/code> is defining the grouping axis, e.g. on which property\/criteria you want to group things. This can be either an intrinsic property of a vertex\/graph or even a complete inner traversal.<\/p>\n<p>The 2<sup>nd<\/sup> <code><strong>by(...)<\/strong><\/code> defines the projection axis, e.g. on which property\/criteria you want to project the grouped vertices\/edges. Again this can be either an intrinsic property of a vertex\/graph or even a complete inner traversal.<\/p>\n<p>Examples are better than words:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;). \/\/ Iterator&lt;Movie&gt; \r\n  inE(&quot;rated&quot;).                          \/\/ Iterator&lt;Rated_Edge&gt;    \r\n    has(&quot;rating&quot;, is(gte(8.0))).         \/\/ iterator.filter(rated -&gt; rated.getRating()&gt;=8.0)\r\n  outV().                                \/\/ Iterator&lt;User&gt;\r\n  group().                               \/\/ Group\r\n    by(&quot;age&quot;).                           \/\/   by &quot;age&quot;\r\n    by(&quot;userId&quot;)                         \/\/   project on &quot;userId&quot;\r\n==&gt;{64=[u610, u408, u452, u476], 14=[u91], 16=[u8], 17=[u136], 19=[u1031, u613, u541], 21=[u689, u1098, u239], 22=[u356, u456], 23=[u273, u663, u678, u390], 24=[u718, u692], 26=[u628, u848, u288], 27=[u445, u685], 29=[u751, u241, u420, u767, u474, u1000], 30=[u423], 31=[u405], 32=[u473], 33=[u548, u421, u472], 34=[u310, u1090, u1011, u262], 35=[u223, u440, u785], 36=[u657, u267], 37=[u434, u398], 38=[u205], 39=[u429, u365, u896], 40=[u693], 41=[u498], 42=[u347, u536], 43=[u389, u287], 44=[u523], 45=[u436, u565], 46=[u335], 47=[u591], 48=[u622], 50=[u672, u264], 51=[u507], 52=[u514], 53=[u318, u277, u328], 54=[u524, u301, u268], 55=[u590], 56=[u477, u569], 57=[u210, u545], 58=[u551], 59=[u651, u202], 60=[u595, u218, u491, u563], 63=[u324, u331, u632, u567]}\r\n<\/pre>\n<p>Above, we group fans of <strong>Blade Runner<\/strong> by their <em>&#8220;age&#8221;<\/em> and only display the <em>&#8220;userId&#8221;<\/em> (projection on <em>&#8220;userId&#8221;<\/em>)<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;). \/\/ Iterator&lt;Movie&gt;\r\n  out(&quot;actor&quot;).                          \/\/ Iterator&lt;Person&gt;  \r\n  group().                               \/\/ Group by\r\n    by(&quot;name&quot;).                          \/\/   actor name\r\n    by(__.in(&quot;actor&quot;).count())           \/\/    project on the number of movies played by this actor\r\n==&gt;{William Sanderson=1, Harrison Ford=10, Joe Turkel=1, Daryl Hannah=4, Morgan Paull=1, Edward James Olmos=1, Joanna Cassidy=2, M. Emmet Walsh=2, Sean Young=3, Rutger Hauer=3, Hy Pyke=1, Brion James=3, James Hong=2}\r\n<\/pre>\n<p>Above, we group the actors playing in <strong>Blade Runner<\/strong> by their name and project on the number of movies they have played in.<\/p>\n<h3>F. where(&#8230;).by(&#8230;).by(&#8230;)<\/h3>\n<p>The <code><strong>where()<\/strong><\/code> clause is usually used to filter the traversal but do you know it can be also used to lookup labeled steps and even project those steps in some axis using inner traversal ?<\/p>\n<p>Let&#8217;s see it in action:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).      \/\/ Iterator&lt;Movie&gt;\r\n  inE(&quot;rated&quot;).values(&quot;rating&quot;).fold().       \/\/ Iterator&lt;Collection&lt;Int&gt;&gt;\r\n  as(&quot;ratings&quot;).                              \/\/ Label the ratings\r\n  V().                                        \/\/ Start a new traversal\r\n  hasLabel(&quot;movie&quot;).                          \/\/ Select all movies\r\n  as(&quot;movies&quot;).                               \/\/ Label them &quot;movies&quot;\r\n  where(&quot;movies&quot;, gte(&quot;ratings&quot;)).            \/\/ where &quot;movies&quot; &gt;= &quot;ratings&quot; ???? \r\n    by(inE(&quot;rated&quot;).values(&quot;rating&quot;).mean()). \/\/  project &quot;movies&quot; on its avg rating\r\n    by(unfold().mean()).                      \/\/  project &quot;ratings&quot; on its mean      \r\n  limit(10).\r\n  project(&quot;title&quot;, &quot;avg_rating&quot;).\r\n    by(&quot;title&quot;).\r\n    by(inE(&quot;rated&quot;).values(&quot;rating&quot;).mean())\r\n==&gt;{title=American History X, avg_rating=8.297709923664122}\r\n==&gt;{title=The Simpsons (TV Series), avg_rating=8.602564102564102}\r\n==&gt;{title=One, Two, Three, avg_rating=8.3}\r\n==&gt;{title=The Great Escape, avg_rating=8.254237288135593}\r\n==&gt;{title=Bicycle Thieves, avg_rating=8.428571428571429}\r\n==&gt;{title=Citizen Kane, avg_rating=8.235294117647058}\r\n==&gt;{title=Duck Soup, avg_rating=8.257142857142858}\r\n==&gt;{title=Pulp Fiction, avg_rating=8.581005586592179}\r\n==&gt;{title=Unforgiven, avg_rating=8.296296296296296}\r\n==&gt;{title=In the Name of the Father, avg_rating=8.253731343283581}\r\n<\/pre>\n<p>So some explanation is required since the traversal is rather complex:<\/p>\n<p><code><strong>inE(\"rated\").values(\"rating\").fold().as(\"ratings\")<\/strong><\/code>: we collect all the ratings for <strong>Blade Runner<\/strong> into a <code><strong>Iterator&lt;Collection&lt;Int&gt;&gt;<\/strong><\/code> and save them as <em>&#8220;ratings&#8221;<\/em>.<\/p>\n<p><code><strong>where(\"movies\", gte(\"ratings\"))<\/strong><\/code>: this where clause is non-sensical in itself, you don&#8217;t compare Movie vertices to an <code><strong>Iterator&lt;Collection&lt;Int&gt;&gt;<\/strong><\/code>!<\/p>\n<p>That&#8217;s why the projections by <code><strong>by()<\/strong><\/code> come into play.<\/p>\n<p><code><strong>by(inE(\"rated\").values(\"rating\").mean())<\/strong><\/code> projects <em>&#8220;movies&#8221;<\/em> on its average rating.<br \/>\n<code><strong>by(unfold().mean())<\/strong><\/code> projects <em>&#8220;ratings&#8221;<\/em> by talking the nested collection of integer and computing their average.<\/p>\n<h3>G. dedup(&#8230;).by(&#8230;)<\/h3>\n<p>This projection after <code><strong>dedup()<\/strong><\/code> is rarely used because it&#8217;s hard to find a relevant use case. However here it is:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  has(&quot;movie&quot;, &quot;title&quot;, &quot;Blade Runner&quot;).         \/\/ Iterator&lt;Movie&gt;\r\n  as(&quot;blade_runner&quot;).\r\n  inE(&quot;rated&quot;).has(&quot;rating&quot;, gte(8.203)).outV(). \/\/ Iterator&lt;User&gt;\r\n  outE(&quot;rated&quot;).has(&quot;rating&quot;, gte(8.203)).inV(). \/\/ Iterator&lt;Movie&gt;\r\n  where(neq(&quot;blade_runner&quot;)).                 \r\n  dedup().                                       \/\/ Deduplicate the movies\r\n    by(&quot;country&quot;).                               \/\/  by their country !!\r\n  project(&quot;title&quot;,&quot;country&quot;).\r\n    by(&quot;title&quot;).\r\n    by(&quot;country&quot;)\r\n<\/pre>\n<p>The example is somewhat contrived, it is here just to illustrate the usage of <code><strong>dedup(...).by(...)<\/strong><\/code>. Normally <code><strong>dedup()<\/strong><\/code> uses the canonical <code><strong>equals()<\/strong><\/code> and <code><strong>hashCode()<\/strong><\/code> methods but you can impose your own projection for deduplication with the <code><strong>by()<\/strong><\/code> modulator. In the above traversal we just take 1 movie for each country.<\/p>\n<h1>III Selection<\/h1>\n<p>By selection, we mean <strong>SQL selection<\/strong> e.g. filtering the vertices\/edges to be returned.<\/p>\n<p>For selection <strong>Gremlin<\/strong> exposes 3 operators\/steps:<\/p>\n<ul>\n<li><code><strong>has(property, predicate)<\/strong><\/code><\/li>\n<li><code><strong>filter(predicate)<\/strong><\/code><\/li>\n<li><code><strong>where(predicate)<\/strong><\/code> or <code><strong>where(label, predicate)<\/strong><\/code><\/li>\n<\/ul>\n<blockquote><p>One important remark about filtering and selection. They rely heavily on <strong>primary keys<\/strong> or <strong>indices<\/strong>. If the column\/property on which you filter is NOT either a primary key or an indexed property, Gremlin would have no other choice than performing a full scan of all possible vertices\/edges and that can just kill your server.<\/p><\/blockquote>\n<h3>A. has(property, predicate)<\/h3>\n<p>This is the most common and useful step for filtering on vertex\/edge properties. Let&#8217;s say we want to fetch the first 5 movies released in the USA<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).\r\n  has(&quot;country&quot;, Search.tokenPrefix(&quot;United&quot;)).\r\n  limit(5).\r\n  valueMap(&quot;title&quot;, &quot;country&quot;)\r\n==&gt;{country=[United States], title=[The Italian Job]}\r\n==&gt;{country=[United States], title=[Great Expectations]}\r\n==&gt;{country=[United States], title=[The Assassination of Jesse James By The Coward Robert Ford]}\r\n==&gt;{country=[United States], title=[Alien Resurrection]}\r\n==&gt;{country=[United States], title=[A Star Is Born]}\r\n<\/pre>\n<p>Please notice the usage of specific <a href=\"http:\/\/docs.datastax.com\/en\/dse\/5.1\/dse-dev\/datastax_enterprise\/graph\/using\/useSearchIndexes.html#useSearchIndexes__tokenPrefix-Text\"><strong>DSE Search<\/strong><\/a> step <code><strong>Search.tokenPrefix(\"United\")<\/strong><\/code><\/p>\n<p>One big limitation of <code><strong>has()<\/strong><\/code> is that it only works on intrinsic properties of vertex\/edge, you cannot perform filtering on inner traversals. For this we need <code><strong>filter(predicate)<\/strong><\/code><\/p>\n<h3>B. filter(predicate)<\/h3>\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  hasLabel(&quot;movie&quot;).\r\n  filter(values(&quot;country&quot;).is(Search.tokenPrefix(&quot;United&quot;))).\r\n  limit(5).\r\n  valueMap(&quot;title&quot;, &quot;country&quot;)\r\n==&gt;{country=[United States], title=[The Italian Job]}\r\n==&gt;{country=[United States], title=[Great Expectations]}\r\n==&gt;{country=[United States], title=[The Assassination of Jesse James By The Coward Robert Ford]}\r\n==&gt;{country=[United States], title=[Alien Resurrection]}\r\n==&gt;{country=[United States], title=[A Star Is Born]}\r\n<\/pre>\n<p>In fact, any <code><strong>has(property, predicate)<\/strong><\/code> step can be rewritten as <code><strong>filter(values(property).predicate)<\/strong><\/code>. But <code><strong>filter(predicate)<\/strong><\/code> can do more than that:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).                           \r\n  has(&quot;country&quot;, Search.tokenPrefix(&quot;United&quot;)).\r\n  filter(out(&quot;belongsTo&quot;).values(&quot;name&quot;).is(&quot;Sci-Fi&quot;)).\r\n  limit(5).\r\n  project(&quot;title&quot;, &quot;genres&quot;).\r\n    by(&quot;title&quot;).\r\n    by(out(&quot;belongsTo&quot;).values(&quot;name&quot;).fold())\r\n==&gt;{title=Alien Resurrection, genres=[Sci-Fi, Horror]}\r\n==&gt;{title=Total Recall, genres=[Sci-Fi, Action]}\r\n==&gt;{title=Battlefield Earth, genres=[Sci-Fi, Action]}\r\n==&gt;{title=Eternal Sunshine of the Spotless Mind, genres=[Romance, Sci-Fi, Comedy, Drama]}\r\n==&gt;{title=Back to the Future. Part III, genres=[Sci-Fi, Western, Adventure, Comedy, Fantasy]}\r\n<\/pre>\n<p>Using <code><strong>filter(predicate)<\/strong><\/code> we restrict the movies released in the USA to those having genre <strong>Sci-Fi<\/strong>.<\/p>\n<h3>C. where(predicate) or where(label, predicate)<\/h3>\n<p>We can achieve the same result using <code><strong>where(predicate)<\/strong><\/code><\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).                           \r\n  has(&quot;country&quot;, Search.tokenPrefix(&quot;United&quot;)).\r\n  where(out(&quot;belongsTo&quot;).values(&quot;name&quot;).is(&quot;Sci-Fi&quot;)).\r\n  limit(5).\r\n  project(&quot;title&quot;, &quot;genres&quot;).\r\n    by(&quot;title&quot;).\r\n    by(out(&quot;belongsTo&quot;).values(&quot;name&quot;).fold())\r\n==&gt;{title=Alien Resurrection, genres=[Sci-Fi, Horror]}\r\n==&gt;{title=Total Recall, genres=[Sci-Fi, Action]}\r\n==&gt;{title=Battlefield Earth, genres=[Sci-Fi, Action]}\r\n==&gt;{title=Eternal Sunshine of the Spotless Mind, genres=[Romance, Sci-Fi, Comedy, Drama]}\r\n==&gt;{title=Back to the Future. Part III, genres=[Sci-Fi, Western, Adventure, Comedy, Fantasy]}\r\n<\/pre>\n<p>But the real advantage of <code><strong>where()<\/strong><\/code> is its ability to resolve\/lookup previously labeled steps in the current traversal. Let&#8217;s say we want to find movies where the director plays in his own movie e.g. the director is also one of the actors.<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).as(&quot;movie&quot;).         \/\/ Iterator&lt;Movie&gt; labeled as &quot;movie&quot;\r\n  out(&quot;director&quot;).as(&quot;director&quot;).        \/\/ Itrator&lt;User&gt; labeled as &quot;director&quot;\r\n  select(&quot;movie&quot;).                       \/\/ Jump back to &quot;movie&quot; \r\n  out(&quot;actor&quot;).as(&quot;actors&quot;).             \/\/ Iterator&lt;User&gt; labeled as &quot;actors&quot;\r\n  where(eq(&quot;director&quot;)).                 \/\/ Actor should be same as &quot;director&quot;\r\n  limit(5).                               \r\n  select(&quot;movie&quot;, &quot;director&quot;, &quot;actors&quot;). \/\/ Projection of the saved labeled\r\n    by(&quot;title&quot;).                         \/\/ by movie &quot;title&quot; \r\n    by(&quot;name&quot;).                          \/\/ by director &quot;name&quot;\r\n    by(values(&quot;name&quot;).fold())            \/\/ by actors &quot;name&quot; \r\n==&gt;{movie=Scary Movie, director=Keenen Ivory Wayans, actors=[Keenen Ivory Wayans]}\r\n==&gt;{movie=Much Ado About Nothing, director=Kenneth Branagh, actors=[Kenneth Branagh]}\r\n==&gt;{movie=Love and Death, director=Woody Allen, actors=[Woody Allen]}\r\n==&gt;{movie=Torrente, el brazo tonto de la ley, director=Santiago Segura, actors=[Santiago Segura]}\r\n==&gt;{movie=The Fly, director=David Cronenberg, actors=[David Cronenberg]}\r\n<\/pre>\n<p>We have found some of those movies but the actors list is wrong, it contains only one actor which is also director. The problem is in the step <code><strong>out(\"actor\").as(\"actors\").where(eq(\"director\"))<\/strong><\/code>. Indeed <strong>among all actors of the movie we only retain the one which is also director and we label it as &#8220;actors&#8221;<\/strong>. Thus the &#8220;actors&#8221; label is restricted to the director itself.<\/p>\n<p>To fix it, on the projection step, we can fetch the complete list of actors by navigating from the movie itself:<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).as(&quot;movie&quot;).           \/\/ Iterator&lt;Movie&gt; labeled as &quot;movie&quot;\r\n  out(&quot;director&quot;).as(&quot;director&quot;).          \/\/ Iterator&lt;User&gt; labeled as &quot;director&quot;\r\n  select(&quot;movie&quot;).                         \/\/ Jump back to &quot;movie&quot; \r\n  out(&quot;actor&quot;).as(&quot;actors&quot;).               \/\/ Iterator&lt;User&gt; labeled as &quot;actors&quot;\r\n  where(eq(&quot;director&quot;)).                   \/\/ Actor should be same as &quot;director&quot;\r\n  limit(5).                               \r\n  select(&quot;movie&quot;, &quot;director&quot;, &quot;movie&quot;).    \/\/ Projection of the saved labeled\r\n    by(&quot;title&quot;).                           \/\/ by movie &quot;title&quot; \r\n    by(&quot;name&quot;).                            \/\/ by director &quot;name&quot;\r\n    by(out(&quot;actor&quot;).values(&quot;name&quot;).fold()) \/\/ by actors' &quot;name&quot; navigating from movie\r\n==&gt;{movie=[David L. Lander, Cheri Oteri, Dave Sheridan, Carmen Electra, Anna Faris, Shawn Wayans, Shannon Elizabeth, Andrea Nemeth, Regina Hall, Dan Joffre, Kurt Fuller, Keenen Ivory Wayans, Lochlyn Munro, Jon Abrahams, Trevor Roberts, Marlon Wayans, James Van Der Beek], director=Keenen Ivory Wayans}\r\n==&gt;{movie=[Denzel Washington, Kenneth Branagh, Jimmy Yuill, Alex Lowe, Brian Blessed, Patrick Doyle, Richard Briers, Robert Sean Leonard, Imelda Staunton, Kate Beckinsale, Phyllida Law, Gerard Horan, Keanu Reeves, Emma Thompson, Richard Clifford, Ben Elton, Michael Keaton], director=Kenneth Branagh}\r\n==&gt;{movie=[Howard Vernon, Woody Allen, Alfred Lutter, Aubrey Morris, James Tolkan, Olga Georges-Picot, Harold Gould, Diane Keaton, Jessica Harper], director=Woody Allen}\r\n==&gt;{movie=[Ca\u00f1ita Brava, Tony Leblanc, Jorge Sanz, Jimmy Barnat\u00e1n, Neus Asensi, Gabino Diego, Nuria Carbonell, Nuria Carbonell, Chus Lampreave, Mariola Fuentes, Andreu Buenafuente, Fernando Trueba, Dar\u00edo Paso, Santiago Barullo, Santiago Segura, Santiago Urrialde, Carlos Lucas, Carlos Perea, Espartaco Santoni, M\u00e1ximo Pradera, Poli D\u00edaz, El Gran Wyoming, Daniel Monz\u00f3n, Julio Sanju\u00e1n, Carlos Bardem, Carlos Faemino, Manuel Manqui\u00f1a, Manuel Tallaf\u00e9, Antonio de la Torre, Javier Bardem, Javier Cansado, Javier C\u00e1mara], director=Santiago Segura}\r\n==&gt;{movie=[Joy Boushel, Jeff Goldblum, Geena Davis, Shawn Hewitt, Leslie Carlson, Michael Copeman, Carol Lazare, John Getz, George Chuvalo, David Cronenberg], director=David Cronenberg}\r\n<\/pre>\n<p>The result is again non-sensical. We expect to have title, director name and actors&#8217; name but instead we get the list of actors as &#8220;movie&#8221; and the director name &#8230;<\/p>\n<p>The problem lies in the fact that on the <code><strong>select(\"movie\", \"director\", \"movie\")<\/strong><\/code> step we re-use the label <em>&#8220;movie&#8221;<\/em> twice ! How <strong>Gremlin<\/strong> resolves labeled steps projection is described below:<\/p>\n<ul>\n<li><em>&#8220;movie&#8221;<\/em> \u2192 <code><strong>by(\"title\")<\/strong><\/code> <\/li>\n<li><em>&#8220;director&#8221;<\/em> \u2192 <code><strong>by(\"name\")<\/strong><\/code> <\/li>\n<li><em>&#8220;movie&#8221;<\/em> \u2192 <code><strong>by(out(\"actor\").values(\"name\").fold())<\/strong><\/code> <\/li>\n<\/ul>\n<p>So in the end, the last projection for <em>&#8220;movie&#8221;<\/em> label will override the first one and that explains the weird output we have. To fix that we only need to <strong>double-label<\/strong> the first movie step<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\ngremlin&gt;g.V().\r\n  hasLabel(&quot;movie&quot;).as(&quot;movie&quot;).as(&quot;movie_actors&quot;) \/\/ Double labelling\r\n  out(&quot;director&quot;).as(&quot;director&quot;).          \r\n  select(&quot;movie&quot;).                         \r\n  out(&quot;actor&quot;).as(&quot;actors&quot;).               \r\n  where(eq(&quot;director&quot;)).                   \r\n  limit(5).                               \r\n  select(&quot;movie&quot;, &quot;director&quot;, &quot;movie_actors&quot;).     \/\/ Projection of the saved labeled\r\n    by(&quot;title&quot;).                                   \/\/ by movie &quot;title&quot; \r\n    by(&quot;name&quot;).                                    \/\/ by director &quot;name&quot;\r\n    by(out(&quot;actor&quot;).values(&quot;name&quot;).fold())         \/\/ by actors' &quot;name&quot; navigating from movie\r\n==&gt;{movie=Scary Movie, director=Keenen Ivory Wayans, movie_actors=[David L. Lander, Cheri Oteri, Dave Sheridan, Carmen Electra, Anna Faris, Shawn Wayans, Shannon Elizabeth, Andrea Nemeth, Regina Hall, Dan Joffre, Kurt Fuller, Keenen Ivory Wayans, Lochlyn Munro, Jon Abrahams, Trevor Roberts, Marlon Wayans, James Van Der Beek]}\r\n==&gt;{movie=Much Ado About Nothing, director=Kenneth Branagh, movie_actors=[Denzel Washington, Kenneth Branagh, Jimmy Yuill, Alex Lowe, Brian Blessed, Patrick Doyle, Richard Briers, Robert Sean Leonard, Imelda Staunton, Kate Beckinsale, Phyllida Law, Gerard Horan, Keanu Reeves, Emma Thompson, Richard Clifford, Ben Elton, Michael Keaton]}\r\n==&gt;{movie=Love and Death, director=Woody Allen, movie_actors=[Howard Vernon, Woody Allen, Alfred Lutter, Aubrey Morris, James Tolkan, Olga Georges-Picot, Harold Gould, Diane Keaton, Jessica Harper]}\r\n==&gt;{movie=Torrente, el brazo tonto de la ley, director=Santiago Segura, movie_actors=[Ca\u00f1ita Brava, Tony Leblanc, Jorge Sanz, Jimmy Barnat\u00e1n, Neus Asensi, Gabino Diego, Nuria Carbonell, Nuria Carbonell, Chus Lampreave, Mariola Fuentes, Andreu Buenafuente, Fernando Trueba, Dar\u00edo Paso, Santiago Barullo, Santiago Segura, Santiago Urrialde, Carlos Lucas, Carlos Perea, Espartaco Santoni, M\u00e1ximo Pradera, Poli D\u00edaz, El Gran Wyoming, Daniel Monz\u00f3n, Julio Sanju\u00e1n, Carlos Bardem, Carlos Faemino, Manuel Manqui\u00f1a, Manuel Tallaf\u00e9, Antonio de la Torre, Javier Bardem, Javier Cansado, Javier C\u00e1mara]}\r\n==&gt;{movie=The Fly, director=David Cronenberg, movie_actors=[Joy Boushel, Jeff Goldblum, Geena Davis, Shawn Hewitt, Leslie Carlson, Michael Copeman, Carol Lazare, John Getz, George Chuvalo, David Cronenberg]}\r\n<\/pre>\n<p>Now the result is correct.<\/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 6th 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 Recursive traversals Path object<\/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\/13352"}],"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=13352"}],"version-history":[{"count":21,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/13352\/revisions"}],"predecessor-version":[{"id":13398,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/13352\/revisions\/13398"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=13352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=13352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}