CINXE.COM

Recipes

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 2.0.18"> <title>Recipes</title> <style> /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*! normalize.css v2.1.2 | MIT License | git.io/normalize */ /* ========================================================================== HTML5 display definitions ========================================================================== */ /** Correct `block` display not defined in IE 8/9. */ @import url(http://cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.1/css/font-awesome.css); article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } /** Correct `inline-block` display not defined in IE 8/9. */ audio, canvas, video { display: inline-block; } /** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */ [hidden], template { display: none; } script { display: none !important; } /* ========================================================================== Base ========================================================================== */ /** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** Remove default margin. */ body { margin: 0; } /* ========================================================================== Links ========================================================================== */ /** Remove the gray background color from active links in IE 10. */ a { background: transparent; } /** Address `outline` inconsistency between Chrome and other browsers. */ a:focus { outline: thin dotted; } /** Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* ========================================================================== Typography ========================================================================== */ /** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** Address styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /** Address styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /** Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** Correct font family set oddly in Safari 5 and Chrome. */ code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } /** Improve readability of pre-formatted text in all browsers. */ pre { white-space: pre-wrap; } /** Set consistent quote types. */ q { quotes: "\201C" "\201D" "\2018" "\2019"; } /** Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* ========================================================================== Embedded content ========================================================================== */ /** Remove border when inside `a` element in IE 8/9. */ img { border: 0; } /** Correct overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* ========================================================================== Figures ========================================================================== */ /** Address margin not present in IE 8/9 and Safari 5. */ figure { margin: 0; } /* ========================================================================== Forms ========================================================================== */ /** Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } /** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ button, input { line-height: normal; } /** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */ button, select { text-transform: none; } /** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */ button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */ textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } /* ========================================================================== Tables ========================================================================== */ /** Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; } meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; } meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; } *, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } html, body { font-size: 100%; } body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; } a:hover { cursor: pointer; } img, object, embed { max-width: 100%; height: auto; } object, embed { height: 100%; } img { -ms-interpolation-mode: bicubic; } #map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } .left { float: left !important; } .right { float: right !important; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-justify { text-align: justify !important; } .hide { display: none; } .antialiased, body { -webkit-font-smoothing: antialiased; } img { display: inline-block; vertical-align: middle; } textarea { height: auto; min-height: 50px; } select { width: 100%; } p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; } .subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .mathblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #6c818f; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; } /* Typography resets */ div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; } /* Default Link Styles */ a { color: #444444; text-decoration: underline; line-height: inherit; } a:hover, a:focus { color: #111111; } a img { border: none; } /* Default paragraph styles */ p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; text-rendering: optimizeLegibility; } p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; } /* Default header styles */ h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; font-weight: bold; font-style: normal; color: #465158; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.2125em; } h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #909ea7; line-height: 0; } h1 { font-size: 2.125em; } h2 { font-size: 1.6875em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; } h4 { font-size: 1.125em; } h5 { font-size: 1.125em; } h6 { font-size: 1em; } hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; } /* Helpful Typography Defaults */ em, i { font-style: italic; line-height: inherit; } strong, b { font-weight: bold; line-height: inherit; } small { font-size: 60%; line-height: inherit; } code { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; color: #444444; } /* Lists */ ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; } ul, ol { margin-left: 0; } ul.no-bullet, ol.no-bullet { margin-left: 0; } /* Unordered Lists */ ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ } ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } ul.square { list-style-type: square; } ul.circle { list-style-type: circle; } ul.disc { list-style-type: disc; } ul.no-bullet { list-style: none; } /* Ordered Lists */ ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; } /* Definition Lists */ dl dt { margin-bottom: 0.3em; font-weight: bold; } dl dd { margin-bottom: 0.75em; } /* Abbreviations */ abbr, acronym { text-transform: uppercase; font-size: 90%; color: black; border-bottom: 1px dotted #dddddd; cursor: help; } abbr { text-transform: none; } /* Blockquotes */ blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; } blockquote cite { display: block; font-size: 0.8125em; color: #748590; } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited { color: #748590; } blockquote, blockquote p { line-height: 1.5; color: #909ea7; } /* Microformats */ .vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; } .vcard li { margin: 0; display: block; } .vcard .fn { font-weight: bold; font-size: 0.9375em; } .vevent .summary { font-weight: bold; } .vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; } @media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; } h1 { font-size: 2.75em; } h2 { font-size: 2.3125em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; } h4 { font-size: 1.4375em; } } /* Print styles. Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) */ .print-only { display: none !important; } @media print { * { background: transparent !important; color: #000 !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; /* h5bp.com/t */ } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; } h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; } .hide-on-print { display: none !important; } .print-only { display: block !important; } .hide-for-print { display: none !important; } .show-for-print { display: inherit !important; } } /* Tables */ table { background: white; margin-bottom: 1.25em; border: solid 0 #dddddd; } table thead, table tfoot { background: none; font-weight: bold; } table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222222; text-align: left; } table tr th, table tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222222; } table tr.even, table tr.alt, table tr:nth-of-type(even) { background: none; } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; } .clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; } .clearfix:after, .float-group:after { clear: both; } *:not(pre) > code { font-size: 0.95em; padding: 0; white-space: nowrap; background-color: #f2f2f2; border: 0 solid #dddddd; -webkit-border-radius: 6px; border-radius: 6px; text-shadow: none; } pre, pre > code { line-height: 1.2; color: inherit; font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; } .keyseq { color: #333333; } kbd:not(.keyseq) { display: inline-block; color: black; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; } .keyseq kbd:first-child { margin-left: 0; } .keyseq kbd:last-child { margin-right: 0; } .menuseq, .menu { color: black; } b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; } b.button:before { content: "["; padding: 0 3px 0 2px; } b.button:after { content: "]"; padding: 0 2px 0 3px; } p a > code:hover { color: #373737; } #header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; } #header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; } #header:after, #content:after, #footnotes:after, #footer:after { clear: both; } #header { margin-bottom: 2.5em; } #header > h1 { color: #111111; font-weight: bold; border-bottom: 1px solid #dddddd; margin-bottom: -28px; padding-bottom: 32px; } #header span { color: #909ea7; } #header #revnumber { text-transform: capitalize; } #header br { display: none; } #header br + span { padding-left: 3px; } #header br + span:before { content: "\2013 \0020"; } #header br + span.author { padding-left: 0; } #header br + span.author:before { content: ", "; } #toc { border-bottom: 1px solid #dddddd; padding-bottom: 1.25em; } #toc > ul { margin-left: 0.25em; } #toc ul.sectlevel0 > li > a { font-style: italic; } #toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } #toc ul { list-style-type: none; } #toctitle { color: #6c818f; } @media only screen and (min-width: 768px) { body.toc2 { padding-left: 15em; padding-right: 0; } #toc.toc2 { position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #dddddd; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; } #toc.toc2 #toctitle { margin-top: 0; font-size: 1.2em; } #toc.toc2 > ul { font-size: .90em; } #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; } #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } body.toc2.toc-right { padding-left: 0; padding-right: 15em; } body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #dddddd; left: auto; right: 0; } } @media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; } #toc.toc2 { width: 20em; } #toc.toc2 #toctitle { font-size: 1.375em; } #toc.toc2 > ul { font-size: 0.95em; } #toc.toc2 ul ul { padding-left: 1.25em; } body.toc2.toc-right { padding-left: 0; padding-right: 20em; } } #content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 6px; border-radius: 6px; } #content #toc > :first-child { margin-top: 0; } #content #toc > :last-child { margin-bottom: 0; } #content #toc a { text-decoration: none; } #content #toctitle { font-weight: bold; font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; font-size: 1em; padding-left: 0.125em; } #footer { max-width: 100%; background-color: black; padding: 1.25em; } #footer-text { color: white; line-height: 1.35; } .sect1 { padding-bottom: 1.25em; } .sect1 + .sect1 { border-top: 1px solid #dddddd; } #content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; } #content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; vertical-align: text-top; display: block; margin-top: 0.05em; } #content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; } #content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #465158; text-decoration: none; } #content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #3b444a; } .imageblock, .literalblock, .listingblock, .mathblock, .verseblock, .videoblock { margin-bottom: 1.25em; } .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .mathblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; } .tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; } table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; } .admonitionblock > table { border: 0; background: none; width: 100%; } .admonitionblock > table td.icon { text-align: center; width: 80px; } .admonitionblock > table td.icon img { max-width: none; } .admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; } .admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #909ea7; } .admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; } .exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 6px; border-radius: 6px; } .exampleblock > .content > :first-child { margin-top: 0; } .exampleblock > .content > :last-child { margin-bottom: 0; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #333333; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; } .exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; } .exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; } .sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 6px; border-radius: 6px; } .sidebarblock > :first-child { margin-top: 0; } .sidebarblock > :last-child { margin-bottom: 0; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #333333; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; } .sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; } .sidebarblock > .content > .title { color: #6c818f; margin-top: 0; line-height: 1.5; } .exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; } .literalblock pre:not([class]), .listingblock pre:not([class]) { background: #eeeeee; } .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border-width: 1px; border-style: solid; border-color: #cccccc; -webkit-border-radius: 6px; border-radius: 6px; padding: 0.5em; word-wrap: break-word; } .literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } .literalblock pre > code, .literalblock pre[class] > code, .listingblock pre > code, .listingblock pre[class] > code { display: block; } @media only screen { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.76em; } } @media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.855em; } } @media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.95em; } } .listingblock pre.highlight { padding: 0; } .listingblock pre.highlight > code { padding: 0.5em; } .listingblock > .content { position: relative; } .listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; } .listingblock:hover code.asciidoc:before { content: "asciidoc"; } .listingblock:hover code.clojure:before { content: "clojure"; } .listingblock:hover code.css:before { content: "css"; } .listingblock:hover code.groovy:before { content: "groovy"; } .listingblock:hover code.html:before { content: "html"; } .listingblock:hover code.java:before { content: "java"; } .listingblock:hover code.javascript:before { content: "javascript"; } .listingblock:hover code.python:before { content: "python"; } .listingblock:hover code.ruby:before { content: "ruby"; } .listingblock:hover code.sass:before { content: "sass"; } .listingblock:hover code.scss:before { content: "scss"; } .listingblock:hover code.xml:before { content: "xml"; } .listingblock:hover code.yaml:before { content: "yaml"; } .listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } .listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; } table.pyhltable { border: 0; margin-bottom: 0; } table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; } table.pyhltable td.code { padding-left: .75em; padding-right: 0; } .highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; } .highlight.pygments .lineno { display: inline-block; margin-right: .25em; } table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; } .quoteblock { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; } .quoteblock blockquote { margin: 0 0 1.25em 0; padding: 0 0 0.5625em 0; border: 0; } .quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; } .quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: 0.8125em; color: #748590; } .quoteblock .attribution br { display: none; } .quoteblock .attribution cite { display: block; margin-bottom: 0.625em; } table thead th, table tfoot th { font-weight: bold; } table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 6px; border-radius: 6px; border-top: 0 solid #dddddd; border-bottom: 0 solid #dddddd; } table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; } table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; } table.tableblock td .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { margin-bottom: 0; } th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; } th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; } th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; } th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; } th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; } th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; } tbody tr th { display: table-cell; line-height: 1.5; background: none; } tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: #222222; font-weight: bold; } td > div.verse { white-space: pre; } ol { margin-left: 0.25em; } ul li ol { margin-left: 0; } dl dd { margin-left: 1.125em; } dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; } ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; } ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; } ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; } ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; } ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; } ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; } ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; } ul.inline > li > * { display: block; } .unstyled dl dt { font-weight: normal; font-style: normal; } ol.arabic { list-style-type: decimal; } ol.decimal { list-style-type: decimal-leading-zero; } ol.loweralpha { list-style-type: lower-alpha; } ol.upperalpha { list-style-type: upper-alpha; } ol.lowerroman { list-style-type: lower-roman; } ol.upperroman { list-style-type: upper-roman; } ol.lowergreek { list-style-type: lower-greek; } .hdlist > table, .colist > table { border: 0; background: none; } .hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; } td.hdlist1 { padding-right: .75em; font-weight: bold; } td.hdlist1, td.hdlist2 { vertical-align: top; } .literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; } .colist > table tr > td:first-of-type { padding: 0 .75em; line-height: 1; } .colist > table tr > td:last-of-type { padding: 0.25em 0; } .qanda > ol > li > p > em:only-child { color: #373737; } .thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; } .imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; } .imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; } .imageblock > .title { margin-bottom: 0; } .imageblock.thumb, .imageblock.th { border-width: 6px; } .imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; } .image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; } .image.left { margin-right: 0.625em; } .image.right { margin-left: 0.625em; } a.image { text-decoration: none; } span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; } span.footnote a, span.footnoteref a { text-decoration: none; } #footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; } #footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; } #footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; } #footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; } #footnotes .footnote:last-of-type { margin-bottom: 0; } #content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; } .gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; } .gist .file-data > table td.line-data { width: 99%; } div.unbreakable { page-break-inside: avoid; } .big { font-size: larger; } .small { font-size: smaller; } .underline { text-decoration: underline; } .overline { text-decoration: overline; } .line-through { text-decoration: line-through; } .aqua { color: #00bfbf; } .aqua-background { background-color: #00fafa; } .black { color: black; } .black-background { background-color: black; } .blue { color: #0000bf; } .blue-background { background-color: #0000fa; } .fuchsia { color: #bf00bf; } .fuchsia-background { background-color: #fa00fa; } .gray { color: #606060; } .gray-background { background-color: #7d7d7d; } .green { color: #006000; } .green-background { background-color: #007d00; } .lime { color: #00bf00; } .lime-background { background-color: #00fa00; } .maroon { color: #600000; } .maroon-background { background-color: #7d0000; } .navy { color: #000060; } .navy-background { background-color: #00007d; } .olive { color: #606000; } .olive-background { background-color: #7d7d00; } .purple { color: #600060; } .purple-background { background-color: #7d007d; } .red { color: #bf0000; } .red-background { background-color: #fa0000; } .silver { color: #909090; } .silver-background { background-color: #bcbcbc; } .teal { color: #006060; } .teal-background { background-color: #007d7d; } .white { color: #bfbfbf; } .white-background { background-color: #fafafa; } .yellow { color: #bfbf00; } .yellow-background { background-color: #fafa00; } span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } .admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; } .admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #444444; color: #333333; } .admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; } .admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; } .admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; } .admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; } .conum { display: inline-block; color: white !important; background-color: black; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; } .conum * { color: white !important; } .conum + b { display: none; } .conum:after { content: attr(data-value); } .conum:not([data-value]):empty { display: none; } .listingblock code { white-space: pre; overflow: auto; overflow-wrap: normal; /* needed for webkit browsers */ } #toc ul.sectlevel0 > li > a { font-style: normal; font-weight: bold; } h4 { color: #6c818f; } .literalblock > .content > pre, .listingblock > .content > pre { -webkit-border-radius: 6px; border-radius: 6px; margin-left: 2em; margin-right: 2em; } .admonitionblock { margin-left: 2em; margin-right: 2em; } .admonitionblock > table { border: 1px solid #609060; border-top-width: 1.5em; background-color: #e9ffe9; border-collapse: separate; -webkit-border-radius: 0; border-radius: 0; } .admonitionblock > table td.icon { padding-top: .5em; padding-bottom: .5em; } .admonitionblock > table td.content { padding: .5em 1em; color: black; font-size: .9em; border-left: none; } .sidebarblock { background-color: #e8ecef; border-color: #ccc; } .sidebarblock > .content > .title { color: #444444; } table.tableblock.grid-all { border-collapse: collapse; -webkit-border-radius: 0; border-radius: 0; } table.tableblock.grid-all th.tableblock, table.tableblock.grid-all td.tableblock { border-bottom: 1px solid #aaa; } #footer { background-color: #465158; padding: 2em; } #footer-text { color: #eee; font-size: 0.8em; text-align: center; } .tabs{position:relative;margin:40px auto;width:1024px;max-width:100%;overflow:hidden;padding-top:10px;margin-bottom:60px}.tabs input{position:absolute;z-index:1000;height:50px;left:0;top:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);cursor:pointer;margin:0}.tabs input:hover+label{background:#e08f24}.tabs label{background:#e9ffe9;color:#1a1a1a;font-size:15px;line-height:50px;height:60px;position:relative;top:0;padding:0 20px;float:left;display:block;letter-spacing:1px;text-transform:uppercase;font-weight:bold;text-align:center;box-shadow:2px 0 2px rgba(0,0,0,0.1),-2px 0 2px rgba(0,0,0,0.1);box-sizing:border-box;-webkit-transition:all 150ms ease 0s;transition:all 150ms ease 0s}.tabs label:hover{cursor:pointer}.tabs label:after{content:'';background:#609060;position:absolute;bottom:-2px;left:0;width:100%;height:2px;display:block}.tabs-2 input{width:50%}.tabs-2 input.tab-selector-1{left:0%}.tabs-2 input.tab-selector-2{left:50%}.tabs-2 label{width:50%}.tabs-3 input{width:33.3333333333%}.tabs-3 input.tab-selector-1{left:0%}.tabs-3 input.tab-selector-2{left:33.3333333333%}.tabs-3 input.tab-selector-3{left:66.6666666667%}.tabs-3 label{width:33.3333333333%}.tabs-4 input{width:25%}.tabs-4 input.tab-selector-1{left:0%}.tabs-4 input.tab-selector-2{left:25%}.tabs-4 input.tab-selector-3{left:50%}.tabs-4 input.tab-selector-4{left:75%}.tabs-4 label{width:25%}.tabs-5 input{width:20%}.tabs-5 input.tab-selector-1{left:0%}.tabs-5 input.tab-selector-2{left:20%}.tabs-5 input.tab-selector-3{left:40%}.tabs-5 input.tab-selector-4{left:60%}.tabs-5 input.tab-selector-5{left:80%}.tabs-5 label{width:20%}.tabs-6 input{width:16.6666666667%}.tabs-6 input.tab-selector-1{left:0%}.tabs-6 input.tab-selector-2{left:16.6666666667%}.tabs-6 input.tab-selector-3{left:33.3333333333%}.tabs-6 input.tab-selector-4{left:50%}.tabs-6 input.tab-selector-5{left:66.6666666667%}.tabs-6 input.tab-selector-6{left:83.3333333333%}.tabs-6 label{width:16.6666666667%}.tabs-7 input{width:14.2857142857%}.tabs-7 input.tab-selector-1{left:0%}.tabs-7 input.tab-selector-2{left:14.2857142857%}.tabs-7 input.tab-selector-3{left:28.5714285714%}.tabs-7 input.tab-selector-4{left:42.8571428571%}.tabs-7 input.tab-selector-5{left:57.1428571429%}.tabs-7 input.tab-selector-6{left:71.4285714286%}.tabs-7 input.tab-selector-7{left:85.7142857143%}.tabs-7 label{width:14.2857142857%}.tabs label:first-of-type{z-index:4}.tab-label-2{z-index:4}.tab-label-3{z-index:3}.tab-label-4{z-index:2}.tabs input:checked+label{background:#609060;color:#fefefe;z-index:6}.clear-shadow{clear:both}.tabcontent{height:auto;width:100%;float:left;position:relative;z-index:5;background:#eee;top:-10px;box-sizing:border-box}.tabcontent>div{position:relative;float:left;width:0;height:0;box-sizing:border-box;top:0;left:0;z-index:1;opacity:0;background:#eee}.tabcontent .CodeRay{background-color:#fefefe}.tabs .tab-selector-1:checked ~ .tabcontent .tabcontent-1{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-2:checked ~ .tabcontent .tabcontent-2{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-3:checked ~ .tabcontent .tabcontent-3{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-4:checked ~ .tabcontent .tabcontent-4{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-5:checked ~ .tabcontent .tabcontent-5{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-6:checked ~ .tabcontent .tabcontent-6{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-7:checked ~ .tabcontent .tabcontent-7{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px} .invisible {color: rgba(0,0,0,0); font-size: 0;} </style> <style> /*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */ pre.CodeRay{background:#f7f7f8} .CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .CodeRay span.line-numbers{display:inline-block;margin-right:.75em} .CodeRay .line-numbers strong{color:#000} table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none} table.CodeRay td{vertical-align:top;line-height:inherit} table.CodeRay td.line-numbers{text-align:right} table.CodeRay td.code{padding:0 0 0 .75em} .CodeRay .debug{color:#fff!important;background:navy!important} .CodeRay .annotation{color:#007} .CodeRay .attribute-name{color:navy} .CodeRay .attribute-value{color:#700} .CodeRay .binary{color:#509} .CodeRay .comment{color:#998;font-style:italic} .CodeRay .char{color:#04d} .CodeRay .char .content{color:#04d} .CodeRay .char .delimiter{color:#039} .CodeRay .class{color:#458;font-weight:bold} .CodeRay .complex{color:#a08} .CodeRay .constant,.CodeRay .predefined-constant{color:teal} .CodeRay .color{color:#099} .CodeRay .class-variable{color:#369} .CodeRay .decorator{color:#b0b} .CodeRay .definition{color:#099} .CodeRay .delimiter{color:#000} .CodeRay .doc{color:#970} .CodeRay .doctype{color:#34b} .CodeRay .doc-string{color:#d42} .CodeRay .escape{color:#666} .CodeRay .entity{color:#800} .CodeRay .error{color:#808} .CodeRay .exception{color:inherit} .CodeRay .filename{color:#099} .CodeRay .function{color:#900;font-weight:bold} .CodeRay .global-variable{color:teal} .CodeRay .hex{color:#058} .CodeRay .integer,.CodeRay .float{color:#099} .CodeRay .include{color:#555} .CodeRay .inline{color:#000} .CodeRay .inline .inline{background:#ccc} .CodeRay .inline .inline .inline{background:#bbb} .CodeRay .inline .inline-delimiter{color:#d14} .CodeRay .inline-delimiter{color:#d14} .CodeRay .important{color:#555;font-weight:bold} .CodeRay .interpreted{color:#b2b} .CodeRay .instance-variable{color:teal} .CodeRay .label{color:#970} .CodeRay .local-variable{color:#963} .CodeRay .octal{color:#40e} .CodeRay .predefined{color:#369} .CodeRay .preprocessor{color:#579} .CodeRay .pseudo-class{color:#555} .CodeRay .directive{font-weight:bold} .CodeRay .type{font-weight:bold} .CodeRay .predefined-type{color:inherit} .CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold} .CodeRay .key{color:#808} .CodeRay .key .delimiter{color:#606} .CodeRay .key .char{color:#80f} .CodeRay .value{color:#088} .CodeRay .regexp .delimiter{color:#808} .CodeRay .regexp .content{color:#808} .CodeRay .regexp .modifier{color:#808} .CodeRay .regexp .char{color:#d14} .CodeRay .regexp .function{color:#404;font-weight:bold} .CodeRay .string{color:#d20} .CodeRay .string .string .string{background:#ffd0d0} .CodeRay .string .content{color:#d14} .CodeRay .string .char{color:#d14} .CodeRay .string .delimiter{color:#d14} .CodeRay .shell{color:#d14} .CodeRay .shell .delimiter{color:#d14} .CodeRay .symbol{color:#990073} .CodeRay .symbol .content{color:#a60} .CodeRay .symbol .delimiter{color:#630} .CodeRay .tag{color:teal} .CodeRay .tag-special{color:#d70} .CodeRay .variable{color:#036} .CodeRay .insert{background:#afa} .CodeRay .delete{background:#faa} .CodeRay .change{color:#aaf;background:#007} .CodeRay .head{color:#f8f;background:#505} .CodeRay .insert .insert{color:#080} .CodeRay .delete .delete{color:#800} .CodeRay .change .change{color:#66f} .CodeRay .head .head{color:#f4f} </style> <!-- --> <!-- Matomo --> <script> var _paq = window._paq = window._paq || []; /* We explicitly disable cookie tracking to avoid privacy issues */ _paq.push(['disableCookies']); /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="https://analytics.apache.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '27']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script> <!-- End Matomo Code --> </head> <body class="book toc2 toc-left"> <div id="header"> <div id="toc" class="toc2"> <div id="toctitle">Table of Contents</div> <ul class="sectlevel0"> <li><a href="#_recipes">Recipes</a></li> <li><a href="#_traversal_recipes">Traversal Recipes</a> <ul class="sectlevel1"> <li><a href="#between-vertices">Between Vertices</a></li> <li><a href="#centrality">Centrality</a> <ul class="sectlevel2"> <li><a href="#degree-centrality">Degree Centrality</a></li> <li><a href="#betweeness-centrality">Betweeness Centrality</a></li> <li><a href="#closeness-centrality">Closeness Centrality</a></li> <li><a href="#eigenvector-centrality">Eigenvector Centrality</a></li> <li><a href="#pagerank-centrality">PageRank Centrality</a></li> </ul> </li> <li><a href="#collections">Collections</a></li> <li><a href="#connected-components">Connected Components</a> <ul class="sectlevel2"> <li><a href="#_small_graph_traversals">Small graph traversals</a></li> <li><a href="#_small_graph_scalability">Small graph scalability</a></li> <li><a href="#_large_graphs">Large graphs</a></li> </ul> </li> <li><a href="#cycle-detection">Cycle Detection</a></li> <li><a href="#duplicate-edge">Duplicate Edge Detection</a></li> <li><a href="#duplicate-vertex">Duplicate Vertex Detection</a></li> <li><a href="#edge-move">Moving an Edge</a></li> <li><a href="#element-existence">Element Existence</a></li> <li><a href="#if-then-based-grouping">If-Then Based Grouping</a></li> <li><a href="#looping">Looping</a> <ul class="sectlevel2"> <li><a href="#_conditional_looping_with_max_depth">Conditional Looping with Max Depth</a></li> <li><a href="#_emitting_loop_depth">Emitting Loop Depth</a></li> <li><a href="#_optional_loop_depth">Optional Loop Depth</a></li> </ul> </li> <li><a href="#operating-on-dropped-elements">Operating on Dropped Elements</a></li> <li><a href="#pagination">Pagination</a></li> <li><a href="#recommendation">Recommendation</a></li> <li><a href="#shortest-path">Shortest Path</a></li> <li><a href="#traversal-induced-values">Traversal Induced Values</a></li> <li><a href="#tree">Tree</a> <ul class="sectlevel2"> <li><a href="#_lowest_common_ancestor">Lowest Common Ancestor</a></li> <li><a href="#_maximum_depth">Maximum Depth</a></li> <li><a href="#_time_based_indexing">Time-based Indexing</a></li> </ul> </li> <li><a href="#olap-spark-yarn">OLAP traversals with Spark on YARN</a> <ul class="sectlevel2"> <li><a href="#_approach">Approach</a></li> <li><a href="#_prerequisites">Prerequisites</a></li> <li><a href="#_running_the_job">Running the job</a></li> <li><a href="#_explanation">Explanation</a></li> <li><a href="#_additional_configuration_options">Additional configuration options</a></li> </ul> </li> </ul> </li> <li><a href="#_anti_patterns">Anti-Patterns</a> <ul class="sectlevel1"> <li><a href="#long-traversals">Long Traversals</a></li> <li><a href="#unspecified-keys-and-labels">Unspecified Keys and Labels</a></li> <li><a href="#unnecessary-steps">Unnecessary Steps</a></li> <li><a href="#unspecified-label-in-global-vertex-lookup">Unspecified Label in Global Vertex lookup</a></li> <li><a href="#steps-instead-of-tokens">Steps Instead of Tokens</a></li> <li><a href="#_has_and_traversal_arguments">has() and Traversal Arguments</a></li> </ul> </li> <li><a href="#_implementation_recipes">Implementation Recipes</a> <ul class="sectlevel1"> <li><a href="#style-guide">Style Guide</a></li> <li><a href="#traversal-component-reuse">Traversal Component Reuse</a></li> </ul> </li> <li><a href="#contributing">How to Contribute a Recipe</a></li> <li><a href="#_appendix">Appendix</a></li> </ul> </div> </div> <div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <a class="image" href="https://tinkerpop.apache.org"><img src="../images/apache-tinkerpop-logo.png" alt="apache tinkerpop logo" width="500"></a> </div> </div> <div class="paragraph"> <p><strong>3.7.3</strong></p> </div> </div> </div> <h1 id="_recipes" class="sect0">Recipes</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-chef.png" alt="gremlin chef" width="120"></span> All programming languages tend to have <a href="https://en.wikipedia.org/wiki/Software_design_pattern">patterns of usage</a> for commonly occurring problems. Gremlin is not different in that respect. There are many commonly occurring traversal themes that have general applicability to any graph. Gremlin Recipes present these common traversal patterns and methods of usage that will provide some basic building blocks for virtually any graph in any domain.</p> </div> <div class="paragraph"> <p>Recipes assume general familiarity with Gremlin and the Apache TinkerPop™ stack. Be sure to have read the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/getting-started">Getting Started</a> tutorial and the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/the-gremlin-console/">The Gremlin Console</a> tutorial.</p> </div> </div> </div> <h1 id="_traversal_recipes" class="sect0">Traversal Recipes</h1> <div class="sect1"> <h2 id="between-vertices">Between Vertices</h2> <div class="sectionbody"> <div class="paragraph"> <p>It is quite common to have a situation where there are two particular vertices of a graph and a need to execute some traversal on the paths found between them. Consider the following examples using the modern toy graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796703-1" type="radio" name="radio-set-1729796703-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796703-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796703-2" type="radio" name="radio-set-1729796703-1" class="tab-selector-2" /> <label for="tab-1729796703-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).bothE() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] ==&gt;e[<span class="integer">8</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">4</span>] gremlin&gt; g.V(<span class="integer">1</span>).bothE().where(otherV().hasId(<span class="integer">2</span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] gremlin&gt; v1 = g.V(<span class="integer">1</span>).next();<span class="type">[]</span> gremlin&gt; v2 = g.V(<span class="integer">2</span>).next();<span class="type">[]</span> gremlin&gt; g.V(v1).bothE().where(otherV().is(v2)) <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] gremlin&gt; g.V(v1).outE().where(inV().is(v2)) <span class="comment">//</span>// <b class="conum">(4)</b> ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] gremlin&gt; g.V(<span class="integer">1</span>).outE().where(inV().has(id, within(<span class="integer">2</span>,<span class="integer">3</span>))) <span class="comment">//</span>// <b class="conum">(5)</b> ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] gremlin&gt; g.V(<span class="integer">1</span>).out().where(__.in().hasId(<span class="integer">6</span>)) <span class="comment">//</span>// <b class="conum">(6)</b> ==&gt;v[<span class="integer">3</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).bothE() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).bothE().where(otherV().hasId(<span class="integer">2</span>)) <span class="comment">//</span>// <b class="conum">(2)</b> v1 = g.V(<span class="integer">1</span>).next();<span class="type">[]</span> v2 = g.V(<span class="integer">2</span>).next();<span class="type">[]</span> g.V(v1).bothE().where(otherV().is(v2)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(v1).outE().where(inV().is(v2)) <span class="comment">//</span>// <b class="conum">(4)</b> g.V(<span class="integer">1</span>).outE().where(inV().has(id, within(<span class="integer">2</span>,<span class="integer">3</span>))) <span class="comment">//</span>// <b class="conum">(5)</b> g.V(<span class="integer">1</span>).out().where(__.in().hasId(<span class="integer">6</span>)) <span class="invisible">//</span><b class="conum">6</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>There are three edges from the vertex with the identifier of "1".</p> </li> <li> <p>Filter those three edges using the <code>where()</code>-step using the identifier of the vertex returned by <code>otherV()</code> to ensure it matches on the vertex of concern, which is the one with an identifier of "2".</p> </li> <li> <p>Note that the same traversal will work if there are actual <code>Vertex</code> instances rather than just vertex identifiers.</p> </li> <li> <p>The vertex with identifier "1" has all outgoing edges, so it would also be acceptable to use the directional steps of <code>outE()</code> and <code>inV()</code> since the schema allows it.</p> </li> <li> <p>There is also no problem with filtering the terminating side of the traversal on multiple vertices, in this case, vertices with identifiers "2" and "3".</p> </li> <li> <p>There&#8217;s no reason why the same pattern of exclusion used for edges with <code>where()</code> can&#8217;t work for a vertex between two vertices.</p> </li> </ol> </div> <div class="paragraph"> <p>The basic pattern of using <code>where()</code>-step to find the "other" known vertex can be applied in far more complex scenarios. For one such example, consider the following traversal that finds all the paths between a group of defined vertices:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796703-3" type="radio" name="radio-set-1729796703-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796703-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796703-4" type="radio" name="radio-set-1729796703-3" class="tab-selector-2" /> <label for="tab-1729796703-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; ids = [<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">6</span>].toArray() ==&gt;<span class="integer">2</span> ==&gt;<span class="integer">4</span> ==&gt;<span class="integer">6</span> gremlin&gt; g.V(ids).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). repeat(bothE().otherV().simplePath()).times(<span class="integer">5</span>).emit(hasId(within(ids))).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). filter(select(last,<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).by(id).where(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>, lt(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>))). path().by().by(label) ==&gt;[v[<span class="integer">2</span>],knows,v[<span class="integer">1</span>],knows,v[<span class="integer">4</span>]] ==&gt;[v[<span class="integer">2</span>],knows,v[<span class="integer">1</span>],created,v[<span class="integer">3</span>],created,v[<span class="integer">4</span>]] ==&gt;[v[<span class="integer">2</span>],knows,v[<span class="integer">1</span>],created,v[<span class="integer">3</span>],created,v[<span class="integer">6</span>]] ==&gt;[v[<span class="integer">2</span>],knows,v[<span class="integer">1</span>],knows,v[<span class="integer">4</span>],created,v[<span class="integer">3</span>],created,v[<span class="integer">6</span>]] ==&gt;[v[<span class="integer">4</span>],created,v[<span class="integer">3</span>],created,v[<span class="integer">6</span>]] ==&gt;[v[<span class="integer">4</span>],knows,v[<span class="integer">1</span>],created,v[<span class="integer">3</span>],created,v[<span class="integer">6</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">ids = [<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">6</span>].toArray() g.V(ids).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). repeat(bothE().otherV().simplePath()).times(<span class="integer">5</span>).emit(hasId(within(ids))).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). filter(select(last,<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).by(id).where(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>, lt(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>))). path().by().by(label)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>For another example, consider the following schema:</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/recipe-job-schema.png" alt="recipe job schema" width="750"></span></p> </div> <div class="paragraph"> <p>Assume that the goal is to find information about a known job and a known person. Specifically, the idea would be to extract the known job, the company that created the job, the date it was created by the company and whether or not the known person completed an application.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796703-5" type="radio" name="radio-set-1729796703-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796703-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796703-6" type="radio" name="radio-set-1729796703-5" class="tab-selector-2" /> <label for="tab-1729796703-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">company</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Blueprints, Inc</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">company</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Rexster, LLC</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexster</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job1</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job2</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job3</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job4</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application1</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application2</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application3</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application4</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/20/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/15/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/16/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexster</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/18/2015</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; vBlueprintsJob1 = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job1</span><span class="delimiter">&quot;</span></span>).next() ==&gt;v[<span class="integer">8</span>] gremlin&gt; vRexsterJob1 = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job4</span><span class="delimiter">&quot;</span></span>).next() ==&gt;v[<span class="integer">14</span>] gremlin&gt; vStephen = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).next() ==&gt;v[<span class="integer">2</span>] gremlin&gt; vBob = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).next() ==&gt;v[<span class="integer">0</span>] gremlin&gt; g.V(vRexsterJob1).as(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). coalesce(__.in(<span class="string"><span class="delimiter">'</span><span class="content">appliesTo</span><span class="delimiter">'</span></span>).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">completes</span><span class="delimiter">'</span></span>).is(vStephen)), constant(<span class="predefined-constant">false</span>)).as(<span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). by().by().by(<span class="string"><span class="delimiter">'</span><span class="content">creationDate</span><span class="delimiter">'</span></span>).by() ==&gt;[<span class="key">job</span>:v[<span class="integer">14</span>],<span class="key">company</span>:v[<span class="integer">6</span>],<span class="key">created</span>:<span class="integer">12</span>/<span class="integer">18</span>/<span class="integer">2015</span>,<span class="key">application</span>:v[<span class="integer">20</span>]] gremlin&gt; g.V(vRexsterJob1, vBlueprintsJob1).as(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). coalesce(__.in(<span class="string"><span class="delimiter">'</span><span class="content">appliesTo</span><span class="delimiter">'</span></span>).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">completes</span><span class="delimiter">'</span></span>).is(vBob)), constant(<span class="predefined-constant">false</span>)).as(<span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). by().by().by(<span class="string"><span class="delimiter">'</span><span class="content">creationDate</span><span class="delimiter">'</span></span>).by() ==&gt;[<span class="key">job</span>:v[<span class="integer">14</span>],<span class="key">company</span>:v[<span class="integer">6</span>],<span class="key">created</span>:<span class="integer">12</span>/<span class="integer">18</span>/<span class="integer">2015</span>,<span class="key">application</span>:<span class="predefined-constant">false</span>] ==&gt;[<span class="key">job</span>:v[<span class="integer">8</span>],<span class="key">company</span>:v[<span class="integer">4</span>],<span class="key">created</span>:<span class="integer">12</span>/<span class="integer">20</span>/<span class="integer">2015</span>,<span class="key">application</span>:v[<span class="integer">16</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">company</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Blueprints, Inc</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">company</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Rexster, LLC</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexster</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job1</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job2</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job3</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job4</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application1</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application2</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application3</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">application</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">application4</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">completes</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appBob2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">appliesTo</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">appStephen2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob1</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/20/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob2</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/15/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprints</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">blueprintsJob3</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/16/2015</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexster</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">rexsterJob1</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">creationDate</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12/18/2015</span><span class="delimiter">&quot;</span></span>).iterate() vBlueprintsJob1 = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job1</span><span class="delimiter">&quot;</span></span>).next() vRexsterJob1 = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">job</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">job4</span><span class="delimiter">&quot;</span></span>).next() vStephen = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">stephen</span><span class="delimiter">&quot;</span></span>).next() vBob = g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).next() g.V(vRexsterJob1).as(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). coalesce(__.in(<span class="string"><span class="delimiter">'</span><span class="content">appliesTo</span><span class="delimiter">'</span></span>).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">completes</span><span class="delimiter">'</span></span>).is(vStephen)), constant(<span class="predefined-constant">false</span>)).as(<span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). by().by().by(<span class="string"><span class="delimiter">'</span><span class="content">creationDate</span><span class="delimiter">'</span></span>).by() g.V(vRexsterJob1, vBlueprintsJob1).as(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>). coalesce(__.in(<span class="string"><span class="delimiter">'</span><span class="content">appliesTo</span><span class="delimiter">'</span></span>).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">completes</span><span class="delimiter">'</span></span>).is(vBob)), constant(<span class="predefined-constant">false</span>)).as(<span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">job</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">company</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">application</span><span class="delimiter">'</span></span>). by().by().by(<span class="string"><span class="delimiter">'</span><span class="content">creationDate</span><span class="delimiter">'</span></span>).by()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>While the traversals above are more complex, the pattern for finding "things" between two vertices is largely the same. Note the use of the <code>where()</code>-step to terminate the traversers for a specific user. It is embedded in a <code>coalesce()</code> step to handle situations where the specified user did not complete an application for the specified job and will return <code>false</code> in those cases.</p> </div> </div> </div> <div class="sect1"> <h2 id="centrality">Centrality</h2> <div class="sectionbody"> <div class="paragraph"> <p>There are many measures of <a href="https://en.wikipedia.org/wiki/Centrality">centrality</a> which are meant to help identify the most important vertices in a graph. As these measures are common in graph theory, this section attempts to demonstrate how some of these different indicators can be calculated using Gremlin.</p> </div> <div class="sect2"> <h3 id="degree-centrality">Degree Centrality</h3> <div class="paragraph"> <p><a href="https://en.wikipedia.org/wiki/Centrality#Degree_centrality">Degree centrality</a> is a measure of the number of edges associated to each vertex. The following examples use the modern toy graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796714-1" type="radio" name="radio-set-1729796714-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796714-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796714-2" type="radio" name="radio-set-1729796714-1" class="tab-selector-2" /> <label for="tab-1729796714-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().group().by().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[v[<span class="integer">1</span>]:<span class="integer">3</span>,v[<span class="integer">2</span>]:<span class="integer">1</span>,v[<span class="integer">3</span>]:<span class="integer">3</span>,v[<span class="integer">4</span>]:<span class="integer">3</span>,v[<span class="integer">5</span>]:<span class="integer">1</span>,v[<span class="integer">6</span>]:<span class="integer">1</span>] gremlin&gt; g.V().group().by().by(inE().count()) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;[v[<span class="integer">1</span>]:<span class="integer">0</span>,v[<span class="integer">2</span>]:<span class="integer">1</span>,v[<span class="integer">3</span>]:<span class="integer">3</span>,v[<span class="integer">4</span>]:<span class="integer">1</span>,v[<span class="integer">5</span>]:<span class="integer">1</span>,v[<span class="integer">6</span>]:<span class="integer">0</span>] gremlin&gt; g.V().group().by().by(outE().count()) <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;[v[<span class="integer">1</span>]:<span class="integer">3</span>,v[<span class="integer">2</span>]:<span class="integer">0</span>,v[<span class="integer">3</span>]:<span class="integer">0</span>,v[<span class="integer">4</span>]:<span class="integer">2</span>,v[<span class="integer">5</span>]:<span class="integer">0</span>,v[<span class="integer">6</span>]:<span class="integer">1</span>] gremlin&gt; g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>).by().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(4)</b> ==&gt;[<span class="key">v</span>:v[<span class="integer">1</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">2</span>],<span class="key">degree</span>:<span class="integer">1</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">3</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">4</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">5</span>],<span class="key">degree</span>:<span class="integer">1</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">6</span>],<span class="key">degree</span>:<span class="integer">1</span>] gremlin&gt; g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>).by().by(bothE().count()). <span class="comment">//</span>// <b class="conum">(5)</b> order().by(<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>, desc). limit(<span class="integer">4</span>) ==&gt;[<span class="key">v</span>:v[<span class="integer">1</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">3</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">4</span>],<span class="key">degree</span>:<span class="integer">3</span>] ==&gt;[<span class="key">v</span>:v[<span class="integer">2</span>],<span class="key">degree</span>:<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().group().by().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().group().by().by(inE().count()) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().group().by().by(outE().count()) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>).by().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(4)</b> g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>).by().by(bothE().count()). <span class="comment">//</span>// <b class="conum">(5)</b> order().by(<span class="string"><span class="delimiter">&quot;</span><span class="content">degree</span><span class="delimiter">&quot;</span></span>, desc). limit(<span class="integer">4</span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Calculation of degree centrality which counts all incident edges on each vertex to include those that are both incoming and outgoing.</p> </li> <li> <p>Calculation of in-degree centrality which only counts incoming edges to a vertex.</p> </li> <li> <p>Calculation of out-degree centrality which only counts outgoing edges from a vertex.</p> </li> <li> <p>The previous examples all produce a single <code>Map</code> as their output. While that is a desirable output, producing a stream of <code>Map</code> objects can allow some greater flexibility.</p> </li> <li> <p>For example, use of a stream enables use of an ordered limit that can be executed in a distributed fashion in OLAP traversals.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#group-step">group</a> step takes up to two separate <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#by-step">by</a> modulators. The first <code>by()</code> tells <code>group()</code> what the key in the resulting <code>Map</code> will be (i.e. the value to group on). In the above examples, the <code>by()</code> is empty and as a result, the grouping will be on the incoming <code>Vertex</code> object itself. The second <code>by()</code> is the value to be stored in the <code>Map</code> for each key. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="betweeness-centrality">Betweeness Centrality</h3> <div class="paragraph"> <p><a href="https://en.wikipedia.org/wiki/Betweenness_centrality">Betweeness centrality</a> is a measure of the number of times a vertex is found between the <a href="#shortest-path">shortest path</a> of each vertex pair in a graph. Consider the following graph for demonstration purposes:</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/betweeness-example.png" alt="betweeness example" width="600"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796714-3" type="radio" name="radio-set-1729796714-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796714-3" class="tab-label-1">console (groovy )</label> <input id="tab-1729796714-4" type="radio" name="radio-set-1729796714-3" class="tab-selector-2" /> <label for="tab-1729796714-4" class="tab-label-2">groovy </label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(both().simplePath().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)).emit(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(select(last, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>). coalesce(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>), aggregate(local,<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(6)</b> select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).unfold(). <span class="comment">//</span>// <b class="conum">(7)</b> groupCount().next() <span class="comment">//</span>// <b class="conum">(8)</b> ==&gt;v[A]=<span class="integer">14</span> ==&gt;v[B]=<span class="integer">28</span> ==&gt;v[C]=<span class="integer">20</span> ==&gt;v[D]=<span class="integer">20</span> ==&gt;v[E]=<span class="integer">28</span> ==&gt;v[F]=<span class="integer">14</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate() g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(both().simplePath().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)).emit(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(select(last, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>). coalesce(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>), aggregate(local,<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(6)</b> select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).unfold(). <span class="comment">//</span>// <b class="conum">(7)</b> groupCount().next() <span class="invisible">//</span><b class="conum">8</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Starting from each vertex in the graph&#8230;&#8203;</p> </li> <li> <p>&#8230;&#8203;traverse on both - incoming and outgoing - edges, avoiding <a href="#cycle-detection">cyclic paths</a>.</p> </li> <li> <p>Create a triple consisting of the first vertex, the last vertex and the length of the path between them.</p> </li> <li> <p>Determine whether a path between those two vertices was already found.</p> </li> <li> <p>If this is the first path between the two vertices, store the triple in an internal collection named "triples".</p> </li> <li> <p>Keep only those paths between a pair of vertices that have the same length as the first path that was found between them.</p> </li> <li> <p>Select all shortest paths and unfold them.</p> </li> <li> <p>Count the number of occurrences of each vertex, which is ultimately its betweeness score.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Since the betweeness centrality algorithm requires the shortest path between any pair of vertices in the graph, its practical applications are very limited. It&#8217;s recommended to use this algorithm only on small subgraphs (graphs like the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#grateful-dead">Grateful Dead graph</a> with only 808 vertices and 8049 edges already require a massive amount of compute resources to determine the shortest paths between all vertex pairs). </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="closeness-centrality">Closeness Centrality</h3> <div class="paragraph"> <p><a href="https://en.wikipedia.org/wiki/Centrality">Closeness centrality</a> is a measure of the distance of one vertex to all other reachable vertices in the graph. The following examples use the modern toy graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796714-5" type="radio" name="radio-set-1729796714-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796714-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796714-6" type="radio" name="radio-set-1729796714-5" class="tab-selector-2" /> <label for="tab-1729796714-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="float">1f</span>).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(both().simplePath().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)).emit(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(select(last, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>). coalesce(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>), aggregate(local,<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(6)</b> group().by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(7)</b> by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local).sack(div).sack().sum()).next() ==&gt;v[<span class="integer">1</span>]=<span class="float">2.1666666666666665</span> ==&gt;v[<span class="integer">2</span>]=<span class="float">1.6666666666666665</span> ==&gt;v[<span class="integer">3</span>]=<span class="float">2.1666666666666665</span> ==&gt;v[<span class="integer">4</span>]=<span class="float">2.1666666666666665</span> ==&gt;v[<span class="integer">5</span>]=<span class="float">1.6666666666666665</span> ==&gt;v[<span class="integer">6</span>]=<span class="float">1.6666666666666665</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="float">1f</span>).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(both().simplePath().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)).emit(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(select(last, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>). coalesce(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">y</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>), aggregate(local,<span class="string"><span class="delimiter">&quot;</span><span class="content">triples</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">triple</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">z</span><span class="delimiter">&quot;</span></span>).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">length</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(6)</b> group().by(select(first, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(7)</b> by(select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">v</span><span class="delimiter">&quot;</span></span>).count(local).sack(div).sack().sum()).next()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Defines a Gremlin <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sack-step">sack</a> with a value of one.</p> </li> <li> <p>Traverses on both - incoming and outgoing - edges, avoiding <a href="#cycle-detection">cyclic paths</a>.</p> </li> <li> <p>Create a triple consisting of the first vertex, the last vertex and the length of the path between them.</p> </li> <li> <p>Determine whether a path between those two vertices was already found.</p> </li> <li> <p>If this is the first path between the two vertices, store the triple in an internal collection named "triples".</p> </li> <li> <p>Keep only those paths between a pair of vertices that have the same length as the first path that was found between them.</p> </li> <li> <p>For each vertex divide 1 by the product of the lengths of all shortest paths that start with this particular vertex.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Since the closeness centrality algorithm requires the shortest path between any pair of vertices in the graph, its practical applications are very limited. It&#8217;s recommended to use this algorithm only on small subgraphs (graphs like the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#grateful-dead">Grateful Dead graph</a> with only 808 vertices and 8049 edges already require a massive amount of compute resources to determine the shortest paths between all vertex pairs). </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="eigenvector-centrality">Eigenvector Centrality</h3> <div class="paragraph"> <p>A calculation of <a href="https://en.wikipedia.org/wiki/Centrality#Eigenvector_centrality">eigenvector centrality</a> uses the relative importance of adjacent vertices to help determine their centrality. In other words, unlike <a href="#degree-centrality">degree centrality</a> the vertex with the greatest number of incident edges does not necessarily give it the highest rank. Consider the following example using the Grateful Dead graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796714-7" type="radio" name="radio-set-1729796714-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796714-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796714-8" type="radio" name="radio-set-1729796714-7" class="tab-selector-2" /> <label for="tab-1729796714-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/grateful-dead.xml</span><span class="delimiter">'</span></span>).read().iterate() gremlin&gt; g.V().repeat(groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).out()).times(<span class="integer">5</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> order(local).by(values, desc).limit(local, <span class="integer">10</span>).next() <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;PLAYING IN THE BAND=<span class="integer">8758598</span> ==&gt;ME AND MY UNCLE=<span class="integer">8214246</span> ==&gt;JACK STRAW=<span class="integer">8173882</span> ==&gt;EL PASO=<span class="integer">7666994</span> ==&gt;TRUCKING=<span class="integer">7643494</span> ==&gt;PROMISED LAND=<span class="integer">7339027</span> ==&gt;CHINA CAT SUNFLOWER=<span class="integer">7322213</span> ==&gt;CUMBERLAND BLUES=<span class="integer">6730838</span> ==&gt;RAMBLE ON ROSE=<span class="integer">6676667</span> ==&gt;LOOKS LIKE RAIN=<span class="integer">6674121</span> gremlin&gt; g.V().repeat(groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).out().timeLimit(<span class="integer">100</span>)).times(<span class="integer">5</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> order(local).by(values, desc).limit(local, <span class="integer">10</span>).next() ==&gt;PLAYING IN THE BAND=<span class="integer">8758598</span> ==&gt;ME AND MY UNCLE=<span class="integer">8214246</span> ==&gt;JACK STRAW=<span class="integer">8173882</span> ==&gt;EL PASO=<span class="integer">7666994</span> ==&gt;TRUCKING=<span class="integer">7643494</span> ==&gt;PROMISED LAND=<span class="integer">7339027</span> ==&gt;CHINA CAT SUNFLOWER=<span class="integer">7322213</span> ==&gt;CUMBERLAND BLUES=<span class="integer">6730838</span> ==&gt;RAMBLE ON ROSE=<span class="integer">6676667</span> ==&gt;LOOKS LIKE RAIN=<span class="integer">6674121</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/grateful-dead.xml</span><span class="delimiter">'</span></span>).read().iterate() g.V().repeat(groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).out()).times(<span class="integer">5</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> order(local).by(values, desc).limit(local, <span class="integer">10</span>).next() <span class="comment">//</span>// <b class="conum">(2)</b> g.V().repeat(groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).out().timeLimit(<span class="integer">100</span>)).times(<span class="integer">5</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> order(local).by(values, desc).limit(local, <span class="integer">10</span>).next()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The traversal iterates through each vertex in the graph and for each one repeatedly group counts each vertex that passes through using the vertex as the key. The <code>Map</code> of this group count is stored in a variable named "m". The <code>out()</code> traversal is repeated thirty times or until the paths are exhausted. Five iterations should provide enough time to converge on a solution. Calling <code>cap('m')</code> at the end simply extracts the <code>Map</code> side-effect stored in "m".</p> </li> <li> <p>The entries in the <code>Map</code> are then iterated and sorted with the top ten most central vertices presented as output.</p> </li> <li> <p>The previous examples can be expanded on a little bit by including a <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#timelimit-step">time limit</a>. The <code>timeLimit()</code> prevents the traversal from taking longer than one hundred milliseconds to execute (the previous example takes considerably longer than that). While the answer provided with the <code>timeLimit()</code> is not the absolute ranking, it does provide a relative ranking that closely matches the absolute one. The use of <code>timeLimit()</code> in certain algorithms (e.g. recommendations) can shorten the time required to get a reasonable and usable result.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="pagerank-centrality">PageRank Centrality</h3> <div class="paragraph"> <p>While not technically a recipe, it&#8217;s worth noting here in the "Centrality Section" that <a href="https://en.wikipedia.org/wiki/PageRank">PageRank</a> centrality can be calculated with Gremlin with the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#pagerank-step">pageRank()</a>-step which is designed to work with <code>GraphComputer</code> (OLAP) based traversals.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796714-9" type="radio" name="radio-set-1729796714-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796714-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796714-10" type="radio" name="radio-set-1729796714-9" class="tab-selector-2" /> <label for="tab-1729796714-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g = traversal().withEmbedded(graph).withComputer() ==&gt;graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin&gt; g.V().pageRank().with(PageRank.propertyName,<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>) ==&gt;<span class="float">0.30472009079122503</span> ==&gt;<span class="float">0.17579889899708237</span> ==&gt;<span class="float">0.11375510357865543</span> ==&gt;<span class="float">0.11375510357865543</span> ==&gt;<span class="float">0.14598540152719108</span> ==&gt;<span class="float">0.14598540152719108</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g = traversal().withEmbedded(graph).withComputer() g.V().pageRank().with(PageRank.propertyName,<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> </div> </div> <div class="sect1"> <h2 id="collections">Collections</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image"><img src="../images/gremlin-collections.png" alt="gremlin collections" width="400"></span></p> </div> <div class="paragraph"> <p>Lists and maps form the basis for much of the processing in Gremlin traversals. They are core to how side-effects are typically held and how results are generally produced. Being able to pick them apart and reformat them is sometimes required. This need to shape the data within a traversal may arise both at the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#terminal-steps">terminal step</a> of the traversal (technically just prior to the terminal step) or in the middle of a traversal. Considering the former, a transformation just prior to iteration will get the result into the form required by the application which would remove the need for additional application level manipulation. Moreover, a transformation at this stage may reduce the size of the payload being returned which could be useful in remote applications. Examining the latter, there may be times where a <code>List</code> or <code>Map</code> requires some mid-traversal transformation so as to continue with the general logic of the traversal itself. For example, a traversal at some point might produce a <code>Map</code> of <code>List</code> objects where the lists contain vertices, where each <code>List</code> might need to be sorted by some criteria and then the top item for each extracted to become the basis for the continued traversal. Executing transformations for either of these types of situations can be made possible with the patterns described in this section.</p> </div> <div class="paragraph"> <p>The appearance of a <code>List</code> as a traverser in Gremlin usually arises as a result of a <code>fold()</code> operation, but may also appear by way of some side-effect steps like <code>aggregate()</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-1" type="radio" name="radio-set-1729796726-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-2" type="radio" name="radio-set-1729796726-1" class="tab-selector-2" /> <label for="tab-1729796726-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().fold() ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]] gremlin&gt; g.V().aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().fold() g.V().aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is worth noting that while a <code>Path</code> is not technically a <code>List</code> it does present like one and can be manipulated in similar fashion to lists:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-3" type="radio" name="radio-set-1729796726-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-4" type="radio" name="radio-set-1729796726-3" class="tab-selector-2" /> <label for="tab-1729796726-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().out().out().path() ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().out().out().path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>These examples are obviously trivial and there are other ways that a traverser might end up in a <code>List</code> form, but, at this moment, the point here is to focus less on how to get a <code>List</code> and more on how to manipulate one within the Gremlin language. The examples going forward will also be similarly contrived insofar as producing a usable <code>List</code> to manipulate. Bear in mind that it may be quite possible to get the same end results of these examples using more direct means than what is demonstrated.</p> </div> <div class="paragraph"> <p>It may seem simple, but the most obvious choice to modifying what is in a list is to simply <code>unfold()</code> the <code>List</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-5" type="radio" name="radio-set-1729796726-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-6" type="radio" name="radio-set-1729796726-5" class="tab-selector-2" /> <label for="tab-1729796726-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().fold().unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;marko ==&gt;vadas ==&gt;lop ==&gt;josh ==&gt;ripple ==&gt;peter gremlin&gt; g.V().aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;marko ==&gt;vadas ==&gt;lop ==&gt;josh ==&gt;ripple ==&gt;peter</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().fold().unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above examples show that <code>unfold()</code> works quite well when you don&#8217;t want to preserve the <code>List</code> structure of the traverser as it just flattens <code>List</code> traversers to the traversal stream. The above examples only have one <code>List</code> as a result, but consider what happens when there is more than one:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-7" type="radio" name="radio-set-1729796726-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-8" type="radio" name="radio-set-1729796726-7" class="tab-selector-2" /> <label for="tab-1729796726-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().union(fold(),fold()) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]] ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]] gremlin&gt; g.V().union(fold(),fold()).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;marko ==&gt;marko ==&gt;vadas ==&gt;vadas ==&gt;lop ==&gt;lop ==&gt;josh ==&gt;josh ==&gt;ripple ==&gt;ripple ==&gt;peter ==&gt;peter</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().union(fold(),fold()) g.V().union(fold(),fold()).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The two separate <code>List</code> traversers are flattened to a single traversal stream and all the results are mixed together. While this approach may be acceptable, there are many cases where it might not be so. To preserve the individual structure of the <code>List</code> traversers "locally" <code>unfold()</code> the lists to transform them:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-9" type="radio" name="radio-set-1729796726-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-10" type="radio" name="radio-set-1729796726-9" class="tab-selector-2" /> <label for="tab-1729796726-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). union(fold(),fold()). local(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()) ==&gt;[marko,vadas,lop,josh,ripple,peter] ==&gt;[marko,vadas,lop,josh,ripple,peter]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). union(fold(),fold()). local(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The call to <code>local()</code> executes its anonymous sub-traversal over each individual <code>List</code> iterator and as the sub-traversal ends with a <code>fold()</code>-step, the results are reduced back into a <code>List</code> to preserve the original structure, thus maintaining two traverser results.</p> </div> <div class="paragraph"> <p>This pattern for unfolding and folding <code>List</code> traversers ends up having other applications:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-11" type="radio" name="radio-set-1729796726-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-12" type="radio" name="radio-set-1729796726-11" class="tab-selector-2" /> <label for="tab-1729796726-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>]] ==&gt;[v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]] gremlin&gt; g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()). local(unfold(). <span class="comment">//</span>// <b class="conum">(2)</b> order(). by(bothE().count(),desc). limit(<span class="integer">1</span>). fold()) ==&gt;[v[<span class="integer">1</span>]] ==&gt;[v[<span class="integer">4</span>]] gremlin&gt; g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()). <span class="comment">//</span>// <b class="conum">(3)</b> local(unfold(). has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,gte(<span class="integer">29</span>)). values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). mean()) ==&gt;<span class="float">29.0</span> ==&gt;<span class="float">33.5</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()). local(unfold(). <span class="comment">//</span>// <b class="conum">(2)</b> order(). by(bothE().count(),desc). limit(<span class="integer">1</span>). fold()) g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()). <span class="comment">//</span>// <b class="conum">(3)</b> local(unfold(). has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,gte(<span class="integer">29</span>)). values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). mean())</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The output consists of two <code>List</code> traversers.</p> </li> <li> <p>For each <code>List</code> of vertices, order them by their number of edges, and choose the first one which will be the one with the highest degree (i.e. number of edges). By ending with <code>fold()</code> the <code>List</code> traverser structure is preserved thus returning two <code>List</code> objects. Consider this a method for choosing a "max" or a highly ranked vertex. In this case the rank was determined by the number of edges, but it could have just as easily been determined by a vertex property, edge property, a calculated value, etc. - one simply needs to alter the <code>by()</code>-step modulator to <code>order()</code>.</p> </li> <li> <p>For each <code>List</code> of vertices, filter that <code>List</code> to only include vertices that have an "age" property with a value greater than or equal to "29" and then average the results of each list. More generally, consider how this approach performs some kind of reducing calculation on each <code>List</code> traverser. In this case, an average was calculated, but it might also have been a <code>sum()</code>, <code>count()</code> or similar operation that reduced the list to a single calculated value.</p> </li> </ol> </div> <div class="paragraph"> <p>So far, this section has focused on what to do with a <code>List</code> traverser once there is one present and there have been fairly contrived examples for how to produce one in the first place. The use of <code>fold()</code> has been used most frequently at this point to achieve list creation and that step should be recalled whenever there is a need to reduce some traversal stream to an actual <code>List</code>. Of course, it may become necessary to more manually construct a <code>List</code>, especially in cases where the expected output of the traversal is composed of one or more ordered results in the form of a <code>List</code>. For example, consider the following three traversals:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-13" type="radio" name="radio-set-1729796726-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-14" type="radio" name="radio-set-1729796726-13" class="tab-selector-2" /> <label for="tab-1729796726-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;<span class="integer">29</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> repeat(out()). until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). path(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;[marko,lop] ==&gt;[marko,josh,ripple] ==&gt;[marko,josh,lop] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> repeat(outE().inV()). until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). path(). local(unfold(). has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). mean()) ==&gt;<span class="float">0.4</span> ==&gt;<span class="float">1.0</span> ==&gt;<span class="float">0.7</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> repeat(out()). until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). path(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> repeat(outE().inV()). until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). path(). local(unfold(). has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). mean())</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Get the age of "marko"</p> </li> <li> <p>get the "name" values of the vertices in the collected paths that traverse out from "marko" to any vertex with the "lang" of "java".</p> </li> <li> <p>Get the average of the "weight" values of edges in the collected paths that traverse out from "marko" to any vertex with the "lang" of "java". Note the use of the earlier defined pattern that used <code>local()</code> in conjunction with <code>unfold()</code>. In this case it filters out vertices from the <code>Path</code> as they are not relevant as the concern is only with the "weight" property on the edges.</p> </li> </ol> </div> <div class="paragraph"> <p>For purposes of this example, the three traversals above happen to represent three pieces of data that are required by an application. It is plain to note that all of the above traversals hold a similar pattern that starts with "getting 'marko'" and, in the case of the latter two, traversing on outgoing edges away from him and collecting data from that path. Ideally, all three of these traversals should execute as one to prevent having to submit three separate traversals, thus incurring additional query execution costs for what amounts to be largely the same underlying data but with different transformations applied. The goal here would be to return the results of this data as a <code>List</code> with three results (i.e. triple) that could then be submitted once by the application. The following example demonstrates the use of <code>aggregate()</code> to aid in construction of this <code>List</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-15" type="radio" name="radio-set-1729796726-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-16" type="radio" name="radio-set-1729796726-15" class="tab-selector-2" /> <label for="tab-1729796726-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). repeat(outE().as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).inV().as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> by(select(all,<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(5)</b> by(select(all,<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).mean()). fold(). <span class="comment">//</span>// <b class="conum">(6)</b> aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(7)</b> by(cap(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(8)</b> by(cap(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>)). cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) ==&gt;[<span class="integer">29</span>,[[marko,lop],[marko,josh,ripple],[marko,josh,lop]],[<span class="float">0.4</span>,<span class="float">1.0</span>,<span class="float">0.7</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). repeat(outE().as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).inV().as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> until(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>)). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> by(select(all,<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(5)</b> by(select(all,<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).mean()). fold(). <span class="comment">//</span>// <b class="conum">(6)</b> aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(7)</b> by(cap(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(8)</b> by(cap(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>)). cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Get the "marko" vertex and label that step as "v".</p> </li> <li> <p>Store the first "age" of "marko" as the first item in the <code>List</code> called "a", which will ultimately be the result.</p> </li> <li> <p>Execute the traversal away from "marko" and continue to traverse on outgoing edges until the vertex has the value of "java" for the "lang" property. Note the labels of "e" and "v". Note that "e" will contain a <code>List</code> of all of the edges that have been traversed and "v" will contain a <code>List</code> of all the vertices that have been traversed.</p> </li> <li> <p>The incoming traverser to <code>aggregate('b')</code> are vertices that terminate the <code>repeat()</code> (i.e. those with the "lang" of "java"). Note however that the <code>by()</code> modulator overrides that traverser completely by starting a fresh stream of the list of vertices in "v". Those vertices are unfolded to retrieve the name property from each and then are reduced with <code>fold()</code> back into a list to be stored in the side-effected named "b".</p> </li> <li> <p>A similar use of <code>aggregate()</code> as the previous step, though this one turns "e" into a stream of edges to calculate the <code>mean()</code> to store in a <code>List</code> called "c". Note that <code>aggregate()</code> (short form for <code>aggregate(global)</code>) was used here instead of <code>aggregate(local)</code>, as the former is an eager collection of the elements in the stream (<code>aggregate(local)</code> is lazy) and will force the traversal to be iterated up to that point before moving forward. Without that eager collection, "v" and "e" would not contain the complete information required for the production of "b" and "c".</p> </li> <li> <p>Adding <code>fold()</code>-step here is a bit of a trick. To see the trick, copy and paste all lines of Gremlin up to but not including this <code>fold()</code>-step and run them against the "modern" graph. The output is three vertices and if the <code>profile()</code>-step was added one would also see that the traversal contained three traversers. These three traversers with a vertex in each one were produced from the <code>repeat()</code>-step (i.e. those vertices that had the "lang" of "java" when traversing away from "marko"). The <code>aggregate()</code>-steps are side-effects and just allow the traversers to pass through them unchanged. The <code>fold()</code> obviously converts those three traversers to a single <code>List</code> to make one traverser with a <code>List</code> inside. That means that the remaining steps following the <code>fold()</code> will only be executed one time each instead of three, which, as will be shown, is critical to the proper result.</p> </li> <li> <p>The single traverser with the <code>List</code> of three vertices in it passes to <code>aggregate(local)</code>. The <code>by()</code> modulator presents an override telling Gremlin to ignore the <code>List</code> of three vertices and simply grab the "b" side effect created earlier and stick that into "a" as part of the result. The <code>List</code> with three vertices passes out unchanged as <code>aggregate(local)</code> is a side-effect step.</p> </li> <li> <p>Again, the single traverser with the <code>List</code> of three vertices passes to <code>aggregate(local)</code> and again, the <code>by()</code> modulator presents an override to include "c" into the result.</p> </li> </ol> </div> <div class="paragraph"> <p>All of the above code and explanation show that <code>aggregate()</code> can be used to construct <code>List</code> objects as side-effects which can then be used as a result. Note that <code>aggregate()</code> can take a <code>Scope</code>, should it make sense that lazy <code>List</code> creation is not acceptable with respect to the nature of the traversal. An interesting sub-pattern that emerges here is that the <code>by()</code>-step can modulate its step to completely override the current traverser and ignore its contents for purpose of that step. This ability to override a traverser acts as a powerful and flexible tool as it means that each traverser can effectively become a completely different object as determined by a sub-traversal.</p> </div> <div class="paragraph"> <p>Another interesting method for <code>List</code> creation was demonstrated a bit earlier but not examined in detail - the use of <code>union()</code>. It was shown earlier in the following context where it helped create a <code>List</code> of two lists of three vertices each:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-17" type="radio" name="radio-set-1729796726-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-18" type="radio" name="radio-set-1729796726-17" class="tab-selector-2" /> <label for="tab-1729796726-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold()) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>]] ==&gt;[v[<span class="integer">4</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().union(limit(<span class="integer">3</span>).fold(),tail(<span class="integer">3</span>).fold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>By folding the results of <code>union()</code>, it becomes possible to essentially construct lists with arbitrary traversal results.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-19" type="radio" name="radio-set-1729796726-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-20" type="radio" name="radio-set-1729796726-19" class="tab-selector-2" /> <label for="tab-1729796726-20" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). local(union(identity(), <span class="comment">//</span>// <b class="conum">(1)</b> bothE().count()). fold()) ==&gt;[v[<span class="integer">1</span>],<span class="integer">3</span>] ==&gt;[v[<span class="integer">2</span>],<span class="integer">1</span>] ==&gt;[v[<span class="integer">3</span>],<span class="integer">3</span>] ==&gt;[v[<span class="integer">4</span>],<span class="integer">3</span>] ==&gt;[v[<span class="integer">5</span>],<span class="integer">1</span>] ==&gt;[v[<span class="integer">6</span>],<span class="integer">1</span>] gremlin&gt; g.V(). aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). by(union(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).count(local), <span class="comment">//</span>// <b class="conum">(2)</b> identity(), bothE().count()). fold()). cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==&gt;[[<span class="integer">0</span>,v[<span class="integer">1</span>],<span class="integer">3</span>],[<span class="integer">1</span>,v[<span class="integer">2</span>],<span class="integer">1</span>],[<span class="integer">2</span>,v[<span class="integer">3</span>],<span class="integer">3</span>],[<span class="integer">3</span>,v[<span class="integer">4</span>],<span class="integer">3</span>],[<span class="integer">4</span>,v[<span class="integer">5</span>],<span class="integer">1</span>],[<span class="integer">5</span>,v[<span class="integer">6</span>],<span class="integer">1</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). local(union(identity(), <span class="comment">//</span>// <b class="conum">(1)</b> bothE().count()). fold()) g.V(). aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). by(union(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).count(local), <span class="comment">//</span>// <b class="conum">(2)</b> identity(), bothE().count()). fold()). cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>For each vertex, create a "pair" (i.e. a <code>List</code> of two objects) of the vertex itself and its edge count.</p> </li> <li> <p>For each vertex, create a "triple" (i.e. a <code>List</code> of three objects) of the index of the vertex (starting at zero), the vertex itself and its edge count.</p> </li> </ol> </div> <div class="paragraph"> <p>The pattern here is to use <code>union()</code> in conjunction with <code>fold()</code>. As explained earlier, the <code>fold()</code> operation reduces the stream from <code>union()</code> to a single <code>List</code> that is then fed forward to the next step in the traversal.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> TinkerPop 3.4.4 introduced <code>elementMap()</code>-step which essentially replaces much of the transformations described below that are applied to the results of <code>valueMap()</code>-step. While the direct use of <code>elementMap()</code> eliminates the need for the transformation, the explanations below are still edifying and worth examining. </td> </tr> </table> </div> <div class="paragraph"> <p>Now that <code>List</code> patterns have been explained, there can now be some attention on <code>Map</code>. One of the most common ways to end up with a <code>Map</code> is with <code>valueMap()</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-21" type="radio" name="radio-set-1729796726-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-22" type="radio" name="radio-set-1729796726-21" class="tab-selector-2" /> <label for="tab-1729796726-22" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==&gt;[<span class="key">name</span>:[marko],<span class="key">age</span>:[<span class="integer">29</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The problem is that unless the graph is making use of multi-properties, there is little need to have the value of each property stored as a <code>List</code>. One way to unwrap this value from the list is to avoid having it there in the first place by avoiding use of <code>valueMap()</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-23" type="radio" name="radio-set-1729796726-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-24" type="radio" name="radio-set-1729796726-23" class="tab-selector-2" /> <label for="tab-1729796726-24" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). local(properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). group().by(key()).by(value())) ==&gt;[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). local(properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). group().by(key()).by(value()))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Interestingly, it&#8217;s worth looking at how to process the output of <code>valueMap()</code> to attain this output as the approach is generally applicable to processing any <code>Map</code> instances with any sorts of values:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-25" type="radio" name="radio-set-1729796726-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-26" type="radio" name="radio-set-1729796726-25" class="tab-selector-2" /> <label for="tab-1729796726-26" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). unfold(). group(). by(keys). by(select(values).unfold()) ==&gt;[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). unfold(). group(). by(keys). by(select(values).unfold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The code above, basically deconstructs then reconstructs the <code>Map</code>. The key to the pattern is to first <code>unfold()</code> the <code>Map</code> into its key and value entries. Then for each key and value produce a new <code>Map</code> using <code>group()</code> where the key for that map is the key of the entry (those are obviously unique as you picked them out of the <code>valueMap()</code>) and the value is simply the <code>unfold()</code> of the list of values in each entry. Recall that the <code>select(values).unfold()</code> only returns one value (i.e. the first) not only because there is only one, but also because <code>by()</code> will only call <code>next()</code> on that sub-traversal (it does not iterate the entire thing).</p> </div> <div class="paragraph"> <p>Generally speaking, a <code>Map</code> constructed as part of <code>group()</code> or <code>project()</code> will already be in the form required as the <code>by()</code> modulators would be written in such a fashion as to produce that final output. It would be unnecessary to deconstruct/reconstruct it. Be certain that there isn&#8217;t a way to re-write the <code>group()</code> or <code>project()</code> to get the desired output before taking this approach.</p> </div> <div class="paragraph"> <p>In the following case, <code>project()</code> is used to create a <code>Map</code> that does not meet this requirement as it contains some unavoidable extraneous keys in the output <code>Map</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-27" type="radio" name="radio-set-1729796726-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-28" type="radio" name="radio-set-1729796726-27" class="tab-selector-2" /> <label for="tab-1729796726-28" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))) ==&gt;[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>,<span class="key">lang</span>:n/a] ==&gt;[<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>,<span class="key">lang</span>:n/a] ==&gt;[<span class="key">name</span>:lop,<span class="key">age</span>:n/a,<span class="key">lang</span>:java] ==&gt;[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>,<span class="key">lang</span>:n/a] ==&gt;[<span class="key">name</span>:ripple,<span class="key">age</span>:n/a,<span class="key">lang</span>:java] ==&gt;[<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>,<span class="key">lang</span>:n/a]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>)))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The use of <code>coalesce()</code> works around the problem where "age" and "lang" are not necessarily property keys present on every single vertex in the traversal stream. When the "age" or "lang" are not present, the constant of "n/a" is supplied. While this may be an acceptable output, it is possible to shape the <code>Map</code> to be "nicer":</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-29" type="radio" name="radio-set-1729796726-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-30" type="radio" name="radio-set-1729796726-29" class="tab-selector-2" /> <label for="tab-1729796726-30" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). local(unfold(). filter(select(values).is(P.neq(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). group(). by(keys). by(values)) ==&gt;[<span class="key">name</span>:[marko],<span class="key">age</span>:[<span class="integer">29</span>]] ==&gt;[<span class="key">name</span>:[vadas],<span class="key">age</span>:[<span class="integer">27</span>]] ==&gt;[<span class="key">name</span>:[lop],<span class="key">lang</span>:[java]] ==&gt;[<span class="key">name</span>:[josh],<span class="key">age</span>:[<span class="integer">32</span>]] ==&gt;[<span class="key">name</span>:[ripple],<span class="key">lang</span>:[java]] ==&gt;[<span class="key">name</span>:[peter],<span class="key">age</span>:[<span class="integer">35</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). local(unfold(). filter(select(values).is(P.neq(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). group(). by(keys). by(values))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The additional steps above <code>unfold()</code> the <code>Map</code> to key-value entries and filter the values for "n/a" and remove them prior to reconstructing the <code>Map</code> with the method shown earlier. To go a step further, apply the pattern presented earlier to flatten <code>List</code> values within a <code>Map</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-31" type="radio" name="radio-set-1729796726-31" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-31" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-32" type="radio" name="radio-set-1729796726-31" class="tab-selector-2" /> <label for="tab-1729796726-32" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). local(unfold(). filter(select(values).is(P.neq(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). group(). by(keys). by(select(values).unfold())) ==&gt;[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==&gt;[<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==&gt;[<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==&gt;[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==&gt;[<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==&gt;[<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). project(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>),constant(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). local(unfold(). filter(select(values).is(P.neq(<span class="string"><span class="delimiter">'</span><span class="content">n/a</span><span class="delimiter">'</span></span>))). group(). by(keys). by(select(values).unfold()))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>As there may be a desire to remove entries from a <code>Map</code>, there may also be the need to add keys to a <code>Map</code>. The pattern here involves the use of a <code>union()</code> that returns the <code>Map</code> instances which can be flattened to entries and then reconstructed as a new <code>Map</code> that has been merged together:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-33" type="radio" name="radio-set-1729796726-33" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-33" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-34" type="radio" name="radio-set-1729796726-33" class="tab-selector-2" /> <label for="tab-1729796726-34" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). union(project(<span class="string"><span class="delimiter">'</span><span class="content">degree</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> by(bothE().count()), valueMap().with(WithOptions.tokens)). unfold(). <span class="comment">//</span>// <b class="conum">(2)</b> group(). by(keys). by(select(values).unfold()) ==&gt;[<span class="key">degree</span>:<span class="integer">3</span>,<span class="key">name</span>:marko,<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">29</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). union(project(<span class="string"><span class="delimiter">'</span><span class="content">degree</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> by(bothE().count()), valueMap().with(WithOptions.tokens)). unfold(). <span class="comment">//</span>// <b class="conum">(2)</b> group(). by(keys). by(select(values).unfold())</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The <code>valueMap().with(WithOptions.tokens)</code> of a <code>Vertex</code> can be extended with the "degree" of the <code>Vertex</code> by performing a <code>union()</code> of the two traversals that produce that output (both produce <code>Map</code> objects). Recall that <code>valueMap().with(WithOptions.tokens)</code> is replaced by <code>elementMap()</code> in 3.4.4 - this examples continues with the "old" form for demonstration purposes.</p> </li> <li> <p>The <code>unfold()</code>-step is used to decompose the <code>Map</code> objects into key/value entries that are then constructed back into a single new <code>Map</code> given the patterns shown earlier. The <code>Map</code> objects of both traversals in the <code>union()</code> are essentially merged together.</p> </li> </ol> </div> <div class="paragraph"> <p>When using this pattern, it is important to recognize that if there are non-unique keys produced by the traversals supplied to <code>union()</code>, they will overwrite one another given the final <code>by()</code> modulator above. If changed to <code>by(select(values).unfold().fold())</code> they will merge to produce a <code>List</code> of values. Of course, that change will bring a <code>List</code> back for all the values of the new <code>Map</code>. With some added logic the <code>Map</code> values can be flattened out of <code>List</code> instances when necessary:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796726-35" type="radio" name="radio-set-1729796726-35" class="tab-selector-1" checked="checked" /> <label for="tab-1729796726-35" class="tab-label-1">console (groovy)</label> <input id="tab-1729796726-36" type="radio" name="radio-set-1729796726-35" class="tab-selector-2" /> <label for="tab-1729796726-36" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(). has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). union(valueMap().with(WithOptions.tokens), project(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(constant(<span class="integer">100</span>))). unfold(). group(). by(keys). by(select(values). unfold(). fold(). choose(count(local).is(eq(<span class="integer">1</span>)), unfold())) ==&gt;[<span class="key">name</span>:marko,<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">age</span>:[<span class="integer">29</span>,<span class="integer">100</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(). has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). union(valueMap().with(WithOptions.tokens), project(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(constant(<span class="integer">100</span>))). unfold(). group(). by(keys). by(select(values). unfold(). fold(). choose(count(local).is(eq(<span class="integer">1</span>)), unfold()))</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="connected-components">Connected Components</h2> <div class="sectionbody"> <div class="paragraph"> <p>Gremlin can be used to find <a href="https://en.wikipedia.org/wiki/Connected_component_(graph_theory)">connected components</a> in a graph. In a directed graph like in TinkerPop, components can be weakly or strongly connected. This recipe is restricted to finding <a href="https://en.wikipedia.org/wiki/Directed_graph#Directed_graph_connectivity">weakly connected components</a>, in which the direction of edges is not taken into account.</p> </div> <div class="paragraph"> <p>Depending on the size of the graph, three solution regimes can be discriminated:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Small graphs that fit in the memory of a single machine</p> </li> <li> <p>Medium-sized graphs backed by storage for which an OLTP linear scan is still feasible. This regime is left to third party TinkerPop implementations, since TinkerPop itself has no storage-backed reference implementations. The idea is that component membership is stored in the graph, rather than in memory.</p> </li> <li> <p>Large graphs requiring an approach with <code>HadoopGraph</code> and <code>SparkGraphComputer</code> to yield results in a reasonable time.</p> </li> </ol> </div> <div class="paragraph"> <p>These regimes are discussed separately using the following graph with three weakly connected components:</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/connected-components.png" alt="connected components" width="600"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796742-1" type="radio" name="radio-set-1729796742-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796742-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796742-2" type="radio" name="radio-set-1729796742-1" class="tab-selector-2" /> <label for="tab-1729796742-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">B</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">D</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">E</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">F</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">B</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">D</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">E</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">F</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> <div class="sect2"> <h3 id="_small_graph_traversals">Small graph traversals</h3> <div class="paragraph"> <p>Connected components in a small graph can be determined with either an OLTP traversal or the OLAP <code>connectedComponent()</code>-step. The <code>connectedComponent()</code>-step is available as of TinkerPop 3.4.0 and is described in more detail in the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#connectedcomponent-step">Reference Documentation</a>. The traversal looks like:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796742-3" type="radio" name="radio-set-1729796742-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796742-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796742-4" type="radio" name="radio-set-1729796742-3" class="tab-selector-2" /> <label for="tab-1729796742-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withComputer().V().connectedComponent(). group().by(ConnectedComponent.component). select(values).unfold() ==&gt;[v[E],v[D]] ==&gt;[v[C],v[B],v[A]] ==&gt;[v[F]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withComputer().V().connectedComponent(). group().by(ConnectedComponent.component). select(values).unfold()</code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>component</code> option passed to <code>by()</code> is statically imported from <code>ConnectedComponent</code> and refers to the default property key within which the result of the algorithm is stored. </td> </tr> </table> </div> <div class="paragraph"> <p>A straightforward way to detect the various subgraphs with an OLTP traversal is to do this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796742-5" type="radio" name="radio-set-1729796742-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796742-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796742-6" type="radio" name="radio-set-1729796742-5" class="tab-selector-2" /> <label for="tab-1729796742-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().emit(cyclicPath().or().not(both())). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(__.where(without(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both()).until(cyclicPath()). <span class="comment">//</span>// <b class="conum">(2)</b> group().by(path().unfold().limit(<span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(path().unfold().dedup().fold()). <span class="comment">//</span>// <b class="conum">(4)</b> select(values).unfold() <span class="comment">//</span>// <b class="conum">(5)</b> ==&gt;[v[A],v[B],v[C]] ==&gt;[v[D],v[E]] ==&gt;[v[F]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().emit(cyclicPath().or().not(both())). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(__.where(without(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both()).until(cyclicPath()). <span class="comment">//</span>// <b class="conum">(2)</b> group().by(path().unfold().limit(<span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(3)</b> by(path().unfold().dedup().fold()). <span class="comment">//</span>// <b class="conum">(4)</b> select(values).unfold() <span class="invisible">//</span><b class="conum">5</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The initial emit() step allows for output of isolated vertices, in addition to the discovery of components as described in (2).</p> </li> <li> <p>The entire component to which the first returned vertex belongs, is visited. To allow for components of any structure, a repeat loop is applied that only stops for a particular branch of the component when it detects a cyclic path. Collection <code>'a'</code> is used to keep track of visited vertices, for both subtraversals within a component and new traversals resulting from the <code>g.V()</code> linear scan.</p> </li> <li> <p>While <code>'a'</code> nicely keeps track of vertices already visited, the actual components need to be extracted from the path information. The <code>path().unfold().limit(1)</code> closure provides the starting vertex of surviving traversers, which can be used to group the components.</p> </li> <li> <p>This clause collects the unique vertices from all paths with the same starting vertex, thus from the same weak component.</p> </li> <li> <p>The values of the groupby map contain the lists of vertices making up the requested components.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="_small_graph_scalability">Small graph scalability</h3> <div class="paragraph"> <p>The scalability of the OLTP traversal and the <code>connectedComponent()</code>-step for in-memory graphs is shown in the figures below.</p> </div> <div id="cc-scale-size" class="imageblock"> <div class="content"> <img src="../images/cc-scale-size.png" alt="cc scale size" width="600"> </div> <div class="title">Figure 1. Run times for finding connected components in a randomly generated graph with 10 components of equal size and with an edge/vertex ratio of 6</div> </div> <div class="paragraph"> <p>In general, the <code>connectedComponent()</code>-step is almost a factor two faster than the OLTP traversal. Only, for very small graphs the overhead of running the ConnectedComponentVertexProgram is larger than that of the OLTP traversal. The vertex program works by having interconnected vertices exchange id&#8217;s and store the lowest id until no vertex receives a lower id. This algorithm is commonly applied in <a href="https://en.wikipedia.org/wiki/Bulk_synchronous_parallel">bulk synchronous parallel</a> systems, e.g. in <a href="https://spark.apache.org/graphx">Apache Spark GraphX</a>. Overhead for the vertex program arises because it has to run as many cycles as the largest length of the shortest paths between any two vertices in a component of the graph. In every cycle each vertex has to be checked for being "halted". Overhead of the OLTP traversal consists of each traverser having to carry complete path information. For pure depth-first-search or breadth-first-search implementations, connected-component algotithms should scale as <span class="big">O</span>(V+E). For the traversals in the figure above this is almost the case.</p> </div> <div id="cc-scale-ratio" class="imageblock"> <div class="content"> <img src="../images/cc-scale-ratio.png" alt="cc scale ratio" width="600"> </div> <div class="title">Figure 2. Run times for finding connected components in a randomly generated graph with 10 components, each consisting of 6400 vertices</div> </div> <div class="paragraph"> <p>The random graphs used for the scalability tests can be modulated with the edge/vertex ratio. For small ratios the components generated are more lint-like and harder to process by the <code>connectedComponent()</code>-step. For high ratios the components are more mesh-like and the ConnectedComponentVertexProgram needs few cycles to process the graph. These characteristics show clearly from the graph. Indeed, for a given number of vertices, the run time of the <code>connectedComponent()</code>-step does not depend on the number of edges, but rather on the maximum shortest path length in the graph.</p> </div> </div> <div class="sect2"> <h3 id="_large_graphs">Large graphs</h3> <div class="paragraph"> <p>Large graphs in TinkerPop require distributed processing by <code>SparkGraphComputer</code> to get results in a reasonable time (OLAP approach). This means that the graph must be available as <code>HadoopGraph</code> (third party TinkerPop implementations often allow to make a graph available as an <code>HadoopGraph</code> by providing an Hadoop <code>InputFormat</code>). Running the <code>connectedComponent()</code>-step on an <code>HadoopGraph</code> works the same as for a small graph, provided that <code>SparkGraphComputer</code> is specified as the graph computer, either with the <code>gremlin.hadoop.defaultGraphComputer</code> property or as part of the <code>withComputer()</code>-step.</p> </div> <div class="paragraph"> <p>Scalability of the the <code>connectedComponent()</code>-step with <code>SparkGraphComputer</code> is high, but note that:</p> </div> <div class="ulist"> <ul> <li> <p>The graph should fit in the memory of the Spark cluster to allow the VertexProgram to run its cycles without spilling intermediate results to disk and loosing most of the gains from the distributed processing.</p> </li> <li> <p>As discussed for small graphs, the BSP algorithm does not play well with graphs having a large shortest path between any pair of vertices. Overcoming this limitation is still a <a href="http://www.vldb.org/pvldb/vol7/p1821-yan.pdf">subject of academic research</a>.</p> </li> </ul> </div> </div> </div> </div> <div class="sect1"> <h2 id="cycle-detection">Cycle Detection</h2> <div class="sectionbody"> <div class="paragraph"> <p>A cycle occurs in a graph where a path loops back on itself to the originating vertex. For example, in the graph depicted below Gremlin could be use to detect the cycle among vertices <code>A-B-C</code>.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/graph-cycle.png" alt="graph cycle" width="250"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796752-1" type="radio" name="radio-set-1729796752-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796752-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796752-2" type="radio" name="radio-set-1729796752-1" class="tab-selector-2" /> <label for="tab-1729796752-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(out().simplePath()).times(<span class="integer">2</span>). where(out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[v[a],v[b],v[c]] ==&gt;[v[b],v[c],v[a]] ==&gt;[v[c],v[a],v[b]] gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(out().simplePath()).times(<span class="integer">2</span>). where(out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path(). dedup().by(unfold().order().by(id).dedup().fold()) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;[v[a],v[b],v[c]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).iterate() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(out().simplePath()).times(<span class="integer">2</span>). where(out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(out().simplePath()).times(<span class="integer">2</span>). where(out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path(). dedup().by(unfold().order().by(id).dedup().fold()) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Gremlin starts its traversal from a vertex labeled "a" and traverses <code>out()</code> from each vertex filtering on the <code>simplePath</code>, which removes paths with repeated objects. The steps going <code>out()</code> are repeated twice as in this case the length of the cycle is known to be three and there is no need to exceed that. The traversal filters with a <code>where()</code> to see only return paths that end with where it started at "a".</p> </li> <li> <p>The previous query returned the <code>A-B-C</code> cycle, but it returned three paths which were all technically the same cycle. It returned three, because there was one for each vertex that started the cycle (i.e. one for <code>A</code>, one for <code>B</code> and one for <code>C</code>). This next line introduce deduplication to only return unique cycles.</p> </li> </ol> </div> <div class="paragraph"> <p>The above case assumed that the need was to only detect cycles over a path length of three. It also respected the directionality of the edges by only considering outgoing ones.</p> </div> <div class="paragraph"> <p>Also note that the traversal above won&#8217;t detect self-loops (vertices directly connected to themselves). To do so, you would need to <code>.emit()</code> a Traverser before the repeat()-loop.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796752-3" type="radio" name="radio-set-1729796752-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796752-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796752-4" type="radio" name="radio-set-1729796752-3" class="tab-selector-2" /> <label for="tab-1729796752-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). emit(). repeat(outE().inV().simplePath()). times(<span class="integer">2</span>). outE().inV().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)). path(). by(id). by(label) ==&gt;[a,self,a] ==&gt;[a,knows,b,knows,c,knows,a] ==&gt;[b,knows,c,knows,a,knows,b] ==&gt;[c,knows,a,knows,b,knows,c]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).iterate() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). emit(). repeat(outE().inV().simplePath()). times(<span class="integer">2</span>). outE().inV().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)). path(). by(id). by(label)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>What would need to change to detect cycles of arbitrary length over both incoming and outgoing edges, in the modern graph?</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796752-5" type="radio" name="radio-set-1729796752-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796752-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796752-6" type="radio" name="radio-set-1729796752-5" class="tab-selector-2" /> <label for="tab-1729796752-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).emit(loops().is(gt(<span class="integer">1</span>))). both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path(). dedup().by(unfold().order().by(id).dedup().fold()) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).emit(loops().is(gt(<span class="integer">1</span>))). both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).path(). dedup().by(unfold().order().by(id).dedup().fold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>An interesting type of cycle is known as the Eulerian circuit which is a path taken in a graph where each edge is visited once and the path starts and ends with the same vertex. Consider the following graph, representative of an imaginary but geographically similar <a href="https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg">Königsberg</a> that happens to have an eighth bridge (the diagram depicts edge direction but direction won&#8217;t be considered in the traversal):</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/eulerian-circuit.png" alt="eulerian circuit" width="500"></span></p> </div> <div class="paragraph"> <p>Gremlin can detect if such a cycle exists with:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796752-7" type="radio" name="radio-set-1729796752-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796752-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796752-8" type="radio" name="radio-set-1729796752-7" class="tab-selector-2" /> <label for="tab-1729796752-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">blue</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">orange</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">red</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">green</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().sideEffect(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridge</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>)).barrier(). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(bothE(). <span class="comment">//</span>// <b class="conum">(2)</b> or(__.not(select(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)), __.not(filter(__.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold(). <span class="comment">//</span>// <b class="conum">(3)</b> where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))))).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). otherV()). until(select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).count(local).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>))).hasNext() ==&gt;<span class="predefined-constant">true</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">blue</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">orange</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">red</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">green</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">bridge</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">o</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>).iterate() g.V().sideEffect(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridge</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>)).barrier(). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(bothE(). <span class="comment">//</span>// <b class="conum">(2)</b> or(__.not(select(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)), __.not(filter(__.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold(). <span class="comment">//</span>// <b class="conum">(3)</b> where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))))).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). otherV()). until(select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).count(local).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>))).hasNext()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Gather all the edges in a "bridges" side effect.</p> </li> <li> <p>As mentioned earlier with the diagram, directionality is ignored as the traversal uses <code>bothE</code> and, later, <code>otherV</code>.</p> </li> <li> <p>In continually traversing over both incoming and outgoing edges, this path is only worth continuing if the edges traversed thus far are only traversed once. That set of edges is maintained in "e".</p> </li> <li> <p>The traversal should repeat until the number of edges traversed in "e" is equal to the total number gathered in the first step above, which would mean that the complete circuit has been made.</p> </li> </ol> </div> <div class="paragraph"> <p>Unlike Königsberg, with just seven bridges, a Eulerian circuit exists in the case with an eighth bridge. The first detected circuit can be displayed with:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796752-9" type="radio" name="radio-set-1729796752-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796752-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796752-10" type="radio" name="radio-set-1729796752-9" class="tab-selector-2" /> <label for="tab-1729796752-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().sideEffect(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridge</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>)).barrier(). repeat(bothE().or(__.not(select(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)), __.not(filter(__.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold(). where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))))).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).otherV()). until(select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).count(local).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>))).limit(<span class="integer">1</span>). path().by(id).by(constant(<span class="string"><span class="delimiter">&quot;</span><span class="content"> -&gt; </span><span class="delimiter">&quot;</span></span>)). map {<span class="predefined-type">String</span>.join(<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>, <span class="local-variable">it</span>.get().objects())} ==&gt;orange -&gt; blue -&gt; green -&gt; orange -&gt; red -&gt; green -&gt; red -&gt; orange -&gt; blue</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().sideEffect(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridge</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>)).barrier(). repeat(bothE().or(__.not(select(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)), __.not(filter(__.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).unfold(). where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))))).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).otherV()). until(select(all, <span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).count(local).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">bridges</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>))).limit(<span class="integer">1</span>). path().by(id).by(constant(<span class="string"><span class="delimiter">&quot;</span><span class="content"> -&gt; </span><span class="delimiter">&quot;</span></span>)). map {<span class="predefined-type">String</span>.join(<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>, <span class="local-variable">it</span>.get().objects())}</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="duplicate-edge">Duplicate Edge Detection</h2> <div class="sectionbody"> <div class="paragraph"> <p>Whether part of a graph maintenance process or for some other analysis need, it is sometimes necessary to detect if there is more than one edge between two vertices. The following examples will assume that an edge with the same label and direction will be considered "duplicate".</p> </div> <div class="paragraph"> <p>The "modern" graph does not have any duplicate edges that fit that definition, so the following example adds one that is duplicative of the "created" edge between vertex "1" and "3".</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-1" type="radio" name="radio-set-1729796763-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-2" type="radio" name="radio-set-1729796763-1" class="tab-selector-2" /> <label for="tab-1729796763-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>) ==&gt;e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>One way to find the duplicate edges would be to do something like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-3" type="radio" name="radio-set-1729796763-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-4" type="radio" name="radio-set-1729796763-3" class="tab-selector-2" /> <label for="tab-1729796763-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().outE(). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> by().by(inV().path().by().by(label)). group(). <span class="comment">//</span>// <b class="conum">(2)</b> by(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).fold()). unfold(). <span class="comment">//</span>// <b class="conum">(3)</b> select(values). <span class="comment">//</span>// <b class="conum">(4)</b> where(count(local).is(gt(<span class="integer">1</span>))) ==&gt;[e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().outE(). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> by().by(inV().path().by().by(label)). group(). <span class="comment">//</span>// <b class="conum">(2)</b> by(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).fold()). unfold(). <span class="comment">//</span>// <b class="conum">(3)</b> select(values). <span class="comment">//</span>// <b class="conum">(4)</b> where(count(local).is(gt(<span class="integer">1</span>)))</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "a" and "b" from the <code>project</code> contain the edge and the path respectively. The path consists of a the outgoing vertex, an edge, and the incoming vertex. The use of <code>by().by(label))</code> converts the edge to its label (recall that <code>by</code> are applied in round-robin fashion), so the path will look something like: <code>[v[1],created,v[3]]</code>.</p> </li> <li> <p>Group by the path from "b" and construct a list of edges from "a". Any value in this <code>Map</code> that has a list of edges greater than one means that there is more than one edge for that edge label between those two vertices (i.e. the <code>Map</code> key).</p> </li> <li> <p>Unroll the key-value pairs in the <code>Map</code> of paths-edges.</p> </li> <li> <p>Only the values from the <code>Map</code> are needed and as mentioned earlier, those lists with more than one edge would contain duplicate.</p> </li> </ol> </div> <div class="paragraph"> <p>This method find the duplicates, but does require more memory than other approaches. A slightly more complex approach that uses less memory might look like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-5" type="radio" name="radio-set-1729796763-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-6" type="radio" name="radio-set-1729796763-5" class="tab-selector-2" /> <label for="tab-1729796763-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). <span class="comment">//</span>// <b class="conum">(1)</b> where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).by(label). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).by().by(label)). <span class="comment">//</span>// <b class="conum">(3)</b> unfold(). <span class="comment">//</span>// <b class="conum">(4)</b> select(values). where(count(local).is(gt(<span class="integer">1</span>))) ==&gt;[e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>],e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). <span class="comment">//</span>// <b class="conum">(1)</b> where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).by(label). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).by().by(label)). <span class="comment">//</span>// <b class="conum">(3)</b> unfold(). <span class="comment">//</span>// <b class="conum">(4)</b> select(values). where(count(local).is(gt(<span class="integer">1</span>)))</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>To this point in the traversal, the outgoing edges of a vertex are being iterated with the current edge labeled as "e". For "e", Gremlin traverses to the incoming vertex and back on in edges of that vertex.</p> </li> <li> <p>Those incoming edges are filtered with the following <code>where</code> steps. The first ensures that it does not traverse back over "e" (i.e. the current edge). The second determines if the edge label is equivalent (i.e. the test for the working definition of "duplicate"). The third determines if the outgoing vertex matches the one that started the path labeled as "ov".</p> </li> <li> <p>This line is quite similar to the output achieved in the previous example at step 2. A <code>Map</code> is produced that uses the outgoing vertex, the edge label, and the incoming vertex as the key, with the list of edges for that path as the value.</p> </li> <li> <p>The rest of the traversal is the same as the previous one.</p> </li> </ol> </div> <div class="paragraph"> <p>Note that the above traversal could also be written using <code>match</code> step:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-7" type="radio" name="radio-set-1729796763-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-8" type="radio" name="radio-set-1729796763-7" class="tab-selector-2" /> <label for="tab-1729796763-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().match( __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>).outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).inE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>).outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>,neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>,eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).by(label). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).by().by(label)). unfold().select(values). where(count(local).is(gt(<span class="integer">1</span>))) ==&gt;[e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>],e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().match( __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>).outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).inE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>), __.as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>).outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>,neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>,eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).by(label). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ie</span><span class="delimiter">&quot;</span></span>). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>).by().by(label)). unfold().select(values). where(count(local).is(gt(<span class="integer">1</span>)))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>A third way to approach this problem would be to force a <a href="https://en.wikipedia.org/wiki/Depth-first_search">depth-first search</a>. The previous examples invoke traversal strategies that force a <a href="https://en.wikipedia.org/wiki/Breadth-first_search">breadth first search</a> as a performance optimization.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-9" type="radio" name="radio-set-1729796763-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-10" type="radio" name="radio-set-1729796763-9" class="tab-selector-2" /> <label for="tab-1729796763-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>)). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(label) <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>)). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(label) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Remove strategies that will optimize for breadth first searches and thus allow Gremlin to go depth first.</p> </li> <li> <p>To this point, the traversal is very much like the previous one. Review step 2 in the previous example to see the parallels here.</p> </li> <li> <p>The final <code>where</code> simply looks for edges that match on label, which would then meet the working definition of "duplicate".</p> </li> </ol> </div> <div class="paragraph"> <p>The basic pattern at play here is to compare the path of the outgoing vertex, its outgoing edge label and the incoming vertex. This model can obviously be contracted or expanded as needed to fit different definitions of "duplicate". For example, a "duplicate" definition could extended to the label and properties of the edge. For purposes of demonstration, an additional edge is added to the "modern" graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-11" type="radio" name="radio-set-1729796763-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-12" type="radio" name="radio-set-1729796763-11" class="tab-selector-2" /> <label for="tab-1729796763-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.4d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.5d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>] ==&gt;[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>] ==&gt;[<span class="key">id</span>:<span class="integer">13</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.5</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.4d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.5d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>To identify the duplicate with this revised definition, the previous traversal can be modified to:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796763-13" type="radio" name="radio-set-1729796763-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796763-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796763-14" type="radio" name="radio-set-1729796763-13" class="tab-selector-2" /> <label for="tab-1729796763-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>)). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(label). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>] ==&gt;[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withoutStrategies(LazyBarrierStrategy, PathRetractionStrategy).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>). inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">iv</span><span class="delimiter">&quot;</span></span>). inE(). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>)). where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">ov</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(label). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">e1</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e2</span><span class="delimiter">&quot;</span></span>)).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>).elementMap()</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="duplicate-vertex">Duplicate Vertex Detection</h2> <div class="sectionbody"> <div class="paragraph"> <p>The pattern for finding duplicate vertices is quite similar to the pattern defined in the <a href="#duplicate-edge">Duplicate Edge</a> section. The idea is to extract the relevant features of the vertex into a comparable list that can then be used to group for duplicates.</p> </div> <div class="paragraph"> <p>Consider the following example with some duplicate vertices added to the "modern" graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796775-1" type="radio" name="radio-set-1729796775-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796775-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796775-2" type="radio" name="radio-set-1729796775-1" class="tab-selector-2" /> <label for="tab-1729796775-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">27</span>) ==&gt;v[<span class="integer">0</span>] gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">22</span>) <span class="comment">// not a duplicate because &quot;age&quot; value</span> ==&gt;v[<span class="integer">13</span>] gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>) ==&gt;v[<span class="integer">16</span>] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). group(). by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold()). unfold() ==&gt;[vadas, <span class="integer">27</span>]=[v[<span class="integer">0</span>], v[<span class="integer">2</span>]] ==&gt;[marko, <span class="integer">29</span>]=[v[<span class="integer">16</span>], v[<span class="integer">1</span>]] ==&gt;[peter, <span class="integer">35</span>]=[v[<span class="integer">6</span>]] ==&gt;[vadas, <span class="integer">22</span>]=[v[<span class="integer">13</span>]] ==&gt;[josh, <span class="integer">32</span>]=[v[<span class="integer">4</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">27</span>) g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">22</span>) <span class="comment">// not a duplicate because &quot;age&quot; value</span> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>) g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). group(). by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold()). unfold()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the above case, the "name" and "age" properties are the relevant features for identifying duplication. The key in the <code>Map</code> provided by the <code>group</code> is the list of features for comparison and the value is the list of vertices that match the feature. To extract just those vertices that contain duplicates an additional filter can be added:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796775-3" type="radio" name="radio-set-1729796775-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796775-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796775-4" type="radio" name="radio-set-1729796775-3" class="tab-selector-2" /> <label for="tab-1729796775-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). group(). by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold()). unfold(). filter(select(values).count(local).is(gt(<span class="integer">1</span>))) ==&gt;[vadas, <span class="integer">27</span>]=[v[<span class="integer">0</span>], v[<span class="integer">2</span>]] ==&gt;[marko, <span class="integer">29</span>]=[v[<span class="integer">16</span>], v[<span class="integer">1</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). group(). by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold()). unfold(). filter(select(values).count(local).is(gt(<span class="integer">1</span>)))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>That filter, extracts the values of the <code>Map</code> and counts the vertices within each list. If that list contains more than one vertex then it is a duplicate.</p> </div> </div> </div> <div class="sect1"> <h2 id="edge-move">Moving an Edge</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image"><img src="../images/gremlin-edge.png" alt="gremlin edge" width="145"></span></p> </div> <div class="paragraph"> <p>Aside from their properties, edges are immutable structures where the label and the related in and out vertices cannot be modified. To "move" an edge from one vertex to another, it requires that the edge be dropped and a new edge be created with the same properties and label. It is possible to simulate this "move" in a single traversal as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796784-1" type="radio" name="radio-set-1729796784-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796784-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796784-2" type="radio" name="radio-set-1729796784-1" class="tab-selector-2" /> <label for="tab-1729796784-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). outE().inV(). path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(elementMap()) ==&gt;[marko,[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>],lop] ==&gt;[marko,[<span class="key">id</span>:<span class="integer">7</span>,<span class="key">label</span>:knows,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.5</span>],vadas] ==&gt;[marko,[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:knows,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">1.0</span>],josh]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). outE().inV(). path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(elementMap())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The "marko" vertex contains a "knows" edge to the "vadas" vertex. The following code shows how to "move" that edge to the "peter" vertex in a single traversal:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796784-3" type="radio" name="radio-set-1729796784-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796784-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796784-4" type="radio" name="radio-set-1729796784-3" class="tab-selector-2" /> <label for="tab-1729796784-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).filter(inV().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(1)</b> V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e2</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> sideEffect(select(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).properties(). unfold().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">e2</span><span class="delimiter">'</span></span>). property(select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).key(), select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).value())). <span class="comment">//</span>// <b class="conum">(3)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).drop() <span class="comment">//</span>// <b class="conum">(4)</b> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). outE().inV(). path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(elementMap()) ==&gt;[marko,[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>],lop] ==&gt;[marko,[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:knows,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.5</span>],peter] ==&gt;[marko,[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:knows,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">1.0</span>],josh]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).filter(inV().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(1)</b> V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e2</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> sideEffect(select(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).properties(). unfold().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">e2</span><span class="delimiter">'</span></span>). property(select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).key(), select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).value())). <span class="comment">//</span>// <b class="conum">(3)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">e1</span><span class="delimiter">'</span></span>).drop() <span class="comment">//</span>// <b class="conum">(4)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). outE().inV(). path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(elementMap())</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find the edge to "move" and label that as "e1". It will be necessary to reference this later to get the edge properties to transfer to the new "moved" edge.</p> </li> <li> <p>Add the "moved" edge and label it as "e2".</p> </li> <li> <p>Use a <code>sideEffect()</code> to transfer the properties from "e1" to "e2".</p> </li> <li> <p>Use <code>drop()</code> to get rid of the old edge at "e1" now that the new "e2" edge is in place.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="element-existence">Element Existence</h2> <div class="sectionbody"> <div class="paragraph"> <p>Checking for whether or not a graph element is present in the graph is simple:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796794-1" type="radio" name="radio-set-1729796794-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796794-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796794-2" type="radio" name="radio-set-1729796794-1" class="tab-selector-2" /> <label for="tab-1729796794-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).hasNext() ==&gt;<span class="predefined-constant">true</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>).hasNext() ==&gt;<span class="predefined-constant">false</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).hasNext() g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>).hasNext()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Knowing that an element exists or not is usually a common point of decision in determining the appropriate path of code to take. In the example above, the check is for vertex existence and a typical reason to check for existence is to determine whether or not to add a new vertex or to return the one that exists (i.e. "get or create" pattern). This entire operation can occur in a single traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796794-3" type="radio" name="radio-set-1729796794-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796794-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796794-4" type="radio" name="radio-set-1729796794-3" class="tab-selector-2" /> <label for="tab-1729796794-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">29</span>)) ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">34</span>)) ==&gt;v[<span class="integer">0</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">29</span>)) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">34</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>This use of <code>coalesce()</code> shown above is the basis for this pattern. Note that at the end of <code>has()</code>-step there is either a vertex or not. By using <code>fold()</code>, "existence" or "not existence" is reduced to a <code>List</code> with the vertex or a <code>List</code> with no values. With a <code>List</code> as the traverser flowing into <code>coalesce()</code> the first child traversal to return something will execute. If the <code>List</code> has a vertex then it will <code>unfold()</code> and return the existing one. If it is empty, then the vertex does not exist and it is added and returned.</p> </div> <div class="paragraph"> <p>This "get or create" logic can be expanded to be "upsert" like functionality as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796794-5" type="radio" name="radio-set-1729796794-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796794-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796794-6" type="radio" name="radio-set-1729796794-5" class="tab-selector-2" /> <label for="tab-1729796794-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">29</span>) ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>)). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">34</span>) ==&gt;v[<span class="integer">13</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">29</span>) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>). fold(). coalesce(unfold(), addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>)). property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">34</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>By moving the <code>property()</code>-step that set the "age" value outside of <code>coalesce()</code>, the property is then set for both newly created vertices and for existing ones.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Always consider the specific nature of the graph implementation in use when considering these patterns. Some graph databases may not treat these traversals as true "upsert" operations and may do a "read before write" in their execution. </td> </tr> </table> </div> <div class="paragraph"> <p>It is possible to do similar sorts of operations with edges using the same pattern:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796794-7" type="radio" name="radio-set-1729796794-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796794-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796794-8" type="radio" name="radio-set-1729796794-7" class="tab-selector-2" /> <label for="tab-1729796794-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>). V().has(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>). coalesce(__.inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(outV().as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>)), addE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.5</span>)) ==&gt;e[<span class="integer">0</span>][<span class="integer">2</span>-created-&gt;<span class="integer">5</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>). V().has(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>). coalesce(__.inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(outV().as(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>)), addE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.5</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In this case, the adjacent vertices of the edge are retrieved first and within the <code>coalesce()</code>, the existence of the edge is checked with <code>where()</code> using a matching pattern on the "v" label and returned if found. If the edge is not found between these two vertices, then it is created as part of the second traversal given to <code>coalesce()</code>.</p> </div> </div> </div> <div class="sect1"> <h2 id="if-then-based-grouping">If-Then Based Grouping</h2> <div class="sectionbody"> <div class="paragraph"> <p>Consider the following traversal over the "modern" toy graph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796804-1" type="radio" name="radio-set-1729796804-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796804-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796804-2" type="radio" name="radio-set-1729796804-1" class="tab-selector-2" /> <label for="tab-1729796804-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==&gt;[<span class="integer">32</span>:<span class="integer">1</span>,<span class="integer">35</span>:<span class="integer">1</span>,<span class="integer">27</span>:<span class="integer">1</span>,<span class="integer">29</span>:<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The result is an age distribution that simply shows that every "person" in the graph is of a different age. In some cases, this result is exactly what is needed, but sometimes a grouping may need to be transformed to provide a different picture of the result. For example, perhaps a grouping on the value "age" would be better represented by a domain concept such as "young", "old" and "very old".</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796804-3" type="radio" name="radio-set-1729796804-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796804-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796804-4" type="radio" name="radio-set-1729796804-3" class="tab-selector-2" /> <label for="tab-1729796804-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).groupCount().by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).choose( is(lt(<span class="integer">28</span>)),constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">young</span><span class="delimiter">&quot;</span></span>), choose(is(lt(<span class="integer">30</span>)), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">old</span><span class="delimiter">&quot;</span></span>), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">very old</span><span class="delimiter">&quot;</span></span>)))) ==&gt;[<span class="key">young</span>:<span class="integer">1</span>,<span class="key">old</span>:<span class="integer">1</span>,very <span class="key">old</span>:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).groupCount().by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).choose( is(lt(<span class="integer">28</span>)),constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">young</span><span class="delimiter">&quot;</span></span>), choose(is(lt(<span class="integer">30</span>)), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">old</span><span class="delimiter">&quot;</span></span>), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">very old</span><span class="delimiter">&quot;</span></span>))))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note that the <code>by</code> modulator has been altered from simply taking a string key of "age" to take a <code>Traversal</code>. That inner <code>Traversal</code> utilizes <code>choose</code> which is like an <code>if-then-else</code> clause. The <code>choose</code> is nested and would look like the following in Java:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (age &lt; <span class="integer">28</span>) { <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">young</span><span class="delimiter">&quot;</span></span>; } <span class="keyword">else</span> { <span class="keyword">if</span> (age &lt; <span class="integer">30</span>) { <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">old</span><span class="delimiter">&quot;</span></span>; } <span class="keyword">else</span> { <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">very old</span><span class="delimiter">&quot;</span></span>; } }</code></pre> </div> </div> <div class="paragraph"> <p>The use of <code>choose</code> is a good intuitive choice for this <code>Traversal</code> as it is a natural mapping to <code>if-then-else</code>, but there is another option to consider with <code>coalesce</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796804-5" type="radio" name="radio-set-1729796804-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796804-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796804-6" type="radio" name="radio-set-1729796804-5" class="tab-selector-2" /> <label for="tab-1729796804-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). groupCount().by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). coalesce(is(lt(<span class="integer">28</span>)).constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">young</span><span class="delimiter">&quot;</span></span>), is(lt(<span class="integer">30</span>)).constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">old</span><span class="delimiter">&quot;</span></span>), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">very old</span><span class="delimiter">&quot;</span></span>))) ==&gt;[<span class="key">young</span>:<span class="integer">1</span>,<span class="key">old</span>:<span class="integer">1</span>,very <span class="key">old</span>:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). groupCount().by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). coalesce(is(lt(<span class="integer">28</span>)).constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">young</span><span class="delimiter">&quot;</span></span>), is(lt(<span class="integer">30</span>)).constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">old</span><span class="delimiter">&quot;</span></span>), constant(<span class="string"><span class="delimiter">&quot;</span><span class="content">very old</span><span class="delimiter">&quot;</span></span>)))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The answer is the same, but this traversal removes the nested <code>choose</code>, which makes it easier to read.</p> </div> </div> </div> <div class="sect1"> <h2 id="looping">Looping</h2> <div class="sectionbody"> <div class="paragraph"> <p>One common use case when working with Gremlin is to perform complex looping statements using the <code>repeat()</code> step. While many of the common patterns for looping within traversals are discussed in the documentation there are several more complex patterns that include additional steps which are also commonly used. This section attempts to demonstrate how to use some of these more complex measurements.</p> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/tree-lca.png" alt="tree lca" width="230"></span> The following examples will use this graph depicted here:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-1" type="radio" name="radio-set-1729796814-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-2" type="radio" name="radio-set-1729796814-1" class="tab-selector-2" /> <label for="tab-1729796814-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> <div class="sect2"> <h3 id="_conditional_looping_with_max_depth">Conditional Looping with Max Depth</h3> <div class="paragraph"> <p>One common situation encountered when writing <code>repeat()</code> loop is the need to exit the loop when either a specific condition is met or a maximum depth is reached. Given that graph above the following traversal demonstrates how to accomplish this type of complex exit condition. In this example the traversal will start at vertex A and loop through all outgoing edges until it reaches vertex C or until it has completed 3 loops.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-3" type="radio" name="radio-set-1729796814-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-4" type="radio" name="radio-set-1729796814-3" class="tab-selector-2" /> <label for="tab-1729796814-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(hasId(<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).or().loops().is(<span class="integer">3</span>)) ==&gt;v[C]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(hasId(<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).or().loops().is(<span class="integer">3</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the above case the traversal processes the <code>out().simplePath()</code> step once to move from vertex A to vertex B, a second time to move from vertex B to vertex C, and then exits the traversal because it satisfies the <code>hasId('C')</code> condition since it&#8217;s reached vertex C. If we however change that exit condition to be vertex G, we will see that this traversal now will continue to process thorough vertex X and will exit based on reaching the maximum number of loops (3).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-5" type="radio" name="radio-set-1729796814-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-6" type="radio" name="radio-set-1729796814-5" class="tab-selector-2" /> <label for="tab-1729796814-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(hasId(<span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).or().loops().is(<span class="integer">3</span>)) ==&gt;v[E]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(hasId(<span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).or().loops().is(<span class="integer">3</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The key portion of the above traversal is the <code>until()</code> step. This step is capable of accepting a <code>Traversal</code> object which continues the <code>repeat()</code> loop traversing until the evaluated output of the traversal is true, when the traverser exits the <code>repeat()</code> loop. This methodology allows complex logical conditions to be used as the exit criteria.</p> </div> </div> <div class="sect2"> <h3 id="_emitting_loop_depth">Emitting Loop Depth</h3> <div class="paragraph"> <p>Another common situation encountered when using <code>repeat()</code> loops is the desire to emit not only the value of the traverser at each step, but also to emit the depth of that element in the repeat loop. Below are several different recipes for accomplishing this task based on what the desired output:</p> </div> <div class="paragraph"> <p>If the desired output is to get each vertex and its associated depth this can be accomplished using this traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-7" type="radio" name="radio-set-1729796814-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-8" type="radio" name="radio-set-1729796814-7" class="tab-selector-2" /> <label for="tab-1729796814-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="integer">1</span>).V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(both().simplePath(). sack(assign).by(loops())). emit(). project(<span class="string"><span class="delimiter">'</span><span class="content">vertex</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">depth</span><span class="delimiter">'</span></span>). by(). by(sack()) ==&gt;[<span class="key">vertex</span>:v[B],<span class="key">depth</span>:<span class="integer">0</span>] ==&gt;[<span class="key">vertex</span>:v[C],<span class="key">depth</span>:<span class="integer">1</span>] ==&gt;[<span class="key">vertex</span>:v[E],<span class="key">depth</span>:<span class="integer">2</span>] ==&gt;[<span class="key">vertex</span>:v[D],<span class="key">depth</span>:<span class="integer">2</span>] ==&gt;[<span class="key">vertex</span>:v[F],<span class="key">depth</span>:<span class="integer">3</span>] ==&gt;[<span class="key">vertex</span>:v[G],<span class="key">depth</span>:<span class="integer">4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="integer">1</span>).V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(both().simplePath(). sack(assign).by(loops())). emit(). project(<span class="string"><span class="delimiter">'</span><span class="content">vertex</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">depth</span><span class="delimiter">'</span></span>). by(). by(sack())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>However, if the desired output is to get a single result per level containing all the vertices at that level this can be accomplished with this traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-9" type="radio" name="radio-set-1729796814-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-10" type="radio" name="radio-set-1729796814-9" class="tab-selector-2" /> <label for="tab-1729796814-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(both().simplePath(). group(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). by(loops())). emit(). times(<span class="integer">3</span>). cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==&gt;[<span class="integer">0</span>:[v[B]],<span class="integer">1</span>:[v[C]],<span class="integer">2</span>:[v[E],v[D]]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(both().simplePath(). group(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). by(loops())). emit(). times(<span class="integer">3</span>). cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_optional_loop_depth">Optional Loop Depth</h3> <div class="paragraph"> <p>A third common use of <code>repeat()</code> loops is to loop through a series of steps a certain number of times or until there are no additional edges to traverse. The examples below demonstrate how this loop termination can be accomplished.</p> </div> <div class="paragraph"> <p>The first method, is an extension of our "Conditional Looping with Max Depth" pattern above where the condition in the <code>until()</code> checking for a vertex with a degree of zero.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-11" type="radio" name="radio-set-1729796814-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-12" type="radio" name="radio-set-1729796814-11" class="tab-selector-2" /> <label for="tab-1729796814-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(outE().count().is(<span class="integer">0</span>).or().loops().is(<span class="integer">5</span>)) ==&gt;v[F]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out().simplePath()). until(outE().count().is(<span class="integer">0</span>).or().loops().is(<span class="integer">5</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Another way to accomplish this requirement is to use the <code>optional()</code> step. Here use the <code>optional()</code> step within a <code>repeat().times()</code> loop. In the example below we start at vertex A and traverse all <code>out()</code> edges up to a maximum of 5 times.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-13" type="radio" name="radio-set-1729796814-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-14" type="radio" name="radio-set-1729796814-13" class="tab-selector-2" /> <label for="tab-1729796814-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(optional(out())).times(<span class="integer">5</span>) ==&gt;v[F]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(optional(out())).times(<span class="integer">5</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Each of these patterns can be combined with any variety of other steps to satisfy more complex traversal requirements. For example, we can first use a more traditional <code>repeat()</code> loop to start at vertex A, traverse all <code>both()</code> edges two times, and then traverse <code>out()</code> edges up to five times.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796814-15" type="radio" name="radio-set-1729796814-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796814-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796814-16" type="radio" name="radio-set-1729796814-15" class="tab-selector-2" /> <label for="tab-1729796814-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(both()).times(<span class="integer">2</span>).repeat(optional(out())).times(<span class="integer">5</span>) ==&gt;v[F] ==&gt;v[F]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(both()).times(<span class="integer">2</span>).repeat(optional(out())).times(<span class="integer">5</span>)</code></pre> </div> </div> </div> </div> </section> </div> </div> </div> <div class="sect1"> <h2 id="operating-on-dropped-elements">Operating on Dropped Elements</h2> <div class="sectionbody"> <div class="paragraph"> <p>One common scenario that happens when dropping elements using a traversal is the desire to perform some sort of operation on the elements being removed. This simplest is returning the number of elements being dropped from the traversal, which is shown in the example below from the "modern" graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796825-1" type="radio" name="radio-set-1729796825-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796825-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796825-2" type="radio" name="radio-set-1729796825-1" class="tab-selector-2" /> <label for="tab-1729796825-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>).sideEffect(drop()).count() ==&gt;<span class="integer">2</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>).sideEffect(drop()).count()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>While this may seem a bit counterintuitive, what is occurring in this recipe is that the <code>sideEffect()</code> step is performing the <code>drop()</code> while each of the input traverser is still passed along to the remainder of the traversal, which in this case is a <code>count()</code> operation. This pattern can be extended beyond a simple count to perform a variety of more complex traversals based on the dropped traversal such as removing child nodes in the case of a tree or setting properties on adjacent vertices.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Some graph implementations may not behave in the same manner as TinkerGraph when using side effects, such as the <code>sideEffect(drop())</code>. While the specified pattern works for TinkerGraph, we recommend that this pattern be tested on your chosen implementation to ensure that it functions in the same manner. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="pagination">Pagination</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-paging.png" alt="gremlin paging" width="330"></span>In most database applications, it is oftentimes desirable to return discrete blocks of data for a query rather than all of the data that the total results would contain. This approach to returning data is referred to as "pagination" and typically involves a situation where the client executing the query can specify the start position and end position (or the amount of data to return in lieu of the end position) representing the block of data to return. In this way, one could return the first ten records of one hundred, then the second ten records and so on, until potentially all one hundred were returned.</p> </div> <div class="paragraph"> <p>In Gremlin, a basic approach to paging would look something like the following:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796834-1" type="radio" name="radio-set-1729796834-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796834-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796834-2" type="radio" name="radio-set-1729796834-1" class="tab-selector-2" /> <label for="tab-1729796834-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).fold() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">4</span>],v[<span class="integer">6</span>]] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). fold().as(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). by(range(local, <span class="integer">0</span>, <span class="integer">2</span>)). by(count(local)) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;[<span class="key">persons</span>:[v[<span class="integer">1</span>],v[<span class="integer">2</span>]],<span class="key">count</span>:<span class="integer">4</span>] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). fold().as(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). by(range(local, <span class="integer">2</span>, <span class="integer">4</span>)). by(count(local)) <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;[<span class="key">persons</span>:[v[<span class="integer">4</span>],v[<span class="integer">6</span>]],<span class="key">count</span>:<span class="integer">4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).fold() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). fold().as(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). by(range(local, <span class="integer">0</span>, <span class="integer">2</span>)). by(count(local)) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). fold().as(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">persons</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">count</span><span class="delimiter">'</span></span>). by(range(local, <span class="integer">2</span>, <span class="integer">4</span>)). by(count(local)) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Gets all the "person" vertices.</p> </li> <li> <p>Gets the first two "person" vertices and includes the total number of vertices so that the client knows how many it has to page through.</p> </li> <li> <p>Gets the final two "person" vertices.</p> </li> </ol> </div> <div class="paragraph"> <p>From a functional perspective, the above example shows a fairly standard paging model. Unfortunately, there is a problem. To get the total number of vertices, the traversal must first <code>fold()</code> them, which iterates out the traversal bringing them all into memory. If the number of "person" vertices is large, that step could lead to a long running traversal and perhaps one that would simply run out of memory prior to completion. There is no shortcut to getting a total count without doing a full iteration of the traversal. If the requirement for a total count is removed then the traversals become more simple:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796834-3" type="radio" name="radio-set-1729796834-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796834-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796834-4" type="radio" name="radio-set-1729796834-3" class="tab-selector-2" /> <label for="tab-1729796834-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).range(<span class="integer">0</span>,<span class="integer">2</span>) ==&gt;v[<span class="integer">1</span>] ==&gt;v[<span class="integer">2</span>] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).range(<span class="integer">2</span>,<span class="integer">4</span>) ==&gt;v[<span class="integer">4</span>] ==&gt;v[<span class="integer">6</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).range(<span class="integer">0</span>,<span class="integer">2</span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).range(<span class="integer">2</span>,<span class="integer">4</span>)</code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The first traversal above could also be written as <code>g.V().hasLabel('person').limit(2)</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>In this case, there is no way to know the total count so the only way to know if the end of the results have been reached is to count the results from each paged result to see if there&#8217;s less than the number expected or simply zero results. In that case, further requests for additional pages would be unnecessary. Of course, this approach is not free of problems either. Most graph databases will not optimize the <code>range()</code>-step, meaning that the second traversal will repeat the iteration of the first two vertices to get to the second set of two vertices. In other words, for the second traversal, the graph will still read four vertices even though there was only a request for two.</p> </div> <div class="paragraph"> <p>The only way to completely avoid that problem is to re-use the same traversal instance:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796834-5" type="radio" name="radio-set-1729796834-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796834-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796834-6" type="radio" name="radio-set-1729796834-5" class="tab-selector-2" /> <label for="tab-1729796834-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; t = g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>);<span class="type">[]</span> gremlin&gt; t.next(<span class="integer">2</span>) ==&gt;v[<span class="integer">1</span>] ==&gt;v[<span class="integer">2</span>] gremlin&gt; t.next(<span class="integer">2</span>) ==&gt;v[<span class="integer">4</span>] ==&gt;v[<span class="integer">6</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">t = g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>);<span class="type">[]</span> t.next(<span class="integer">2</span>) t.next(<span class="integer">2</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>A further consideration relates to the order in which results are returned. TinkerPop does not guarantee that the order of the items returned on the same traversal will be the same order each time the traversal is iterated. TinkerPop only guarantees that it does not re-shuffle the order provided by the underlying graph database. This guarantee has two implications:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Iteration order is dependent on the underlying graph database. Some graphs may guarantee ordering and some may not and still some others may guarantee ordering but only under certain conditions. Consult the documentation of the graph database for more information on this.</p> </li> <li> <p>Use <code>order()</code>-step to make iteration order explicit if guarantees are required.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="recommendation">Recommendation</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-recommendation.png" alt="gremlin recommendation" width="180"></span>One of the more common use cases for a graph database is the development of <a href="https://en.wikipedia.org/wiki/Recommender_system">recommendation systems</a> and a simple approach to doing that is through <a href="https://en.wikipedia.org/wiki/Collaborative_filtering">collaborative filtering</a>. Collaborative filtering assumes that if a person shares one set of opinions with a different person, they are likely to have similar taste with respect to other issues. With that basis in mind, it is then possible to make predictions for a specific person as to what their opinions might be.</p> </div> <div class="paragraph"> <p>As a simple example, consider a graph that contains "person" and "product" vertices connected by "bought" edges. The following script generates some data for the graph using that basic schema:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-1" type="radio" name="radio-set-1729796844-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-2" type="radio" name="radio-set-1729796844-1" class="tab-selector-2" /> <label for="tab-1729796844-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jon</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jack</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jill</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; (<span class="integer">1</span>..<span class="integer">10</span>).each { g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> gremlin&gt; (<span class="integer">3</span>..<span class="integer">7</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> gremlin&gt; (<span class="integer">1</span>..<span class="integer">5</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> gremlin&gt; (<span class="integer">6</span>..<span class="integer">10</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jon</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> gremlin&gt; <span class="integer">1</span>.step(<span class="integer">10</span>, <span class="integer">2</span>) { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jack</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> gremlin&gt; <span class="integer">2</span>.step(<span class="integer">10</span>, <span class="integer">2</span>) { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jill</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jon</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jack</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jill</span><span class="delimiter">&quot;</span></span>).iterate() (<span class="integer">1</span>..<span class="integer">10</span>).each { g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> (<span class="integer">3</span>..<span class="integer">7</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> (<span class="integer">1</span>..<span class="integer">5</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">bob</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> (<span class="integer">6</span>..<span class="integer">10</span>).each { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jon</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> <span class="integer">1</span>.step(<span class="integer">10</span>, <span class="integer">2</span>) { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jack</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span> <span class="integer">2</span>.step(<span class="integer">10</span>, <span class="integer">2</span>) { g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">jill</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">product #</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).iterate() }; <span class="type">[]</span></code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The first step to making a recommendation to "alice" using collaborative filtering is to understand what she bought:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-3" type="radio" name="radio-set-1729796844-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-4" type="radio" name="radio-set-1729796844-3" class="tab-selector-2" /> <label for="tab-1729796844-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;product <span class="error">#</span><span class="integer">5</span> ==&gt;product <span class="error">#</span><span class="integer">6</span> ==&gt;product <span class="error">#</span><span class="integer">7</span> ==&gt;product <span class="error">#</span><span class="integer">3</span> ==&gt;product <span class="error">#</span><span class="integer">4</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The following diagram depicts one of the edges traversed in the above example between "alice" and "product #5". Obviously, the other products "alice" bought would have similar relations, but this diagram and those to follow will focus on the neighborhood around that product.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/recommendation-alice-1.png" alt="recommendation alice 1" width="500"></span></p> </div> <div class="paragraph"> <p>The next step is to determine who else purchased those products:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-5" type="radio" name="radio-set-1729796844-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-6" type="radio" name="radio-set-1729796844-5" class="tab-selector-2" /> <label for="tab-1729796844-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;alice ==&gt;bob ==&gt;jack ==&gt;jill ==&gt;jon</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is worth noting that "alice" is in the results above. She should really be excluded from the list as the interest is in what individuals other than herself purchased:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-7" type="radio" name="radio-set-1729796844-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-8" type="radio" name="radio-set-1729796844-7" class="tab-selector-2" /> <label for="tab-1729796844-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;bob ==&gt;jack ==&gt;jill ==&gt;jon</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The following diagram shows "alice" and those others who purchased "product #5".</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/recommendation-alice-2.png" alt="recommendation alice 2" width="600"></span></p> </div> <div class="paragraph"> <p>The knowledge of the people who bought the same things as "alice" can then be used to find the set of products that they bought:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-9" type="radio" name="radio-set-1729796844-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-10" type="radio" name="radio-set-1729796844-9" class="tab-selector-2" /> <label for="tab-1729796844-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;product <span class="error">#</span><span class="integer">1</span> ==&gt;product <span class="error">#</span><span class="integer">2</span> ==&gt;product <span class="error">#</span><span class="integer">3</span> ==&gt;product <span class="error">#</span><span class="integer">4</span> ==&gt;product <span class="error">#</span><span class="integer">5</span> ==&gt;product <span class="error">#</span><span class="integer">7</span> ==&gt;product <span class="error">#</span><span class="integer">9</span> ==&gt;product <span class="error">#</span><span class="integer">6</span> ==&gt;product <span class="error">#</span><span class="integer">8</span> ==&gt;product <span class="error">#</span><span class="integer">10</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><span class="image"><img src="../images/recommendation-alice-3.png" alt="recommendation alice 3" width="800"></span></p> </div> <div class="paragraph"> <p>This set of products could be the basis for recommendation, but it is important to remember that "alice" may have already purchased some of these products and it would be better to not pester her with recommendations for products that she already owns. Those products she already purchased can be excluded as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-11" type="radio" name="radio-set-1729796844-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-12" type="radio" name="radio-set-1729796844-11" class="tab-selector-2" /> <label for="tab-1729796844-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;product <span class="error">#</span><span class="integer">1</span> ==&gt;product <span class="error">#</span><span class="integer">2</span> ==&gt;product <span class="error">#</span><span class="integer">9</span> ==&gt;product <span class="error">#</span><span class="integer">8</span> ==&gt;product <span class="error">#</span><span class="integer">10</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). dedup().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><span class="image"><img src="../images/recommendation-alice-4.png" alt="recommendation alice 4" width="800"></span></p> </div> <div class="paragraph"> <p>The final step would be to group the remaining products (instead of <code>dedup()</code> which was mostly done for demonstration purposes) to form a ranking:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-13" type="radio" name="radio-set-1729796844-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-14" type="radio" name="radio-set-1729796844-13" class="tab-selector-2" /> <label for="tab-1729796844-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> <span class="keyword">in</span>(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(4)</b> groupCount(). order(local). by(values, desc) <span class="comment">//</span>// <b class="conum">(5)</b> ==&gt;[v[<span class="integer">10</span>]:<span class="integer">6</span>,v[<span class="integer">26</span>]:<span class="integer">5</span>,v[<span class="integer">12</span>]:<span class="integer">5</span>,v[<span class="integer">24</span>]:<span class="integer">4</span>,v[<span class="integer">28</span>]:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> <span class="keyword">in</span>(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(3)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(4)</b> groupCount(). order(local). by(values, desc) <span class="invisible">//</span><b class="conum">5</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find "alice" who is the person for whom the product recommendation is being made.</p> </li> <li> <p>Traverse to the products that "alice" bought and gather them for later use in the traversal.</p> </li> <li> <p>Traverse to the "person" vertices who bought the products that "alice" bought and exclude "alice" herself from that list.</p> </li> <li> <p>Given those people who bought similar products to "alice", find the products that they bought and exclude those that she already bought.</p> </li> <li> <p>Group the products and count the number of times they were purchased by others to come up with a ranking of products to recommend to "alice".</p> </li> </ol> </div> <div class="paragraph"> <p>The previous example was already described as "basic" and obviously could take into account whatever data is available to further improve the quality of the recommendation (e.g. product ratings, times of purchase, etc.). One option to improve the quality of what is recommended (without expanding the previous dataset) might be to choose the person vertices that make up the recommendation to "alice" who have the largest common set of purchases.</p> </div> <div class="paragraph"> <p>Looking back to the previous code example, consider its more strip down representation that shows those individuals who have at least one product in common:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-15" type="radio" name="radio-set-1729796844-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-16" type="radio" name="radio-set-1729796844-15" class="tab-selector-2" /> <label for="tab-1729796844-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup() ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">6</span>] ==&gt;v[<span class="integer">8</span>] ==&gt;v[<span class="integer">4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Next, do some grouping to find count how many products they have in common:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-17" type="radio" name="radio-set-1729796844-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-18" type="radio" name="radio-set-1729796844-17" class="tab-selector-2" /> <label for="tab-1729796844-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()) ==&gt;[v[<span class="integer">2</span>]:<span class="integer">3</span>,v[<span class="integer">4</span>]:<span class="integer">2</span>,v[<span class="integer">6</span>]:<span class="integer">3</span>,v[<span class="integer">8</span>]:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above output shows that the best that can be expected is three common products. The traversal needs to be aware of that maximum:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-19" type="radio" name="radio-set-1729796844-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-20" type="radio" name="radio-set-1729796844-19" class="tab-selector-2" /> <label for="tab-1729796844-20" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>) ==&gt;<span class="integer">3</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>With the maximum value available, it can be used to chose those "person" vertices that have the three products in common:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-21" type="radio" name="radio-set-1729796844-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-22" type="radio" name="radio-set-1729796844-21" class="tab-selector-2" /> <label for="tab-1729796844-22" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys) ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">6</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Now that there is a list of "person" vertices to base the recommendation on, traverse to the products that they purchased:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-23" type="radio" name="radio-set-1729796844-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-24" type="radio" name="radio-set-1729796844-23" class="tab-selector-2" /> <label for="tab-1729796844-24" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)) ==&gt;v[<span class="integer">10</span>] ==&gt;v[<span class="integer">12</span>] ==&gt;v[<span class="integer">26</span>] ==&gt;v[<span class="integer">10</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above output shows that one product is held in common making it the top recommendation:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-25" type="radio" name="radio-set-1729796844-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-26" type="radio" name="radio-set-1729796844-25" class="tab-selector-2" /> <label for="tab-1729796844-26" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)). groupCount(). order(local). by(values, desc). by(select(keys).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). unfold().select(keys).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>) ==&gt;product <span class="error">#</span><span class="integer">1</span> ==&gt;product <span class="error">#</span><span class="integer">2</span> ==&gt;product <span class="error">#</span><span class="integer">9</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>). in(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>)).dedup(). group(). by().by(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>). where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)).count()).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). select(values). order(local). by(desc).limit(local, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).unfold(). where(select(values).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>)).select(keys). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">bought</span><span class="delimiter">&quot;</span></span>).where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">self</span><span class="delimiter">&quot;</span></span>)). groupCount(). order(local). by(values, desc). by(select(keys).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). unfold().select(keys).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In considering the practical applications of this recipe, it is worth revisiting the earlier "basic" version of the recommendation algorithm:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-27" type="radio" name="radio-set-1729796844-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-28" type="radio" name="radio-set-1729796844-27" class="tab-selector-2" /> <label for="tab-1729796844-28" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). groupCount(). order(local). by(values, desc) ==&gt;[v[<span class="integer">10</span>]:<span class="integer">6</span>,v[<span class="integer">26</span>]:<span class="integer">5</span>,v[<span class="integer">12</span>]:<span class="integer">5</span>,v[<span class="integer">24</span>]:<span class="integer">4</span>,v[<span class="integer">28</span>]:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)). groupCount(). order(local). by(values, desc)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above traversal performs a full ranking of items based on all the connected data. That could be a time consuming operation depending on the number of paths being traversed. As it turns out, recommendations don&#8217;t need to have perfect knowledge of all data to provide a "pretty good" approximation of a recommendation. It can therefore make sense to place additional limits on the traversal to have it better return more quickly at the expense of examining less data.</p> </div> <div class="paragraph"> <p>Gremlin provides a number of steps that can help with these limits like: <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#coin-step">coin()</a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sample-step">sample()</a>, and <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#timelimit-step">timeLimit()</a>. For example, to have the traversal sample the data for no longer than one second, the previous "basic" recommendation could be changed to:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796844-29" type="radio" name="radio-set-1729796844-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729796844-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729796844-30" type="radio" name="radio-set-1729796844-29" class="tab-selector-2" /> <label for="tab-1729796844-30" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)).timeLimit(<span class="integer">1000</span>). groupCount(). order(local). by(values, desc) ==&gt;[v[<span class="integer">10</span>]:<span class="integer">6</span>,v[<span class="integer">26</span>]:<span class="integer">5</span>,v[<span class="integer">12</span>]:<span class="integer">5</span>,v[<span class="integer">24</span>]:<span class="integer">4</span>,v[<span class="integer">28</span>]:<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">her</span><span class="delimiter">'</span></span>)). out(<span class="string"><span class="delimiter">'</span><span class="content">bought</span><span class="delimiter">'</span></span>).where(without(<span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span>)).timeLimit(<span class="integer">1000</span>). groupCount(). order(local). by(values, desc)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In using sampling methods, it is important to consider that the natural ordering of edges in the graph may not produce an ideal sample for the recommendation. For example, if the edges end up being returned oldest first, then the recommendation will be based on the oldest data, which would not be ideal. As with any traversal, it is important to understand the nature of the graph being traversed and the behavior of the underlying graph database to properly achieve the desired outcome.</p> </div> </div> </div> <div class="sect1"> <h2 id="shortest-path">Shortest Path</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image"><img src="../images/shortest-path.png" alt="shortest path" width="300"></span></p> </div> <div class="paragraph"> <p>When working with a graph, it is often necessary to identify the <a href="https://en.wikipedia.org/wiki/Shortest_path_problem">shortest path</a> between two identified vertices. The following is a simple example that identifies the shortest path between vertex "1" and vertex "5" while traversing over out edges:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-1" type="radio" name="radio-set-1729796857-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-2" type="radio" name="radio-set-1729796857-1" class="tab-selector-2" /> <label for="tab-1729796857-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">4</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">5</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path().limit(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] gremlin&gt; g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path().count(local) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;<span class="integer">4</span> ==&gt;<span class="integer">5</span> gremlin&gt; g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path(). group().by(count(local)).next() <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;<span class="integer">4</span>=[path[v[<span class="integer">1</span>], v[<span class="integer">2</span>], v[<span class="integer">4</span>], v[<span class="integer">5</span>]]] ==&gt;<span class="integer">5</span>=[path[v[<span class="integer">1</span>], v[<span class="integer">2</span>], v[<span class="integer">3</span>], v[<span class="integer">4</span>], v[<span class="integer">5</span>]]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">4</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">5</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).iterate() g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path().limit(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path().count(local) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path(). group().by(count(local)).next() <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The traversal starts at vertex with the identifier of "1" and repeatedly traverses on out edges "until" it finds a vertex with an identifier of "5". The inclusion of <code>simplePath</code> within the <code>repeat</code> is present to filter out repeated paths. The traversal terminates with <code>limit</code> in this case as the first path returned will be the shortest one. Of course, it is possible for there to be more than one path in the graph of the same length (i.e. two or more paths of length three), but this example is not considering that.</p> </li> <li> <p>It might be interesting to know the path lengths for all paths between vertex "1" and "5".</p> </li> <li> <p>Alternatively, one might wish to do a path length distribution over all the paths.</p> </li> </ol> </div> <div class="paragraph"> <p>The following code block demonstrates how the shortest path from <code>v[1]</code> to <code>v[5]</code> can be queried in OLAP, using the <code>shortestPath()</code> step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-3" type="radio" name="radio-set-1729796857-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-4" type="radio" name="radio-set-1729796857-3" class="tab-selector-2" /> <label for="tab-1729796857-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g = g.withComputer() ==&gt;graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">5</span> <span class="key">edges</span>:<span class="integer">5</span>], graphcomputer] gremlin&gt; g.V(<span class="integer">1</span>).shortestPath(). with(ShortestPath.edges, Direction.OUT). with(ShortestPath.target, hasId(<span class="integer">5</span>)) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g = g.withComputer() g.V(<span class="integer">1</span>).shortestPath(). with(ShortestPath.edges, Direction.OUT). with(ShortestPath.target, hasId(<span class="integer">5</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The previous example defines the length of the path by the number of vertices in the path, but the "path" might also be measured by data within the graph itself. The following example use the same graph structure as the previous example, but includes a "weight" on the edges, that will be used to help determine the "cost" of a particular path:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-5" type="radio" name="radio-set-1729796857-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-6" type="radio" name="radio-set-1729796857-5" class="tab-selector-2" /> <label for="tab-1729796857-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">4</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">5</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">1.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">1.5</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path(). group().by(count(local)).next() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;<span class="integer">4</span>=[path[v[<span class="integer">1</span>], v[<span class="integer">2</span>], v[<span class="integer">4</span>], v[<span class="integer">5</span>]]] ==&gt;<span class="integer">5</span>=[path[v[<span class="integer">1</span>], v[<span class="integer">2</span>], v[<span class="integer">3</span>], v[<span class="integer">4</span>], v[<span class="integer">5</span>]]] gremlin&gt; g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>), constant(<span class="float">0.0</span>))). map(unfold().sum()) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;<span class="float">3.00</span> ==&gt;<span class="float">2.00</span> gremlin&gt; g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().by(constant(<span class="float">0.0</span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).map(unfold().sum()) <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;<span class="float">3.00</span> ==&gt;<span class="float">2.00</span> gremlin&gt; g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). map(unfold().coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>), constant(<span class="float">0.0</span>)).sum()).as(<span class="string"><span class="delimiter">'</span><span class="content">cost</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">cost</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==&gt;[<span class="key">cost</span>:<span class="float">3.00</span>,<span class="key">p</span>:[v[<span class="integer">1</span>],e[<span class="integer">0</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">1</span>][<span class="integer">2</span>-knows-&gt;<span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">2</span>][<span class="integer">4</span>-knows-&gt;<span class="integer">5</span>],v[<span class="integer">5</span>]]] ==&gt;[<span class="key">cost</span>:<span class="float">2.00</span>,<span class="key">p</span>:[v[<span class="integer">1</span>],e[<span class="integer">0</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">3</span>][<span class="integer">2</span>-knows-&gt;<span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">4</span>][<span class="integer">3</span>-knows-&gt;<span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">2</span>][<span class="integer">4</span>-knows-&gt;<span class="integer">5</span>],v[<span class="integer">5</span>]]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">4</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>). addV().property(id, <span class="integer">5</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">1</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">1.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">1.5</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">5</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">2</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">3</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">4</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="float">0.25</span>).iterate() g.V(<span class="integer">1</span>).repeat(out().simplePath()).until(hasId(<span class="integer">5</span>)).path(). group().by(count(local)).next() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().by(coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>), constant(<span class="float">0.0</span>))). map(unfold().sum()) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().by(constant(<span class="float">0.0</span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).map(unfold().sum()) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">1</span>).repeat(outE().inV().simplePath()).until(hasId(<span class="integer">5</span>)). path().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). map(unfold().coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>), constant(<span class="float">0.0</span>)).sum()).as(<span class="string"><span class="delimiter">'</span><span class="content">cost</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">cost</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Note that the shortest path as determined by the structure of the graph is the same.</p> </li> <li> <p>Calculate the "cost" of the path as determined by the weight on the edges. As the "weight" data is on the edges between the vertices, it is necessary to change the contents of the <code>repeat</code> step to use <code>outE().inV()</code> so that the edge is included in the path. The path is then post-processed with a <code>by</code> modulator that extracts the "weight" value. The traversal uses <code>coalesce</code> as there is a mixture of vertices and edges in the path and the traversal is only interested in edge elements that can return a "weight" property. The final part of the traversal executes a map function over each path, unfolding it and summing the weights.</p> </li> <li> <p>The same traversal as the one above it, but avoids the use of <code>coalesce</code> with the use of two <code>by</code> modulators. The <code>by</code> modulator is applied in a round-robin fashion, so the first <code>by</code> will always apply to a vertex (as it is the first item in every path) and the second <code>by</code> will always apply to an edge (as it always follows the vertex in the path).</p> </li> <li> <p>The output of the previous examples of the "cost" wasn&#8217;t terribly useful as it didn&#8217;t include which path had the calculated cost. With some slight modifications given the use of <code>select</code> it becomes possible to include the path in the output. Note that the path with the lowest "cost" actually has a longer path length as determined by the graph structure.</p> </li> </ol> </div> <div class="paragraph"> <p>The next code block demonstrates how the <code>shortestPath()</code> step can be used in OLAP to determine the shortest weighted path.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-7" type="radio" name="radio-set-1729796857-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-8" type="radio" name="radio-set-1729796857-7" class="tab-selector-2" /> <label for="tab-1729796857-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g = g.withComputer() ==&gt;graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">5</span> <span class="key">edges</span>:<span class="integer">5</span>], graphcomputer] gremlin&gt; g.V(<span class="integer">1</span>).shortestPath(). with(ShortestPath.edges, Direction.OUT). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). with(ShortestPath.target, hasId(<span class="integer">5</span>)) ==&gt;[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g = g.withComputer() g.V(<span class="integer">1</span>).shortestPath(). with(ShortestPath.edges, Direction.OUT). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). with(ShortestPath.target, hasId(<span class="integer">5</span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The following query illustrates how <code>select(&lt;traversal&gt;)</code> can be used to find all shortest weighted undirected paths in the modern toy graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-9" type="radio" name="radio-set-1729796857-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-10" type="radio" name="radio-set-1729796857-9" class="tab-selector-2" /> <label for="tab-1729796857-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="float">0.0</span>).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(bothE(). sack(sum). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>). otherV(). <span class="comment">//</span>// <b class="conum">(2)</b> where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> group(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>)). by(sack().min()). filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(5)</b> by(sack()). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>))). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>))). group(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(6)</b> by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>)). by(map(union(path().by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>), sack()).fold())). barrier()). cap(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).unfold(). order(). <span class="comment">//</span>// <b class="conum">(7)</b> by(select(keys).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>).id()). barrier(). dedup(). <span class="comment">//</span>// <b class="conum">(8)</b> by(select(keys).select(values).order(local).by(id)) ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">2</span>]}=[path[marko, <span class="float">0.5</span>, vadas], <span class="float">0.5</span>] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">3</span>]}=[path[marko, <span class="float">0.4</span>, lop], <span class="float">0.4</span>] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">4</span>]}=[path[marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh], <span class="float">0.8</span>] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">5</span>]}=[path[marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple], <span class="float">1.8</span>] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">6</span>]}=[path[marko, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter], <span class="float">0.6</span>] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">3</span>]}=[path[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop], <span class="float">0.9</span>] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">4</span>]}=[path[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh], <span class="float">1.3</span>] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">5</span>]}=[path[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple], <span class="float">2.3</span>] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">6</span>]}=[path[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter], <span class="float">1.1</span>] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">4</span>]}=[path[lop, <span class="float">0.4</span>, josh], <span class="float">0.4</span>] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">5</span>]}=[path[lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple], <span class="float">1.4</span>] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">6</span>]}=[path[lop, <span class="float">0.2</span>, peter], <span class="float">0.2</span>] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">5</span>]}=[path[josh, <span class="float">1.0</span>, ripple], <span class="float">1.0</span>] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">6</span>]}=[path[josh, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter], <span class="float">0.6</span>] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">6</span>]}=[path[ripple, <span class="float">1.0</span>, josh, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter], <span class="float">1.6</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="float">0.0</span>).V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(bothE(). sack(sum). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>). otherV(). <span class="comment">//</span>// <b class="conum">(2)</b> where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> group(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(4)</b> by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>)). by(sack().min()). filter(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(5)</b> by(sack()). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>))). where(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>, eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>))). group(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(6)</b> by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>)). by(map(union(path().by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>), sack()).fold())). barrier()). cap(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).unfold(). order(). <span class="comment">//</span>// <b class="conum">(7)</b> by(select(keys).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>).id()). barrier(). dedup(). <span class="comment">//</span>// <b class="conum">(8)</b> by(select(keys).select(values).order(local).by(id))</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Start the traversal from all vertices with an initial sack value of 0.</p> </li> <li> <p>Traverse into all directions and sum up the edge weight values.</p> </li> <li> <p>Filter out the initial start vertex.</p> </li> <li> <p>For the current start and end vertex, update the minimum sack value (weighted length of the path).</p> </li> <li> <p>Compare the current weighted path length to the current minimum weighted path length between the 2 vertices. Eliminate traversers that found a path that is longer than the current shortest path.</p> </li> <li> <p>Update the path and weighted path length for the current start and end vertex pair.</p> </li> <li> <p>Order the output by the start vertex id and then the end vertex id (for better readability).</p> </li> <li> <p>Deduplicate vertex pairs (the shortest path from <code>v[1]</code> to <code>v[6]</code> is the same as the path from <code>v[6]</code> to <code>v[1]</code>).</p> </li> </ol> </div> <div class="paragraph"> <p>Again, this can be translated into an OLAP query using the <code>shortestPath()</code> step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-11" type="radio" name="radio-set-1729796857-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-12" type="radio" name="radio-set-1729796857-11" class="tab-selector-2" /> <label for="tab-1729796857-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; result = g.withComputer().V(). shortestPath(). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). with(ShortestPath.includeEdges, <span class="predefined-constant">true</span>). filter(count(local).is(gt(<span class="integer">1</span>))). group(). by(project(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>). by(limit(local, <span class="integer">1</span>)). by(tail(local, <span class="integer">1</span>))). unfold(). order(). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>).id()).toList() ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">2</span>]}=[path[v[<span class="integer">1</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">2</span>]]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">3</span>]}=[path[v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>]]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">4</span>]}=[path[v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>]]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">5</span>]}=[path[v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">5</span>]]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">6</span>]}=[path[v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">6</span>]]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">1</span>]}=[path[v[<span class="integer">2</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">1</span>]]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">3</span>]}=[path[v[<span class="integer">2</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>]]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">4</span>]}=[path[v[<span class="integer">2</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>]]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">5</span>]}=[path[v[<span class="integer">2</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">5</span>]]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">6</span>]}=[path[v[<span class="integer">2</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">1</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">6</span>]]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">1</span>]}=[path[v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>]]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">2</span>]}=[path[v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">2</span>]]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">4</span>]}=[path[v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>]]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">5</span>]}=[path[v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">5</span>]]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">6</span>]}=[path[v[<span class="integer">3</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">6</span>]]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">1</span>]}=[path[v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>]]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">2</span>]}=[path[v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">2</span>]]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">3</span>]}=[path[v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>]]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">5</span>]}=[path[v[<span class="integer">4</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">5</span>]]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">6</span>]}=[path[v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">6</span>]]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">1</span>]}=[path[v[<span class="integer">5</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>]]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">2</span>]}=[path[v[<span class="integer">5</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">2</span>]]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">3</span>]}=[path[v[<span class="integer">5</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>]]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">4</span>]}=[path[v[<span class="integer">5</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">4</span>]]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">6</span>]}=[path[v[<span class="integer">5</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">4</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">6</span>]]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">1</span>]}=[path[v[<span class="integer">6</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>]]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">2</span>]}=[path[v[<span class="integer">6</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">1</span>], e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>], v[<span class="integer">2</span>]]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">3</span>]}=[path[v[<span class="integer">6</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>]]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">4</span>]}=[path[v[<span class="integer">6</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>]]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">5</span>]}=[path[v[<span class="integer">6</span>], e[<span class="integer">12</span>][<span class="integer">6</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">3</span>], e[<span class="integer">11</span>][<span class="integer">4</span>-created-&gt;<span class="integer">3</span>], v[<span class="integer">4</span>], e[<span class="integer">10</span>][<span class="integer">4</span>-created-&gt;<span class="integer">5</span>], v[<span class="integer">5</span>]]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">result = g.withComputer().V(). shortestPath(). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). with(ShortestPath.includeEdges, <span class="predefined-constant">true</span>). filter(count(local).is(gt(<span class="integer">1</span>))). group(). by(project(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>). by(limit(local, <span class="integer">1</span>)). by(tail(local, <span class="integer">1</span>))). unfold(). order(). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>).id()).toList()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The obvious difference in the result is the absence of property values in the OLAP result. Since OLAP traversers are not allowed to leave the local star graph, it&#8217;s not possible to have the exact same result in an OLAP query. However, the determined shortest paths can be passed back into the OLTP <code>GraphTraversalSource</code>, which can then be used to query the values.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796857-13" type="radio" name="radio-set-1729796857-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796857-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796857-14" type="radio" name="radio-set-1729796857-13" class="tab-selector-2" /> <label for="tab-1729796857-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>, <span class="type">[]</span>). <span class="comment">//</span>// <b class="conum">(1)</b> inject(result.toArray()).as(<span class="string"><span class="delimiter">'</span><span class="content">kv</span><span class="delimiter">'</span></span>).select(values). unfold(). map(unfold().as(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>). coalesce(V().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>)).aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).tail(local, <span class="integer">1</span>).bothE().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>))). values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). fold()). group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">kv</span><span class="delimiter">'</span></span>).select(keys)).unfold(). order(). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>).id()).toList() ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">2</span>]}=[[marko, <span class="float">0.5</span>, vadas]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">3</span>]}=[[marko, <span class="float">0.4</span>, lop]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">4</span>]}=[[marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">5</span>]}=[[marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple]] ==&gt;{from=v[<span class="integer">1</span>], to=v[<span class="integer">6</span>]}=[[marko, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">1</span>]}=[[vadas, <span class="float">0.5</span>, marko]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">3</span>]}=[[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">4</span>]}=[[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">5</span>]}=[[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple]] ==&gt;{from=v[<span class="integer">2</span>], to=v[<span class="integer">6</span>]}=[[vadas, <span class="float">0.5</span>, marko, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">1</span>]}=[[lop, <span class="float">0.4</span>, marko]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">2</span>]}=[[lop, <span class="float">0.4</span>, marko, <span class="float">0.5</span>, vadas]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">4</span>]}=[[lop, <span class="float">0.4</span>, josh]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">5</span>]}=[[lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple]] ==&gt;{from=v[<span class="integer">3</span>], to=v[<span class="integer">6</span>]}=[[lop, <span class="float">0.2</span>, peter]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">1</span>]}=[[josh, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, marko]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">2</span>]}=[[josh, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, marko, <span class="float">0.5</span>, vadas]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">3</span>]}=[[josh, <span class="float">0.4</span>, lop]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">5</span>]}=[[josh, <span class="float">1.0</span>, ripple]] ==&gt;{from=v[<span class="integer">4</span>], to=v[<span class="integer">6</span>]}=[[josh, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">1</span>]}=[[ripple, <span class="float">1.0</span>, josh, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, marko]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">2</span>]}=[[ripple, <span class="float">1.0</span>, josh, <span class="float">0.4</span>, lop, <span class="float">0.4</span>, marko, <span class="float">0.5</span>, vadas]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">3</span>]}=[[ripple, <span class="float">1.0</span>, josh, <span class="float">0.4</span>, lop]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">4</span>]}=[[ripple, <span class="float">1.0</span>, josh]] ==&gt;{from=v[<span class="integer">5</span>], to=v[<span class="integer">6</span>]}=[[ripple, <span class="float">1.0</span>, josh, <span class="float">0.4</span>, lop, <span class="float">0.2</span>, peter]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">1</span>]}=[[peter, <span class="float">0.2</span>, lop, <span class="float">0.4</span>, marko]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">2</span>]}=[[peter, <span class="float">0.2</span>, lop, <span class="float">0.4</span>, marko, <span class="float">0.5</span>, vadas]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">3</span>]}=[[peter, <span class="float">0.2</span>, lop]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">4</span>]}=[[peter, <span class="float">0.2</span>, lop, <span class="float">0.4</span>, josh]] ==&gt;{from=v[<span class="integer">6</span>], to=v[<span class="integer">5</span>]}=[[peter, <span class="float">0.2</span>, lop, <span class="float">0.4</span>, josh, <span class="float">1.0</span>, ripple]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>, <span class="type">[]</span>). <span class="comment">//</span>// <b class="conum">(1)</b> inject(result.toArray()).as(<span class="string"><span class="delimiter">'</span><span class="content">kv</span><span class="delimiter">'</span></span>).select(values). unfold(). map(unfold().as(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>). coalesce(V().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>)).aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">'</span><span class="content">v</span><span class="delimiter">'</span></span>).tail(local, <span class="integer">1</span>).bothE().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">v_or_e</span><span class="delimiter">'</span></span>))). values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>). fold()). group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">kv</span><span class="delimiter">'</span></span>).select(keys)).unfold(). order(). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">from</span><span class="delimiter">'</span></span>).id()). by(select(keys).select(<span class="string"><span class="delimiter">'</span><span class="content">to</span><span class="delimiter">'</span></span>).id()).toList()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The side-effect <code>v</code> is used to keep track of the last processed vertex, hence it needs to be an order-preserving list. Without this explicit definition <code>v</code> would become a <code>BulkSet</code> which doesn&#8217;t preserve the insert order.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="traversal-induced-values">Traversal Induced Values</h2> <div class="sectionbody"> <div class="paragraph"> <p>The parameters of a <code>Traversal</code> can be known ahead of time as constants or might otherwise be passed in as dynamic arguments.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-1" type="radio" name="radio-set-1729796869-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-2" type="radio" name="radio-set-1729796869-1" class="tab-selector-2" /> <label for="tab-1729796869-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, gt(<span class="integer">29</span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;josh</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, gt(<span class="integer">29</span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In plain language, the above Gremlin asks, "What are the names of the people who Marko knows who are over the age of 29?". In this case, "29" is known as a constant to the traversal. Of course, if the question is changed slightly to instead ask, "What are the names of the people who Marko knows who are older than he is?", the hardcoding of "29" will no longer suffice. There are multiple ways Gremlin would allow this second question to be answered. The first is obvious to any programmer - use a variable:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-3" type="radio" name="radio-set-1729796869-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-4" type="radio" name="radio-set-1729796869-3" class="tab-selector-2" /> <label for="tab-1729796869-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; vMarko = g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).next() ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V(vMarko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, gt(marko.value(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>))).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;josh</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">vMarko = g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).next() g.V(vMarko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, gt(marko.value(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>))).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The downside to this approach is that it takes two separate traversals to answer the question. Ideally, there should be a single traversal, that can query "marko" once, determine his <code>age</code> and then use that for the value supplied to filter the people he knows. In this way the <em>value</em> for the <code>age</code> in the <code>has()</code>-filter is <em>induced</em> from the <code>Traversal</code> itself.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-5" type="radio" name="radio-set-1729796869-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-6" type="radio" name="radio-set-1729796869-5" class="tab-selector-2" /> <label for="tab-1729796869-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">friend</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">friend</span><span class="delimiter">'</span></span>, gt(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==&gt;josh</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">friend</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(2)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">friend</span><span class="delimiter">'</span></span>, gt(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find the "marko" <code>Vertex</code> and label it as "marko".</p> </li> <li> <p>Traverse out on the "knows" edges to the adjacent <code>Vertex</code> and label it as "friend".</p> </li> <li> <p>Continue to traverser only if Marko&#8217;s current friend is older than him.</p> </li> <li> <p>Get the name of Marko&#8217;s older friend.</p> </li> </ol> </div> <div class="paragraph"> <p>As another example of how traversal induced values can be used, consider a scenario where there was a graph that contained people, their friendship relationships, and the movies that they liked.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/traversal-induced-values-3.png" alt="traversal induced values 3" width="600"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-7" type="radio" name="radio-set-1729796869-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-8" type="radio" name="radio-set-1729796869-7" class="tab-selector-2" /> <label for="tab-1729796869-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">jen</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">dave</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">the wild bunch</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">young guns</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">unforgiven</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m3</span><span class="delimiter">&quot;</span></span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">jen</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">dave</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">the wild bunch</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">young guns</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">movie</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">unforgiven</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">m3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">u3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">m3</span><span class="delimiter">&quot;</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Getting a list of all the movies that Alice&#8217;s friends like could be done like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-9" type="radio" name="radio-set-1729796869-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-10" type="radio" name="radio-set-1729796869-9" class="tab-selector-2" /> <label for="tab-1729796869-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>) ==&gt;the wild bunch ==&gt;young guns ==&gt;young guns ==&gt;unforgiven</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>but what if there was a need to get a list of movies that <strong>all</strong> her Alice&#8217;s friends liked. In this case, that would mean filtering out "the wild bunch" and "unforgiven".</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-11" type="radio" name="radio-set-1729796869-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-12" type="radio" name="radio-set-1729796869-11" class="tab-selector-2" /> <label for="tab-1729796869-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).dedup(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(__.in(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>)).count().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(4)</b> values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>) ==&gt;young guns</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">friend</span><span class="delimiter">&quot;</span></span>).aggregate(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> out(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).dedup(). <span class="comment">//</span>// <b class="conum">(2)</b> filter(__.in(<span class="string"><span class="delimiter">&quot;</span><span class="content">like</span><span class="delimiter">&quot;</span></span>).where(within(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>)).count().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> select(<span class="string"><span class="delimiter">&quot;</span><span class="content">friends</span><span class="delimiter">&quot;</span></span>).count(local).where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>))). <span class="comment">//</span>// <b class="conum">(4)</b> values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Gather Alice&#8217;s list of friends to a list called "friends".</p> </li> <li> <p>Traverse to the unique list of movies that Alice&#8217;s friends like.</p> </li> <li> <p>Remove movies that weren&#8217;t liked by all friends. This starts by taking each movie and traversing back in on the "like" edges to friends who liked the movie (note the use of <code>where(within("friends"))</code> to limit those likes to only Alice&#8217;s friends as aggregated in step one) and count them up into "a".</p> </li> <li> <p>Count the aggregated friends and see if the number matches what was stored in "a" which would mean that all friends like the movie.</p> </li> </ol> </div> <div class="paragraph"> <p>Traversal induced values are not just for filtering. They can also be used when writing the values of the properties of one <code>Vertex</code> to another:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-13" type="radio" name="radio-set-1729796869-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-14" type="radio" name="radio-set-1729796869-13" class="tab-selector-2" /> <label for="tab-1729796869-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">creator</span><span class="delimiter">'</span></span>, select(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)) ==&gt;v[<span class="integer">3</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">creator</span>:marko,<span class="key">name</span>:lop,<span class="key">lang</span>:java]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">creator</span><span class="delimiter">'</span></span>, select(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In a more complex example of how this might work, consider a situation where the goal is to propagate a value stored on a particular vertex through one of more additional connected vertices using some value on the connecting edges to determine the value to assign. For example, the following graph depicts three "tank" vertices where the edges represent the direction a particular "tank" should drain and the "factor" by which it should do it:</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/traversal-induced-values-1.png" alt="traversal induced values 1" width="700"></span></p> </div> <div class="paragraph"> <p>If the traversal started at tank "a", then the value of "amount" on that tank would be used to calculate what the value of tank "b" was by multiplying it by the value of the "factor" property on the edge between vertices "a" and "b". In this case the amount of tank "b" would then be 50. Following this pattern, when going from tank "b" to tank "c", the value of the "amount" of tank "c" would be 5.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/traversal-induced-values-2.png" alt="traversal induced values 2" width="700"></span></p> </div> <div class="paragraph"> <p>Using Gremlin <code>sack()</code>, this kind of operation could be specified as a single traversal:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-15" type="radio" name="radio-set-1729796869-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-16" type="radio" name="radio-set-1729796869-15" class="tab-selector-2" /> <label for="tab-1729796869-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">100.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">0.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">0.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>, <span class="float">0.5</span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>, <span class="float">0.1</span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; vA = g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).next() ==&gt;v[<span class="integer">0</span>] gremlin&gt; g.withSack(vA.value(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>)). V(vA).repeat(outE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>). inV().property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, sack())). until(__.outE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).count().is(<span class="integer">0</span>)).iterate() gremlin&gt; g.V().elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:tank,<span class="key">amount</span>:<span class="float">100.0</span>,<span class="key">name</span>:a] ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:tank,<span class="key">amount</span>:<span class="float">50.00</span>,<span class="key">name</span>:b] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:tank,<span class="key">amount</span>:<span class="float">5.000</span>,<span class="key">name</span>:c]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">100.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">0.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">tank</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, <span class="float">0.0</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>, <span class="float">0.5</span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>, <span class="float">0.1</span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).iterate() vA = g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).next() g.withSack(vA.value(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>)). V(vA).repeat(outE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">factor</span><span class="delimiter">'</span></span>). inV().property(<span class="string"><span class="delimiter">'</span><span class="content">amount</span><span class="delimiter">'</span></span>, sack())). until(__.outE(<span class="string"><span class="delimiter">'</span><span class="content">drain</span><span class="delimiter">'</span></span>).count().is(<span class="integer">0</span>)).iterate() g.V().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The "sack value" gets initialized to the value of tank "a". The traversal iteratively traverses out on the "drain" edges and uses <code>mult</code> to multiply the sack value by the value of "factor". The sack value at that point is then written to the "amount" of the current vertex.</p> </div> <div class="paragraph"> <p>As shown in the previous example, <code>sack()</code> is a useful way to "carry" and manipulate a value that can be later used elsewhere in the traversal. Here is another example of its usage where it is utilized to increment all the "age" values in the modern toy graph by 10:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-17" type="radio" name="radio-set-1729796869-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-18" type="radio" name="radio-set-1729796869-17" class="tab-selector-2" /> <label for="tab-1729796869-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="integer">0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). sack(assign).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).by(constant(<span class="integer">10</span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, sack()).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">39</span>] ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">37</span>] ==&gt;[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">42</span>] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">45</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="integer">0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). sack(assign).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).by(constant(<span class="integer">10</span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, sack()).elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the above case, the sack is initialized to zero and as each vertex is iterated, the "age" is assigned to the sack with <code>sack(assign).by('age')</code>. That value in the sack is then incremented by the value <code>constant(10)</code> and assigned to the "age" property of the same vertex.</p> </div> <div class="paragraph"> <p>This value the sack is incremented by need not be a constant. It could also be derived from the traversal itself. Using the same example, the "weight" property on the incident edges will be used as the value to add to the sack:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796869-19" type="radio" name="radio-set-1729796869-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796869-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796869-20" type="radio" name="radio-set-1729796869-19" class="tab-selector-2" /> <label for="tab-1729796869-20" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="integer">0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). sack(assign).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).by(bothE().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>).sum()). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, sack()).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="float">30.9</span>] ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="float">27.5</span>] ==&gt;[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="float">34.4</span>] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">name</span>:peter,<span class="key">age</span>:<span class="float">35.2</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="integer">0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>). sack(assign).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).by(bothE().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>).sum()). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, sack()).elementMap()</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="tree">Tree</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image"><img src="../images/gremlin-tree.png" alt="gremlin tree" width="280"></span></p> </div> <div class="sect2"> <h3 id="_lowest_common_ancestor">Lowest Common Ancestor</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/tree-lca.png" alt="tree lca" width="230"></span> Given a tree, the <a href="https://en.wikipedia.org/wiki/Lowest_common_ancestor">lowest common ancestor</a> is the deepest vertex that is common to two or more other vertices. The diagram to the right depicts the common ancestor tree for vertices A and D in the various green shades. The C vertex, the vertex with the darkest green shading, is the lowest common ancestor.</p> </div> <div class="paragraph"> <p>The following code simply sets up the graph depicted above using "hasParent" for the edge label:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-1" type="radio" name="radio-set-1729796882-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-2" type="radio" name="radio-set-1729796882-1" class="tab-selector-2" /> <label for="tab-1729796882-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Given that graph, the following traversal will get the lowest common ancestor for two vertices, A and D:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-3" type="radio" name="radio-set-1729796882-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-4" type="radio" name="radio-set-1729796882-3" class="tab-selector-2" /> <label for="tab-1729796882-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). repeat(__.in(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(hasId(<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>) ==&gt;v[C]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). repeat(__.in(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(hasId(<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above traversal is reasonably straightforward to follow in that it simply traverses up the tree from the A vertex and then traverses down from each ancestor until it finds the "D" vertex. The first path that uncovers that match is the lowest common ancestor.</p> </div> <div class="paragraph"> <p>The complexity of finding the lowest common ancestor increases when trying to find the ancestors of three or more vertices.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-5" type="radio" name="radio-set-1729796882-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-6" type="radio" name="radio-set-1729796882-5" class="tab-selector-2" /> <label for="tab-1729796882-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; input = [<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>] ==&gt;A ==&gt;B ==&gt;D gremlin&gt; g.V(input.head()). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> V().has(id, within(input.tail())). <span class="comment">//</span>// <b class="conum">(2)</b> repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))). <span class="comment">//</span>// <b class="conum">(3)</b> group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)). by(path().count(local).fold()). <span class="comment">//</span>// <b class="conum">(4)</b> unfold().filter(select(values).count(local).is(input.tail().size())). <span class="comment">//</span>// <b class="conum">(5)</b> order().by(select(values). unfold().sum()). <span class="comment">//</span>// <b class="conum">(6)</b> select(keys).limit(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==&gt;v[C]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">input = [<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>] g.V(input.head()). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> V().has(id, within(input.tail())). <span class="comment">//</span>// <b class="conum">(2)</b> repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))). <span class="comment">//</span>// <b class="conum">(3)</b> group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)). by(path().count(local).fold()). <span class="comment">//</span>// <b class="conum">(4)</b> unfold().filter(select(values).count(local).is(input.tail().size())). <span class="comment">//</span>// <b class="conum">(5)</b> order().by(select(values). unfold().sum()). <span class="comment">//</span>// <b class="conum">(6)</b> select(keys).limit(<span class="integer">1</span>) <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The start of the traversal is not so different than the previous one and starts with vertex A.</p> </li> <li> <p>Use a mid-traversal <code>V()</code> to find the child vertices B and D.</p> </li> <li> <p>Traverse up the tree for B and D and find common ancestors that were labeled with "x".</p> </li> <li> <p>Group on the common ancestors where the value of the grouping is the length of the path.</p> </li> <li> <p>The result of the previous step is a <code>Map</code> with a vertex (i.e. common ancestor) for the key and a list of path lengths. Unroll the <code>Map</code> and ensure that the number of path lengths are equivalent to the number of children that were given to the mid-traversal <code>V()</code>.</p> </li> <li> <p>Order the results based on the sum of the path lengths.</p> </li> <li> <p>Since the results were placed in ascending order, the first result must be the lowest common ancestor.</p> </li> </ol> </div> <div class="paragraph"> <p>As the above traversal utilizes a mid-traversal <code>V()</code>, it cannot be used for OLAP. In OLAP, the pattern changes a bit:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-7" type="radio" name="radio-set-1729796882-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-8" type="radio" name="radio-set-1729796882-7" class="tab-selector-2" /> <label for="tab-1729796882-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withComputer(). V().has(id, within(input)). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>).hasId(input.head()). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>).unfold().has(id, within(input.tail())). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))). group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)). by(path().count(local).fold()). unfold().filter(select(values).count(local).is(input.tail().size())). order(). by(select(values).unfold().sum()). select(keys).limit(<span class="integer">1</span>) ==&gt;v[C]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withComputer(). V().has(id, within(input)). aggregate(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>).hasId(input.head()). <span class="comment">//</span>// <b class="conum">(1)</b> repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>). select(<span class="string"><span class="delimiter">'</span><span class="content">input</span><span class="delimiter">'</span></span>).unfold().has(id, within(input.tail())). repeat(out(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>)).emit(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>))). group(). by(select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)). by(path().count(local).fold()). unfold().filter(select(values).count(local).is(input.tail().size())). order(). by(select(values).unfold().sum()). select(keys).limit(<span class="integer">1</span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The main difference for OLAP is the use of <code>aggregate()</code> over the mid-traversal <code>V()</code>.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="_maximum_depth">Maximum Depth</h3> <div class="paragraph"> <p>Finding the maximum depth of a tree starting from a specified root vertex can be determined as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-9" type="radio" name="radio-set-1729796882-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-10" type="radio" name="radio-set-1729796882-9" class="tab-selector-2" /> <label for="tab-1729796882-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).repeat(__.in()).emit().path().count(local).max() ==&gt;<span class="integer">5</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).repeat(__.in()).emit().path().count(local).max() ==&gt;<span class="integer">3</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">G</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">hasParent</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">f</span><span class="delimiter">'</span></span>).iterate() g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>).repeat(__.in()).emit().path().count(local).max() g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).repeat(__.in()).emit().path().count(local).max()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-max-depth.png" alt="gremlin max depth" width="350"></span>The traversals shown above are fairly straightforward. The traversal beings at a particular starting vertex, traverse in on the "hasParent" edges emitting all vertices as it goes. It calculates the path length and then selects the longest one. While this approach is quite direct, there is room for improvement:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-11" type="radio" name="radio-set-1729796882-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-12" type="radio" name="radio-set-1729796882-11" class="tab-selector-2" /> <label for="tab-1729796882-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>). repeat(__.in()).emit(__.not(inE())).tail(<span class="integer">1</span>). path().count(local) ==&gt;<span class="integer">5</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>). repeat(__.in()).emit(__.not(inE())).tail(<span class="integer">1</span>). path().count(local) ==&gt;<span class="integer">3</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>). repeat(__.in()).emit(__.not(inE())).tail(<span class="integer">1</span>). path().count(local) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>). repeat(__.in()).emit(__.not(inE())).tail(<span class="integer">1</span>). path().count(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>There are two optimizations at play. First, there is no need to emit all the vertices, only the "leaf" vertices (i.e. those without incoming edges). Second, all results save the last one can be ignored to that point (i.e. the last one is the one at the deepest point in the tree). In this way, the path and path length only need to be calculated for a single result.</p> </div> <div class="paragraph"> <p>The previous approaches to calculating the maximum depth use path calculations to achieve the answer. Path calculations can be expensive and if possible avoided if they are not needed. Another way to express a traversal that calculates the maximum depth is to use the <code>sack()</code>-step:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-13" type="radio" name="radio-set-1729796882-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-14" type="radio" name="radio-set-1729796882-13" class="tab-selector-2" /> <label for="tab-1729796882-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSack(<span class="integer">1</span>).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>). repeat(__.in().sack(sum).by(constant(<span class="integer">1</span>))).emit(). sack().max() ==&gt;<span class="integer">5</span> gremlin&gt; g.withSack(<span class="integer">1</span>).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>). repeat(__.in().sack(sum).by(constant(<span class="integer">1</span>))).emit(). sack().max() ==&gt;<span class="integer">3</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSack(<span class="integer">1</span>).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">F</span><span class="delimiter">'</span></span>). repeat(__.in().sack(sum).by(constant(<span class="integer">1</span>))).emit(). sack().max() g.withSack(<span class="integer">1</span>).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>). repeat(__.in().sack(sum).by(constant(<span class="integer">1</span>))).emit(). sack().max()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_time_based_indexing">Time-based Indexing</h3> <div class="paragraph"> <p>Trees can be used for modeling time-oriented data in a graph. Modeling time where there are "year", "month" and "day" vertices (or lower granularity as needed) allows the structure of the graph to inherently index data tied to them.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../images/gremlin-index-time.png" alt="gremlin index time" width="800"></span></p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> This model is discussed further in this Neo4j <a href="https://neo4j.com/blog/modeling-a-multilevel-index-in-neoj4/">blog post</a>. Also, there can be other versions of this model that utilize different edge/vertex labeling and property naming strategies. The schema depicted here is designed for simplicity. </td> </tr> </table> </div> <div class="paragraph"> <p>The Gremlin script below creates the graph depicted in the graph above:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-15" type="radio" name="radio-set-1729796882-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-16" type="radio" name="radio-set-1729796882-15" class="tab-selector-2" /> <label for="tab-1729796882-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">year</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">month</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">month</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">30</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">31</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">01</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eA</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eB</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eC</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eD</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eE</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day30</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day01</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eA</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eB</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eC</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eD</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eE</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">'</span><span class="content">year</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">month</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">month</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">30</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">31</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">day</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">01</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eA</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">B</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eB</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">C</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eC</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eD</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">event</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">E</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eE</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">y2016</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day30</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">day01</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eA</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eB</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eC</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eD</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eE</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d30</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">d31</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">d01</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">m05</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">m06</span><span class="delimiter">'</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The code example above does not create any indices. Proper index creation, which is specific to the graph implementation used, will be critical to the performance of traversals over this structure. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796882-17" type="radio" name="radio-set-1729796882-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796882-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796882-18" type="radio" name="radio-set-1729796882-17" class="tab-selector-2" /> <label for="tab-1729796882-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out().out().out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;E ==&gt;C ==&gt;D ==&gt;A ==&gt;B gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out().out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;C ==&gt;D ==&gt;A ==&gt;B gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(3)</b> ==&gt;C ==&gt;D gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">start</span><span class="delimiter">'</span></span>). V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day01</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">end</span><span class="delimiter">'</span></span>). emit().repeat(__.in(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>)).until(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">start</span><span class="delimiter">'</span></span>))). out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==&gt;C ==&gt;D ==&gt;E</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out().out().out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out().out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(2)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>).values() <span class="comment">//</span>// <b class="conum">(3)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">may</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day31</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">start</span><span class="delimiter">'</span></span>). V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">2016</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">june</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">day01</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">end</span><span class="delimiter">'</span></span>). emit().repeat(__.in(<span class="string"><span class="delimiter">'</span><span class="content">next</span><span class="delimiter">'</span></span>)).until(where(eq(<span class="string"><span class="delimiter">'</span><span class="content">start</span><span class="delimiter">'</span></span>))). out(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find all the events in 2016.</p> </li> <li> <p>Find all the events in May of 2016.</p> </li> <li> <p>Find all the events on May 31, 2016.</p> </li> <li> <p>Find all the events between May 31, 2016 and June 1, 2016.</p> </li> </ol> </div> </div> </div> </div> <div class="sect1"> <h2 id="olap-spark-yarn">OLAP traversals with Spark on YARN</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop&#8217;s combination of <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sparkgraphcomputer">SparkGraphComputer</a> and <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#_properties_files">HadoopGraph</a> allows for running distributed, analytical graph queries (OLAP) on a computer cluster. The <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sparkgraphcomputer">reference documentation</a> covers the cases where Spark runs locally or where the cluster is managed by a Spark server. However, many users can only run OLAP jobs via the <a href="http://hadoop.apache.org/">Hadoop 3.x</a> Resource Manager (YARN), which requires <code>SparkGraphComputer</code> to be configured differently. This recipe describes this configuration.</p> </div> <div class="sect2"> <h3 id="_approach">Approach</h3> <div class="paragraph"> <p>Most configuration problems of TinkerPop with Spark on YARN stem from three reasons:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>SparkGraphComputer</code> creates its own <code>SparkContext</code> so it does not get any configs from the usual <code>spark-submit</code> command.</p> </li> <li> <p>The TinkerPop Spark plugin did not include Spark on YARN runtime dependencies until version 3.2.7/3.3.1.</p> </li> <li> <p>Resolving reason 2 by adding the cluster&#8217;s Spark jars to the classpath may create all kinds of version conflicts with the TinkerPop dependencies.</p> </li> </ol> </div> <div class="paragraph"> <p>The current recipe follows a minimalist approach in which no dependencies are added to the dependencies included in the TinkerPop binary distribution. The Hadoop cluster&#8217;s Spark installation is completely ignored. This approach minimizes the chance of dependency version conflicts.</p> </div> </div> <div class="sect2"> <h3 id="_prerequisites">Prerequisites</h3> <div class="paragraph"> <p>This recipe is suitable for both a real external and a local pseudo Hadoop cluster. While the recipe is maintained for the vanilla Hadoop pseudo-cluster, it has been reported to work on real clusters with Hadoop distributions from various vendors.</p> </div> <div class="paragraph"> <p>If you want to try the recipe on a local Hadoop pseudo-cluster, the easiest way to install it is to look at the install script at <a href="https://github.com/apache/tinkerpop/blob/3.7.3/docker/hadoop/install.sh">install.sh</a> and the <code>start hadoop</code> section of <a href="https://github.com/apache/tinkerpop/blob/3.7.3/docker/scripts/build.sh">build.sh</a>.</p> </div> <div class="paragraph"> <p>This recipe assumes that you installed the Gremlin Console with the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#spark-plugin">Spark plugin</a> (the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#hadoop-plugin">Hadoop plugin</a> is optional). Your Hadoop cluster may have been configured to use file compression, e.g. LZO compression. If so, you need to copy the relevant jar (e.g. <code>hadoop-lzo-*.jar</code>) to Gremlin Console&#8217;s <code>ext/spark-gremlin/lib</code> folder.</p> </div> <div class="paragraph"> <p>For starting the Gremlin Console in the right environment, create a shell script (e.g. <code>bin/spark-yarn.sh</code>) with the contents below. Of course, actual values for <code>GREMLIN_HOME</code>, <code>HADOOP_HOME</code> and <code>HADOOP_CONF_DIR</code> need to be adapted to your particular environment.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>#!/bin/bash # Variables to be adapted to the actual environment GREMLIN_HOME=/home/yourdir/lib/apache-tinkerpop-gremlin-console-3.7.3-standalone export HADOOP_HOME=/usr/local/lib/hadoop-3.3.1 export HADOOP_CONF_DIR=/usr/local/lib/hadoop-3.3.1/etc/hadoop # Have TinkerPop find the hadoop cluster configs and hadoop native libraries export CLASSPATH=$HADOOP_CONF_DIR export JAVA_OPTIONS=&quot;-Djava.library.path=$HADOOP_HOME/lib/native:$HADOOP_HOME/lib/native/Linux-amd64-64&quot; # Start gremlin-console without getting the HADOOP_GREMLIN_LIBS warning cd $GREMLIN_HOME [ ! -e empty ] &amp;&amp; mkdir empty export HADOOP_GREMLIN_LIBS=$GREMLIN_HOME/empty bin/gremlin.sh</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_running_the_job">Running the job</h3> <div class="paragraph"> <p>You can now run a gremlin OLAP query with Spark on YARN:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>$ hdfs dfs -put data/tinkerpop-modern.kryo . $ . bin/spark-yarn.sh</code></pre> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796894-1" type="radio" name="radio-set-1729796894-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796894-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796894-2" type="radio" name="radio-set-1729796894-1" class="tab-selector-2" /> <label for="tab-1729796894-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; hadoop = <span class="predefined-type">System</span>.getenv(<span class="string"><span class="delimiter">'</span><span class="content">HADOOP_HOME</span><span class="delimiter">'</span></span>) ==&gt;<span class="regexp"><span class="delimiter">/</span><span class="content">home</span><span class="delimiter">/</span></span>ubuntu/.sdkman/candidates/hadoop/current gremlin&gt; hadoopConfDir = <span class="predefined-type">System</span>.getenv(<span class="string"><span class="delimiter">'</span><span class="content">HADOOP_CONF_DIR</span><span class="delimiter">'</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; archive = <span class="string"><span class="delimiter">'</span><span class="content">spark-gremlin.zip</span><span class="delimiter">'</span></span> ==&gt;spark-gremlin.zip gremlin&gt; archivePath = <span class="string"><span class="delimiter">&quot;</span><span class="content">/tmp/</span><span class="inline"><span class="inline-delimiter">$</span>archive</span><span class="delimiter">&quot;</span></span> ==&gt;<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>spark-gremlin.zip gremlin&gt; [<span class="string"><span class="delimiter">'</span><span class="content">bash</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">-c</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">rm -f </span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="content">; cd ext/spark-gremlin/lib &amp;&amp; zip </span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="content"> *.jar</span><span class="delimiter">&quot;</span></span>].execute().waitFor() ==&gt;<span class="integer">0</span> gremlin&gt; conf = <span class="keyword">new</span> Configurations().properties(<span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>)) ==&gt;org.apache.commons.configuration2.PropertiesConfiguration<span class="error">@</span><span class="float">6d</span><span class="integer">87253</span>e gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.master</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">yarn</span><span class="delimiter">'</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.submit.deployMode</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">client</span><span class="delimiter">'</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.yarn.archive</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="delimiter">&quot;</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.yarn.appMasterEnv.CLASSPATH</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">./__spark_libs__/*:</span><span class="inline"><span class="inline-delimiter">$</span>hadoopConfDir</span><span class="delimiter">&quot;</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.executor.extraClassPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">./__spark_libs__/*:</span><span class="inline"><span class="inline-delimiter">$</span>hadoopConfDir</span><span class="delimiter">&quot;</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.driver.extraLibraryPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native:</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native/Linux-amd64-64</span><span class="delimiter">&quot;</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.executor.extraLibraryPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native:</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native/Linux-amd64-64</span><span class="delimiter">&quot;</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.spark.persistContext</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; hdfs.copyFromLocal(<span class="string"><span class="delimiter">'</span><span class="content">data/tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>) ==&gt;<span class="predefined-constant">null</span> gremlin&gt; graph = GraphFactory.open(conf) ==&gt;hadoopgraph[gryoinputformat-&gt;gryooutputformat] gremlin&gt; g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) ==&gt;graphtraversalsource[hadoopgraph[gryoinputformat-&gt;gryooutputformat], sparkgraphcomputer] gremlin&gt; g.V().group().by(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)).by(both().count()) ==&gt;[<span class="key">ripple</span>:<span class="integer">1</span>,<span class="key">peter</span>:<span class="integer">1</span>,<span class="key">vadas</span>:<span class="integer">1</span>,<span class="key">josh</span>:<span class="integer">3</span>,<span class="key">lop</span>:<span class="integer">3</span>,<span class="key">marko</span>:<span class="integer">3</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">hadoop = <span class="predefined-type">System</span>.getenv(<span class="string"><span class="delimiter">'</span><span class="content">HADOOP_HOME</span><span class="delimiter">'</span></span>) hadoopConfDir = <span class="predefined-type">System</span>.getenv(<span class="string"><span class="delimiter">'</span><span class="content">HADOOP_CONF_DIR</span><span class="delimiter">'</span></span>) archive = <span class="string"><span class="delimiter">'</span><span class="content">spark-gremlin.zip</span><span class="delimiter">'</span></span> archivePath = <span class="string"><span class="delimiter">&quot;</span><span class="content">/tmp/</span><span class="inline"><span class="inline-delimiter">$</span>archive</span><span class="delimiter">&quot;</span></span> [<span class="string"><span class="delimiter">'</span><span class="content">bash</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">-c</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">rm -f </span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="content">; cd ext/spark-gremlin/lib &amp;&amp; zip </span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="content"> *.jar</span><span class="delimiter">&quot;</span></span>].execute().waitFor() conf = <span class="keyword">new</span> Configurations().properties(<span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>)) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.master</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">yarn</span><span class="delimiter">'</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.submit.deployMode</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">client</span><span class="delimiter">'</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.yarn.archive</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>archivePath</span><span class="delimiter">&quot;</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.yarn.appMasterEnv.CLASSPATH</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">./__spark_libs__/*:</span><span class="inline"><span class="inline-delimiter">$</span>hadoopConfDir</span><span class="delimiter">&quot;</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.executor.extraClassPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">./__spark_libs__/*:</span><span class="inline"><span class="inline-delimiter">$</span>hadoopConfDir</span><span class="delimiter">&quot;</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.driver.extraLibraryPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native:</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native/Linux-amd64-64</span><span class="delimiter">&quot;</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">spark.executor.extraLibraryPath</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native:</span><span class="inline"><span class="inline-delimiter">$</span>hadoop</span><span class="content">/lib/native/Linux-amd64-64</span><span class="delimiter">&quot;</span></span>) conf.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.spark.persistContext</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>) hdfs.copyFromLocal(<span class="string"><span class="delimiter">'</span><span class="content">data/tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>) graph = GraphFactory.open(conf) g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) g.V().group().by(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)).by(both().count())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>If you run into exceptions, you will have to dig into the logs. You can do this from the command line with <code>yarn application -list -appStates ALL</code> to find the <code>applicationId</code>, while the logs are available with <code>yarn logs -applicationId application_1498627870374_0008</code>. Alternatively, you can inspect the logs via the YARN Resource Manager UI (e.g. http://rm.your.domain:8088/cluster), provided that YARN was configured with the <code>yarn.log-aggregation-enable</code> property set to <code>true</code>. See the Spark documentation for <a href="https://spark.apache.org/docs/latest/running-on-yarn.html#debugging-your-application">additional hints</a>.</p> </div> </div> <div class="sect2"> <h3 id="_explanation">Explanation</h3> <div class="paragraph"> <p>This recipe does not require running the <code>bin/hadoop/init-tp-spark.sh</code> script described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sparkgraphcomputer">reference documentation</a> and thus is also valid for cluster users without access permissions to do so.</p> </div> <div class="paragraph"> <p>Rather, it exploits the <code>spark.yarn.archive</code> property, which points to an archive with jars on the local file system and is loaded into the various YARN containers. As a result the <code>spark-gremlin.zip</code> archive becomes available as the directory named <code>__spark_libs__</code> in the YARN containers. The <code>spark.executor.extraClassPath</code> and <code>spark.yarn.appMasterEnv.CLASSPATH</code> properties point to the jars inside this directory. This is why they contain the <code>./__spark_lib__/*</code> item. Just because a Spark executor got the archive with jars loaded into its container, does not mean it knows how to access them.</p> </div> <div class="paragraph"> <p>Also the <code>HADOOP_GREMLIN_LIBS</code> mechanism is not used because it can not work for Spark on YARN as implemented (jars added to the <code>SparkContext</code> are not available to the YARN application master).</p> </div> <div class="paragraph"> <p>The <code>gremlin.spark.persistContext</code> property is explained in the reference documentation of <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#sparkgraphcomputer">SparkGraphComputer</a>: it helps in getting follow-up OLAP queries answered faster, because you skip the overhead for getting resources from YARN.</p> </div> </div> <div class="sect2"> <h3 id="_additional_configuration_options">Additional configuration options</h3> <div class="paragraph"> <p>This recipe does most of the graph configuration in the Gremlin Console so that environment variables can be used and the chance of configuration mistakes is minimal. Once you have your setup working, it is probably easier to make a copy of the <code>conf/hadoop/hadoop-gryo.properties</code> file and put the property values specific to your environment there. This is also the right moment to take a look at the <code>spark-defaults.xml</code> file of your cluster, in particular the settings for the <a href="https://spark.apache.org/docs/latest/monitoring.html">Spark History Service</a>, which allows you to access logs of finished applications via the YARN resource manager UI.</p> </div> <div class="paragraph"> <p>This recipe uses the Gremlin Console, but things should not be very different for your own JVM-based application, as long as you do not use the <code>spark-submit</code> or <code>spark-shell</code> commands. You will also want to check the additional runtime dependencies listed in the <code>Gremlin-Plugin-Dependencies</code> section of the manifest file in the <code>spark-gremlin</code> jar.</p> </div> <div class="paragraph"> <p>You may not like the idea that the Hadoop and Spark jars from the TinkerPop distribution differ from the versions in your cluster. If so, just build TinkerPop from source with the corresponding dependencies changed in the various <code>pom.xml</code> files (e.g. <code>spark-core_2.11-2.2.0-some-vendor.jar</code> instead of <code>spark-core_2.11-2.2.0.jar</code>). Of course, TinkerPop will only build for exactly matching or slightly differing artifact versions.</p> </div> </div> </div> </div> <h1 id="_anti_patterns" class="sect0">Anti-Patterns</h1> <div class="openblock partintro"> <div class="content"> <span class="image"><img src="../images/gremlin-anti-gremlin.png" alt="gremlin anti gremlin" width="250"></span> </div> </div> <div class="sect1"> <h2 id="long-traversals">Long Traversals</h2> <div class="sectionbody"> <div class="paragraph"> <p>It can be tempting to generate long traversals, e.g. to create a set of vertices and edges based on information that resides within an application. For example, let&#8217;s consider two lists - one that contains information about persons and another that contains information about the relationship between these persons. To illustrate the problem we will create two list with a few random map entries.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-1" type="radio" name="radio-set-1729796922-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-2" type="radio" name="radio-set-1729796922-1" class="tab-selector-2" /> <label for="tab-1729796922-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; :set max-iteration <span class="integer">10</span> gremlin&gt; rnd = <span class="keyword">new</span> <span class="predefined-type">Random</span>(<span class="integer">123</span>) ; x = <span class="type">[]</span> gremlin&gt; persons = (<span class="integer">1</span>..<span class="integer">100</span>).collect {[<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>: <span class="local-variable">it</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">person </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>: rnd.nextInt(<span class="integer">40</span>) + <span class="integer">20</span>]} ==&gt;[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">name</span>:person <span class="integer">1</span>,<span class="key">age</span>:<span class="integer">42</span>] ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">name</span>:person <span class="integer">2</span>,<span class="key">age</span>:<span class="integer">30</span>] ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">name</span>:person <span class="integer">3</span>,<span class="key">age</span>:<span class="integer">36</span>] ==&gt;[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">name</span>:person <span class="integer">4</span>,<span class="key">age</span>:<span class="integer">49</span>] ==&gt;[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">name</span>:person <span class="integer">5</span>,<span class="key">age</span>:<span class="integer">55</span>] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">name</span>:person <span class="integer">6</span>,<span class="key">age</span>:<span class="integer">37</span>] ==&gt;[<span class="key">id</span>:<span class="integer">7</span>,<span class="key">name</span>:person <span class="integer">7</span>,<span class="key">age</span>:<span class="integer">54</span>] ==&gt;[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">name</span>:person <span class="integer">8</span>,<span class="key">age</span>:<span class="integer">57</span>] ==&gt;[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">name</span>:person <span class="integer">9</span>,<span class="key">age</span>:<span class="integer">45</span>] ==&gt;[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">name</span>:person <span class="integer">10</span>,<span class="key">age</span>:<span class="integer">33</span>] ... gremlin&gt; relations = (<span class="integer">1</span>..<span class="integer">500</span>).collect {[rnd.nextInt(persons.size()), rnd.nextInt(persons.size())]}. unique().grep {<span class="local-variable">it</span>[<span class="integer">0</span>] != <span class="local-variable">it</span>[<span class="integer">1</span>] &amp;&amp; !x.contains(<span class="local-variable">it</span>.reverse())}.collect { x &lt;&lt; <span class="local-variable">it</span> minAge = <span class="predefined-type">Math</span>.min(persons[<span class="local-variable">it</span>[<span class="integer">0</span>]].age, persons[<span class="local-variable">it</span>[<span class="integer">1</span>]].age) knowsSince = <span class="keyword">new</span> <span class="predefined-type">Date</span>().year + <span class="integer">1900</span> - rnd.nextInt(minAge) [<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>: persons[<span class="local-variable">it</span>[<span class="integer">0</span>]].id, <span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>: persons[<span class="local-variable">it</span>[<span class="integer">1</span>]].id, <span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>: knowsSince] } ==&gt;[<span class="key">from</span>:<span class="integer">21</span>,<span class="key">to</span>:<span class="integer">11</span>,<span class="key">since</span>:<span class="integer">2020</span>] ==&gt;[<span class="key">from</span>:<span class="integer">59</span>,<span class="key">to</span>:<span class="integer">61</span>,<span class="key">since</span>:<span class="integer">2018</span>] ==&gt;[<span class="key">from</span>:<span class="integer">1</span>,<span class="key">to</span>:<span class="integer">37</span>,<span class="key">since</span>:<span class="integer">2016</span>] ==&gt;[<span class="key">from</span>:<span class="integer">83</span>,<span class="key">to</span>:<span class="integer">45</span>,<span class="key">since</span>:<span class="integer">2012</span>] ==&gt;[<span class="key">from</span>:<span class="integer">21</span>,<span class="key">to</span>:<span class="integer">51</span>,<span class="key">since</span>:<span class="integer">2020</span>] ==&gt;[<span class="key">from</span>:<span class="integer">27</span>,<span class="key">to</span>:<span class="integer">5</span>,<span class="key">since</span>:<span class="integer">2017</span>] ==&gt;[<span class="key">from</span>:<span class="integer">77</span>,<span class="key">to</span>:<span class="integer">89</span>,<span class="key">since</span>:<span class="integer">2021</span>] ==&gt;[<span class="key">from</span>:<span class="integer">16</span>,<span class="key">to</span>:<span class="integer">44</span>,<span class="key">since</span>:<span class="integer">2016</span>] ==&gt;[<span class="key">from</span>:<span class="integer">95</span>,<span class="key">to</span>:<span class="integer">53</span>,<span class="key">since</span>:<span class="integer">1977</span>] ==&gt;[<span class="key">from</span>:<span class="integer">20</span>,<span class="key">to</span>:<span class="integer">22</span>,<span class="key">since</span>:<span class="integer">2022</span>] ... gremlin&gt; [ <span class="string"><span class="delimiter">&quot;</span><span class="content">Number of persons</span><span class="delimiter">&quot;</span></span>: persons.size() , <span class="string"><span class="delimiter">&quot;</span><span class="content">Number of unique relationships</span><span class="delimiter">&quot;</span></span>: relations.size() ] ==&gt;<span class="predefined-type">Number</span> of persons=<span class="integer">100</span> ==&gt;<span class="predefined-type">Number</span> of unique relationships=<span class="integer">478</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">:set max-iteration <span class="integer">10</span> rnd = <span class="keyword">new</span> <span class="predefined-type">Random</span>(<span class="integer">123</span>) ; x = <span class="type">[]</span> persons = (<span class="integer">1</span>..<span class="integer">100</span>).collect {[<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>: <span class="local-variable">it</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">person </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>: rnd.nextInt(<span class="integer">40</span>) + <span class="integer">20</span>]} relations = (<span class="integer">1</span>..<span class="integer">500</span>).collect {[rnd.nextInt(persons.size()), rnd.nextInt(persons.size())]}. unique().grep {<span class="local-variable">it</span>[<span class="integer">0</span>] != <span class="local-variable">it</span>[<span class="integer">1</span>] &amp;&amp; !x.contains(<span class="local-variable">it</span>.reverse())}.collect { x &lt;&lt; <span class="local-variable">it</span> minAge = <span class="predefined-type">Math</span>.min(persons[<span class="local-variable">it</span>[<span class="integer">0</span>]].age, persons[<span class="local-variable">it</span>[<span class="integer">1</span>]].age) knowsSince = <span class="keyword">new</span> <span class="predefined-type">Date</span>().year + <span class="integer">1900</span> - rnd.nextInt(minAge) [<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>: persons[<span class="local-variable">it</span>[<span class="integer">0</span>]].id, <span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>: persons[<span class="local-variable">it</span>[<span class="integer">1</span>]].id, <span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>: knowsSince] } [ <span class="string"><span class="delimiter">&quot;</span><span class="content">Number of persons</span><span class="delimiter">&quot;</span></span>: persons.size() , <span class="string"><span class="delimiter">&quot;</span><span class="content">Number of unique relationships</span><span class="delimiter">&quot;</span></span>: relations.size() ]</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Now, to create the <code>person</code> vertices and the <code>knows</code> edges between them it may look like a good idea to generate a single graph-mutating traversal, just like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-3" type="radio" name="radio-set-1729796922-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-4" type="radio" name="radio-set-1729796922-3" class="tab-selector-2" /> <label for="tab-1729796922-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; t = g ==&gt;graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin&gt; <span class="keyword">for</span> (person <span class="keyword">in</span> persons) { t = t.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). property(id, person.id). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, person.name). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, person.age).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>person.id<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>) } ; <span class="type">[]</span> gremlin&gt; <span class="keyword">for</span> (relation <span class="keyword">in</span> relations) { t = t.addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>, relation.since). from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>relation.from<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>). to(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>relation.to<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>) } ; <span class="type">[]</span> gremlin&gt; traversalAsString = org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).translate(t.bytecode).getScript() ; <span class="type">[]</span> gremlin&gt; [ <span class="string"><span class="delimiter">&quot;</span><span class="content">Traversal String Length</span><span class="delimiter">&quot;</span></span>: traversalAsString.length() , <span class="string"><span class="delimiter">&quot;</span><span class="content">Traversal Preview</span><span class="delimiter">&quot;</span></span>: traversalAsString.replaceFirst(<span class="regexp"><span class="delimiter">/</span><span class="content">^(.{104}).*(.{64})</span><span class="content">$</span><span class="delimiter">/</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">$1 ... $2</span><span class="delimiter">'</span></span>) ] ==&gt;Traversal <span class="predefined-type">String</span> Length=<span class="integer">41086</span> ==&gt;Traversal Preview=g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).property(T.id,(<span class="type">int</span>) <span class="integer">1</span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,person <span class="integer">1</span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>,(<span class="type">int</span>) <span class="integer">42</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p1</span><span class="delimiter">&quot;</span></span>).addV ... addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>,(<span class="type">int</span>) <span class="integer">2010</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p47</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">p48</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">t = g <span class="keyword">for</span> (person <span class="keyword">in</span> persons) { t = t.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). property(id, person.id). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, person.name). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, person.age).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>person.id<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>) } ; <span class="type">[]</span> <span class="keyword">for</span> (relation <span class="keyword">in</span> relations) { t = t.addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>, relation.since). from(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>relation.from<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>). to(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="inline"><span class="inline-delimiter">${</span>relation.to<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>) } ; <span class="type">[]</span> traversalAsString = org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).translate(t.bytecode).getScript() ; <span class="type">[]</span> [ <span class="string"><span class="delimiter">&quot;</span><span class="content">Traversal String Length</span><span class="delimiter">&quot;</span></span>: traversalAsString.length() , <span class="string"><span class="delimiter">&quot;</span><span class="content">Traversal Preview</span><span class="delimiter">&quot;</span></span>: traversalAsString.replaceFirst(<span class="regexp"><span class="delimiter">/</span><span class="content">^(.{104}).*(.{64})</span><span class="content">$</span><span class="delimiter">/</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">$1 ... $2</span><span class="delimiter">'</span></span>) ]</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>However, this kind of traversal does not scale and it&#8217;s prone to produce a <code>StackOverflowError</code>. This error can hardly be prevented as it&#8217;s a limit imposed by the JVM. The stack size can be increased using the <code>-Xss</code> JVM option, but that&#8217;s not how the problem that&#8217;s discussed here, should be solved. The proper way to accomplish the same thing as in the traversal above is to inject the lists into the traversal and process them from there.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-5" type="radio" name="radio-set-1729796922-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-6" type="radio" name="radio-set-1729796922-5" class="tab-selector-2" /> <label for="tab-1729796922-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSideEffect(<span class="string"><span class="delimiter">&quot;</span><span class="content">rels</span><span class="delimiter">&quot;</span></span>, relations). inject(persons).sideEffect( unfold(). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). property(id, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>)). group(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). by(id). by(unfold())). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">rels</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>). from(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>))). to(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>))). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>)).iterate() gremlin&gt; g ==&gt;graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">100</span> <span class="key">edges</span>:<span class="integer">478</span>], standard]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSideEffect(<span class="string"><span class="delimiter">&quot;</span><span class="content">rels</span><span class="delimiter">&quot;</span></span>, relations). inject(persons).sideEffect( unfold(). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>). property(id, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>)). group(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>). by(id). by(unfold())). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">rels</span><span class="delimiter">&quot;</span></span>).unfold().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>). from(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">from</span><span class="delimiter">&quot;</span></span>))). to(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">m</span><span class="delimiter">&quot;</span></span>).select(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">r</span><span class="delimiter">&quot;</span></span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">to</span><span class="delimiter">&quot;</span></span>))). property(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>, select(<span class="string"><span class="delimiter">&quot;</span><span class="content">since</span><span class="delimiter">&quot;</span></span>)).iterate() g</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Obviously, these traversals are more complicated, but the number of steps is known and thus it&#8217;s the best way to prevent an unexpected <code>StackOverflowError</code>. Furthermore, shorter traversals reduce the (de)serialization costs when such a traversal is send over the wire to a Gremlin Server.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Although the example was based on a graph-mutating traversal, the same rules apply for read-only and mixed traversals. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="unspecified-keys-and-labels">Unspecified Keys and Labels</h2> <div class="sectionbody"> <div class="paragraph"> <p>Some Gremlin steps have optional arguments that represent keys (e.g. <code>elementMap()</code>, valueMap()<code>) or labels (e.g. `out()</code>). In the prototyping phase of a projects it&#8217;s often convenient to use these steps without any arguments. However, in production code this is bad idea and keys and labels should always be specified. Not only does it make the traversal easier to read for others, but it also ensures that the application will not break if the schema changes at one point and the queries return completely different results.</p> </div> <div class="paragraph"> <p>The following code block shows a few examples that are good for prototyping or graph discovery.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-7" type="radio" name="radio-set-1729796922-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-8" type="radio" name="radio-set-1729796922-7" class="tab-selector-2" /> <label for="tab-1729796922-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out() ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">4</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">name</span>:lop,<span class="key">lang</span>:java] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ripple</span><span class="delimiter">&quot;</span></span>).inE().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>, gte(<span class="float">0.5</span>)).outV().properties() ==&gt;vp[name-&gt;josh] ==&gt;vp[age-&gt;<span class="integer">32</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out() g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap() g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ripple</span><span class="delimiter">&quot;</span></span>).inE().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>, gte(<span class="float">0.5</span>)).outV().properties()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The next code block shows the same queries, but with specified keys and labels.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-9" type="radio" name="radio-set-1729796922-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-10" type="radio" name="radio-set-1729796922-9" class="tab-selector-2" /> <label for="tab-1729796922-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>) ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">4</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">lang</span><span class="delimiter">&quot;</span></span>) ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">name</span>:lop,<span class="key">lang</span>:java] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ripple</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>, gte(<span class="float">0.5</span>)).outV(). properties(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>) ==&gt;vp[name-&gt;josh] ==&gt;vp[age-&gt;<span class="integer">32</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>) g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">marko</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).elementMap(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">lang</span><span class="delimiter">&quot;</span></span>) g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ripple</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>, gte(<span class="float">0.5</span>)).outV(). properties(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="unnecessary-steps">Unnecessary Steps</h2> <div class="sectionbody"> <div class="paragraph"> <p>There are quite a few steps and patterns that can be combined into a much shorter form. TinkerPop is trying to optimize queries, by rewriting such patterns automatically using traversal optimization strategies. These strategies, however, do have a few preconditions and under certain circumstance they will not attempt to rewrite a traversal. For example, if the traversal has path computations enabled (e.g. by using certain steps, such as <code>path()</code>, <code>simplePath()</code>, <code>otherV()</code>, etc.), then the assumption is that all steps are required in order to produce the desired path.</p> </div> <div class="paragraph"> <p>An often seen anti-pattern is the one that explicitly traverses to an edge and then to a vertex without using any filters.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-11" type="radio" name="radio-set-1729796922-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-12" type="radio" name="radio-set-1729796922-11" class="tab-selector-2" /> <label for="tab-1729796922-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).inV().dedup() <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">5</span>] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).outV().count() <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;<span class="integer">4</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).inV().dedup() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).outV().count() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The <code>created</code> edge is never really needed as the traversal only asks for all things that were created by all persons in the graph. These "things" are only represented by the adjacent vertices, not the edges.</p> </li> <li> <p>This traversals counts the persons in the graph who created software. The interesting thing about this query is that it actually doesn&#8217;t need to traverse all the way to the <code>person</code> vertices to count them. In this case it&#8217;s sufficient to count the edges between the <code>software</code> and <code>person</code> vertices. The performance of this query pretty much depends on the particular provider implementation, but counting incident edges is usually much faster than counting adjacent vertices.</p> </li> </ol> </div> <div class="paragraph"> <p>The next code block shows the two aforementioned queries properly rewritten.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-13" type="radio" name="radio-set-1729796922-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-14" type="radio" name="radio-set-1729796922-13" class="tab-selector-2" /> <label for="tab-1729796922-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).dedup() ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">5</span>] gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).count() ==&gt;<span class="integer">4</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).dedup() g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>).inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).count()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Another anti-pattern that is commonly seen is the chaining of <code>where()</code>-steps using predicates. Consider the following traversal:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-15" type="radio" name="radio-set-1729796922-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-16" type="radio" name="radio-set-1729796922-15" class="tab-selector-2" /> <label for="tab-1729796922-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).where(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;[<span class="key">a</span>:lop,<span class="key">b</span>:marko,<span class="key">c</span>:vadas] ==&gt;[<span class="key">a</span>:josh,<span class="key">b</span>:marko,<span class="key">c</span>:vadas] ==&gt;[<span class="key">a</span>:peter,<span class="key">b</span>:lop,<span class="key">c</span>:josh]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).where(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Ignoring the anti-patterns that were discussed before, there&#8217;s not much wrong with the traversal, but note the two chained <code>where()</code>-steps (<code>where(lt('a')).by(id).where(gt('b'))).by(id)</code>). Both steps compare the id of the current vertex with the id of a previous vertex. These two conditions can be combined on the predicate level.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-17" type="radio" name="radio-set-1729796922-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-18" type="radio" name="radio-set-1729796922-17" class="tab-selector-2" /> <label for="tab-1729796922-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).and(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;[<span class="key">a</span>:lop,<span class="key">b</span>:marko,<span class="key">c</span>:vadas] ==&gt;[<span class="key">a</span>:josh,<span class="key">b</span>:marko,<span class="key">c</span>:vadas] ==&gt;[<span class="key">a</span>:peter,<span class="key">b</span>:lop,<span class="key">c</span>:josh]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).and(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>profile()</code> output of both queries should make clear why this is better than using two <code>where()</code>-steps.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-19" type="radio" name="radio-set-1729796922-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-20" type="radio" name="radio-set-1729796922-19" class="tab-selector-2" /> <label for="tab-1729796922-20" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).where(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). profile() ==&gt;Traversal Metrics Step Count Traversers <span class="predefined-type">Time</span> (ms) % Dur ============================================================================================================= TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a] <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.156</span> <span class="float">10.45</span> VertexStep(BOTH,vertex) <span class="integer">12</span> <span class="integer">12</span> <span class="float">0.094</span> <span class="float">6.32</span> WherePredicateStep(<span class="predefined-constant">null</span>,lt(a),[id])<span class="error">@</span>[b] <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.215</span> <span class="float">14.34</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.039</span> <span class="float">2.66</span> VertexStep(BOTH,vertex) <span class="integer">18</span> <span class="integer">18</span> <span class="float">0.137</span> <span class="float">9.20</span> NotStep([VertexStep(BOTH,vertex), WherePredicat... <span class="integer">15</span> <span class="integer">15</span> <span class="float">0.421</span> <span class="float">28.13</span> VertexStep(BOTH,vertex) <span class="integer">40</span> <span class="integer">40</span> <span class="float">0.130</span> WherePredicateStep(<span class="predefined-constant">null</span>,eq(a)) <span class="float">0.163</span> WherePredicateStep(<span class="predefined-constant">null</span>,lt(a),[id]) <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.191</span> <span class="float">12.76</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.032</span> <span class="float">2.17</span> WherePredicateStep(<span class="predefined-constant">null</span>,gt(b),[id])<span class="error">@</span>[c] <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.071</span> <span class="float">4.76</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.024</span> <span class="float">1.66</span> SelectStep(last,[a, b, c],[value(name)]) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.058</span> <span class="float">3.93</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.054</span> <span class="float">3.63</span> &gt;TOTAL - - <span class="float">1.499</span> - gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).and(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). profile() ==&gt;Traversal Metrics Step Count Traversers <span class="predefined-type">Time</span> (ms) % Dur ============================================================================================================= TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a] <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.124</span> <span class="float">10.49</span> VertexStep(BOTH,vertex) <span class="integer">12</span> <span class="integer">12</span> <span class="float">0.086</span> <span class="float">7.26</span> WherePredicateStep(<span class="predefined-constant">null</span>,lt(a),[id])<span class="error">@</span>[b] <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.153</span> <span class="float">12.87</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.067</span> <span class="float">5.66</span> VertexStep(BOTH,vertex) <span class="integer">18</span> <span class="integer">18</span> <span class="float">0.047</span> <span class="float">4.03</span> NotStep([VertexStep(BOTH,vertex), WherePredicat... <span class="integer">15</span> <span class="integer">15</span> <span class="float">0.344</span> <span class="float">28.97</span> VertexStep(BOTH,vertex) <span class="integer">40</span> <span class="integer">40</span> <span class="float">0.094</span> WherePredicateStep(<span class="predefined-constant">null</span>,eq(a)) <span class="float">0.147</span> WherePredicateStep(<span class="predefined-constant">null</span>,and(lt(a), gt(b)),[id])... <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.212</span> <span class="float">17.85</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.043</span> <span class="float">3.67</span> SelectStep(last,[a, b, c],[value(name)]) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.053</span> <span class="float">4.51</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">3</span> <span class="integer">3</span> <span class="float">0.055</span> <span class="float">4.68</span> &gt;TOTAL - - <span class="float">1.190</span> -</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).where(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). profile() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). both().where(lt(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).and(gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))).by(id).as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). not(both().where(eq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>))). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). profile()</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="unspecified-label-in-global-vertex-lookup">Unspecified Label in Global Vertex lookup</h2> <div class="sectionbody"> <div class="paragraph"> <p>The severity of the anti-pattern described in this section heavily depends on the provider implementation. Throughout the TinkerPop documentation the code samples often use traversals that start like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-21" type="radio" name="radio-set-1729796922-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-22" type="radio" name="radio-set-1729796922-21" class="tab-selector-2" /> <label for="tab-1729796922-22" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) ==&gt;v[<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>This is totally fine for TinkerGraph as it uses a very simplified indexing schema, e.g. every vertex that has a certain property is stored in the same index. However, providers may prefer to use separate indexes for different vertex labels. This becomes more important as graphs grow much larger over time (which is not what TinkerGraph is meant to do). Hence, any traversal that&#8217;s going to be used in production code should also specify the vertex label to prevent the query engine from searching every index for the provided property value.</p> </div> <div class="paragraph"> <p>The easy fix for the initially mentioned query follows in the code block below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-23" type="radio" name="radio-set-1729796922-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-24" type="radio" name="radio-set-1729796922-23" class="tab-selector-2" /> <label for="tab-1729796922-24" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;v[<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>With the specified label the traversal still returns the same result, but it&#8217;s much safer to use across different providers.</p> </li> <li> <p>Same as statement 1, but a much shorter form to improve readability.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="steps-instead-of-tokens">Steps Instead of Tokens</h2> <div class="sectionbody"> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> As of 3.5.0, <code>ByModulatorOptimizationStrategy</code> is present to automatically translate this anti-pattern to their more performant versions for most cases however, it is still best to write Gremlin according to the contents that follow. </td> </tr> </table> </div> <div class="paragraph"> <p>When child traversals contain a single step, there&#8217;s a good chance that the step can be replaced with a token. These tokens are translated into optimized traversals that execute much faster then their step traversal pendants. A few examples of single step child traversals are shown in the following code block.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-25" type="radio" name="radio-set-1729796922-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-26" type="radio" name="radio-set-1729796922-25" class="tab-selector-2" /> <label for="tab-1729796922-26" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().groupCount().by(label()) ==&gt;[<span class="key">software</span>:<span class="integer">2</span>,<span class="key">person</span>:<span class="integer">4</span>] gremlin&gt; g.V().group().by(label()).by(id().fold()) ==&gt;[<span class="key">software</span>:[<span class="integer">3</span>,<span class="integer">5</span>],<span class="key">person</span>:[<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">6</span>]] gremlin&gt; g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">label</span><span class="delimiter">&quot;</span></span>). by(id()). by(label()) ==&gt;[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software] ==&gt;[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person] gremlin&gt; g.V().choose(label()). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>))). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>))) ==&gt;[<span class="key">person</span>:marko] ==&gt;[<span class="key">person</span>:vadas] ==&gt;[<span class="key">product</span>:lop] ==&gt;[<span class="key">person</span>:josh] ==&gt;[<span class="key">product</span>:ripple] ==&gt;[<span class="key">person</span>:peter]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().groupCount().by(label()) g.V().group().by(label()).by(id().fold()) g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">label</span><span class="delimiter">&quot;</span></span>). by(id()). by(label()) g.V().choose(label()). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>))). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).by(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>With tokens used instead of steps the traversals become a little shorter and more readable.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-27" type="radio" name="radio-set-1729796922-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-28" type="radio" name="radio-set-1729796922-27" class="tab-selector-2" /> <label for="tab-1729796922-28" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().groupCount().by(label) ==&gt;[<span class="key">software</span>:<span class="integer">2</span>,<span class="key">person</span>:<span class="integer">4</span>] gremlin&gt; g.V().group().by(label).by(id) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;[<span class="key">software</span>:[<span class="integer">3</span>,<span class="integer">5</span>],<span class="key">person</span>:[<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">6</span>]] gremlin&gt; g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">label</span><span class="delimiter">&quot;</span></span>). by(id). by(label) ==&gt;[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software] ==&gt;[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person] ==&gt;[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person] gremlin&gt; g.V().choose(label). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==&gt;[<span class="key">person</span>:marko] ==&gt;[<span class="key">person</span>:vadas] ==&gt;[<span class="key">product</span>:lop] ==&gt;[<span class="key">person</span>:josh] ==&gt;[<span class="key">product</span>:ripple] ==&gt;[<span class="key">person</span>:peter]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().groupCount().by(label) g.V().group().by(label).by(id) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().project(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">label</span><span class="delimiter">&quot;</span></span>). by(id). by(label) g.V().choose(label). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). option(<span class="string"><span class="delimiter">&quot;</span><span class="content">software</span><span class="delimiter">&quot;</span></span>, project(<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Note, that tokens use a <code>fold()</code> reducer by default.</p> </li> <li> <p><code>by("name")</code> doesn&#8217;t use a token, but falls into the same category as the String <code>"name"</code> is translated into an optimized traversal.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="_has_and_traversal_arguments">has() and Traversal Arguments</h2> <div class="sectionbody"> <div class="paragraph"> <p>There is an understandable assumption that the <code>has(String,Traversal)</code> overload indicates that the value returned by the <code>Traversal</code> argument will be used as the comparative value for the specified property key. There are often similar assumptions that values of <code>P</code> can take a <code>Traversal</code> argument to achieve a similar end as in <code>has(String, eq(Traversal))</code>. Unfortunately, neither of these work as assumed.</p> </div> <div class="paragraph"> <p>Starting with the latter issue of <code>P</code> and <code>Traversal</code> it should be noted that while <code>P</code> values take <code>Object</code> and thus a <code>Traversal</code> it does not mean the <code>Traversal</code> will be resolved to a result that will be comparable. <code>P</code> will rather do a compare on the raw <code>Traversal</code> object which of course will always return <code>false</code> (unless for some odd reason you happen to store that <code>Traversal</code> object in your graph):</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-29" type="radio" name="radio-set-1729796922-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-30" type="radio" name="radio-set-1729796922-29" class="tab-selector-2" /> <label for="tab-1729796922-30" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, eq(constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>))) gremlin&gt; eq(constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) ==&gt;eq([ConstantStep(josh)])</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, eq(constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>))) eq(constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>As for the former issue with <code>has(String,Traversal)</code>, this requires a bit more explanation. The <code>Traversal</code> object is meant to be treated as a <code>Predicate</code>, meaning that if it returns a value the <code>has()</code> will allow the traverser to pass:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-31" type="radio" name="radio-set-1729796922-31" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-31" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-32" type="radio" name="radio-set-1729796922-31" class="tab-selector-2" /> <label for="tab-1729796922-32" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==&gt;v[<span class="integer">1</span>] ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">4</span>] ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">6</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).is(<span class="string"><span class="delimiter">'</span><span class="content">xyz</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, constant(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).is(<span class="string"><span class="delimiter">'</span><span class="content">xyz</span><span class="delimiter">'</span></span>)) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>constant()</code> always returns a value so all vertices pass through the <code>has()</code></p> </li> <li> <p>By adding <code>is()</code> this <code>Traversal</code> will no longer return a value so no vertices pass through the <code>has()</code></p> </li> </ol> </div> <div class="paragraph"> <p>These examples are a bit contrived for sake of demonstration, but the common pattern folks attempt appears as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-33" type="radio" name="radio-set-1729796922-33" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-33" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-34" type="radio" name="radio-set-1729796922-33" class="tab-selector-2" /> <label for="tab-1729796922-34" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)) ==&gt;v[<span class="integer">1</span>] ==&gt;v[<span class="integer">2</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">4</span>] ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">6</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]).V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).select(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above example represents a commonly seen mistake where we try to dynamically inject the value "josh" from a <code>Map</code> stored in a side-effect named "x". As we can see, since <code>select('x').select('name')</code> returns a value the <code>has()</code> succeeds for every single vertex which is unexpected. The correct way to do this dynamic injection is with <code>where()</code> as in the following example:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-35" type="radio" name="radio-set-1729796922-35" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-35" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-36" type="radio" name="radio-set-1729796922-35" class="tab-selector-2" /> <label for="tab-1729796922-36" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]).V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==&gt;v[<span class="integer">4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]).V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,eq(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>)).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>As a final note on this topic, it&#8217;s worth noting how <code>has(String,Traversal)</code> can be used. Note that the traverser that starts the <code>Traversal</code> argument is the <code>Property</code> value being compared. Therefore, if we wanted to find all the vertices that had the "name" of "josh" we would do:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796922-37" type="radio" name="radio-set-1729796922-37" class="tab-selector-1" checked="checked" /> <label for="tab-1729796922-37" class="tab-label-1">console (groovy)</label> <input id="tab-1729796922-38" type="radio" name="radio-set-1729796922-37" class="tab-selector-2" /> <label for="tab-1729796922-38" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, is(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) ==&gt;v[<span class="integer">4</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, is(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> </div> </div> </section> </div> </div> <h1 id="_implementation_recipes" class="sect0">Implementation Recipes</h1> <div class="sect1"> <h2 id="style-guide">Style Guide</h2> <div class="sectionbody"> <div class="paragraph"> <p>The Gremlin Style Guide has been moved and expanded as part of <a href="https://gremlint.com">Gremlint</a>. Please find that style guide <a href="https://tinkerpop.apache.org/gremlint/#/style-guide">here</a>.</p> </div> </div> </div> <div class="sect1"> <h2 id="traversal-component-reuse">Traversal Component Reuse</h2> <div class="sectionbody"> <div class="paragraph"> <p>Good software development practices require reuse to keep software maintainable. In Gremlin, there are often bits of traversal logic that could be represented as components that might be tested independently and utilized as part of other traversals. One approach to doing this would be to extract such logic into an anonymous traversal and provide it to a parent traversal through <code>flatMap()</code>-step.</p> </div> <div class="paragraph"> <p>Using the modern toy graph as an example, assume that there are number of traversals that are interested in filtering on edges where the "weight" property is greater than "0.5". A query like that might look like this:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796938-1" type="radio" name="radio-set-1729796938-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796938-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796938-2" type="radio" name="radio-set-1729796938-1" class="tab-selector-2" /> <label for="tab-1729796938-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV().both() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV().both()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Repeatedly requiring that filter on "weight" could lead to a lot of duplicate code, which becomes difficult to maintain. It would be nice to extract that logic so as to centralize it for reuse in all places where needed. An anonymous traversal allows that to happen and can be created as follows.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796938-3" type="radio" name="radio-set-1729796938-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796938-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796938-4" type="radio" name="radio-set-1729796938-3" class="tab-selector-2" /> <label for="tab-1729796938-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; weightFilter = outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter).both() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">weightFilter = outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> g.V(<span class="integer">1</span>).flatMap(weightFilter).both()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>weightFilter</code> is an anonymous traversal and it is created by way <code>__</code> class. The <code>__</code> is omitted above from initalization of <code>weightFilter</code> because it is statically imported to the Gremlin Console. The <code>weightFilter</code> gets passed to the "full" traversal by way for <code>flatMap()</code>-step and the results are the same. Of course, there is a problem. If there is an attempt to use that <code>weightFilter</code> a second time, the traversal with thrown an exception because both the <code>weightFilter</code> and parent traversal have been "compiled" which prevents their re-use. A simple fix to this would be to clone the <code>weightFilter</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796938-5" type="radio" name="radio-set-1729796938-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796938-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796938-6" type="radio" name="radio-set-1729796938-5" class="tab-selector-2" /> <label for="tab-1729796938-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; weightFilter = outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).both() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).bothE().otherV() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).groupCount() ==&gt;[v[<span class="integer">4</span>]:<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">weightFilter = outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).both() g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).bothE().otherV() g.V(<span class="integer">1</span>).flatMap(weightFilter.clone()).groupCount()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Now the <code>weightFilter</code> can be reused over and over again. Remembering to <code>clone()</code> might lead to yet another maintenance issue in that failing to recall that step would likely result in a bug. One option might be to wrap the <code>weightFilter</code> creation in a function that returns the clone. Another approach might be to parameterize that function to construct a new anonymous traversal each time with the idea being that this might gain even more flexibility in parameterizing the anonymous traversal itself.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796938-7" type="radio" name="radio-set-1729796938-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796938-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796938-8" type="radio" name="radio-set-1729796938-7" class="tab-selector-2" /> <label for="tab-1729796938-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; weightFilter = { w -&gt; outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(w)).inV() } ==&gt;groovysh_evaluate<span class="error">$</span>_run_closure1<span class="annotation">@b75f3f4</span> gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).both() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).bothE().otherV() ==&gt;v[<span class="integer">5</span>] ==&gt;v[<span class="integer">3</span>] ==&gt;v[<span class="integer">1</span>] gremlin&gt; g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).groupCount() ==&gt;[v[<span class="integer">4</span>]:<span class="integer">1</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">weightFilter = { w -&gt; outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">knows</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, P.gt(w)).inV() } g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).both() g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).bothE().otherV() g.V(<span class="integer">1</span>).flatMap(weightFilter(<span class="float">0.5d</span>)).groupCount()</code></pre> </div> </div> </div> </div> </section> </div> </div> <h1 id="contributing" class="sect0">How to Contribute a Recipe</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>Recipes are generated under the same system as all TinkerPop documentation and is stored directly in the source code repository. TinkerPop documentation is all <a href="http://asciidoc.org/">asciidoc</a> based and can be generated locally with either <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/developer/#building-testing">shell script/Maven</a> or <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/developer/#docker-integration">Docker</a> build commands. Once changes are complete, submit a pull request for review by TinkerPop committers.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Please review existing recipes and attempt to conform to their writing and visual style. It may also be a good idea to discuss ideas for a recipe on the <a href="https://lists.apache.org/list.html?dev@tinkerpop.apache.org">developer mailing list</a> prior to starting work on it, as the community might provide insight on the approach and idea that would be helpful. It is preferable that a <a href="https://issues.apache.org/jira/browse/TINKERPOP">JIRA issue</a> be opened that describes the nature of the recipe so that the eventual pull request can be bound to that issue. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Please read TinkerPop&#8217;s <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/developer/#_contributing">policy on contributing</a> prior to submitting a recipe. </td> </tr> </table> </div> <div class="paragraph"> <p>To contribute a recipe, first clone the repository:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">git clone https://github.com/apache/tinkerpop.git</code></pre> </div> </div> <div class="paragraph"> <p>The recipes can be found in this directory:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">ls docs/src/recipes</code></pre> </div> </div> <div class="paragraph"> <p>Each recipe exists within a separate <code>.asciidoc</code> file. The file name should match the name of the recipe. Recipe names should be short, but descriptive (as they need to fit in the left-hand table of contents when generated). The <code>index.asciidoc</code> is the parent document that "includes" the content of each individual recipe file. A recipe file is included in the <code>index.asciidoc</code> with an entry like this: <code>include::my-recipe.asciidoc[]</code></p> </div> <div class="paragraph"> <p>Documentation should be generated locally for review prior to submitting a pull request. TinkerPop documentation is "live" in that it is bound to a specific version when generated. Furthermore, code examples (those that are <code>gremlin-groovy</code> based) are executed at document generation time with the results written directly into the output. The following command will generate the documentation with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">bin/process-docs.sh</code></pre> </div> </div> <div class="paragraph"> <p>The generated documentation can be found at <code>target/docs/htmlsingle/recipes</code>. This process can be long on the first run of the documentation as it is generating all of the documentation locally (e.g. reference documentation, tutorials, etc). To generate just the recipes, follow this process:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">bin/process-docs.sh -f docs/src/recipes</code></pre> </div> </div> <div class="paragraph"> <p>The <code>bin/process-docs.sh</code> approach requires that Hadoop is installed. To avoid that prerequisite, try using Docker:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker/build.sh -d</code></pre> </div> </div> <div class="paragraph"> <p>The downside to using Docker is that the process will take longer as each run will require the entire documentation set to be generated.</p> </div> <div class="paragraph"> <p>The final step to submitting a recipe is to issue a <a href="https://help.github.com/articles/using-pull-requests/">pull request through GitHub</a>. It is helpful to prefix the name of the pull request with the JIRA issue number, so that TinkerPop&#8217;s automation between GitHub and JIRA are linked. As mentioned earlier in this section, the recipe will go under review by TinkerPop committers prior to merging. This process may take several days to complete. We look forward to receiving your submissions!</p> </div> </div> </div> <h1 id="_appendix" class="sect0">Appendix</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>Many of the recipes are based on questions and answers provided on the <a href="https://groups.google.com/forum/#!forum/gremlin-users">gremlin-users mailing list</a> or on <a href="http://stackoverflow.com/questions/tagged/gremlin">StackOverflow</a>. This section contains those traversals from those sources that do not easily fit any particular pattern (i.e. a recipe), but are nonetheless interesting and thus remain good tools for learning Gremlin.</p> </div> <div id="appendix-a" class="paragraph"> <p><em>For each person in a "follows" graph, determine the number of followers and list their names.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-1" type="radio" name="radio-set-1729796948-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-2" type="radio" name="radio-set-1729796948-1" class="tab-selector-2" /> <label for="tab-1729796948-2" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).iterate() gremlin&gt; g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). map(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()). project(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>). by(select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). by(). by(count(local)) ==&gt;[<span class="key">person</span>:marko,<span class="key">followers</span>:[josh,daniel],<span class="key">numFollowers</span>:<span class="integer">2</span>] ==&gt;[<span class="key">person</span>:josh,<span class="key">followers</span>:[matthias,daniel],<span class="key">numFollowers</span>:<span class="integer">2</span>] ==&gt;[<span class="key">person</span>:daniel,<span class="key">followers</span>:<span class="type">[]</span>,<span class="key">numFollowers</span>:<span class="integer">0</span>] ==&gt;[<span class="key">person</span>:matthias,<span class="key">followers</span>:<span class="type">[]</span>,<span class="key">numFollowers</span>:<span class="integer">0</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>). addV().property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">matthias</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).iterate() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>). map(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()). project(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>). by(select(<span class="string"><span class="delimiter">'</span><span class="content">p</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). by(). by(count(local))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It might also be alternatively written as:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-3" type="radio" name="radio-set-1729796948-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-4" type="radio" name="radio-set-1729796948-3" class="tab-selector-2" /> <label for="tab-1729796948-4" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().group(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(project(<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).count()). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold())).next() ==&gt;daniel={numFollowers=<span class="integer">0</span>, followers=<span class="type">[]</span>} ==&gt;matthias={numFollowers=<span class="integer">0</span>, followers=<span class="type">[]</span>} ==&gt;josh={numFollowers=<span class="integer">2</span>, followers=[matthias, daniel]} ==&gt;marko={numFollowers=<span class="integer">2</span>, followers=[josh, daniel]}</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().group(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(project(<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).count()). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold())).next()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>or even:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-5" type="radio" name="radio-set-1729796948-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-6" type="radio" name="radio-set-1729796948-5" class="tab-selector-2" /> <label for="tab-1729796948-6" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().group(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold(). project(<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>). by(count(local)). by()).next() ==&gt;daniel={numFollowers=<span class="integer">0</span>, followers=<span class="type">[]</span>} ==&gt;matthias={numFollowers=<span class="integer">0</span>, followers=<span class="type">[]</span>} ==&gt;josh={numFollowers=<span class="integer">2</span>, followers=[matthias, daniel]} ==&gt;marko={numFollowers=<span class="integer">2</span>, followers=[josh, daniel]}</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().group(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">follows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold(). project(<span class="string"><span class="delimiter">'</span><span class="content">numFollowers</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">followers</span><span class="delimiter">'</span></span>). by(count(local)). by()).next()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-b" class="paragraph"> <p><em>In the "modern" graph, show each person, the software they worked on and the co-worker count for the software and the names of those co-workers.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-7" type="radio" name="radio-set-1729796948-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-8" type="radio" name="radio-set-1729796948-7" class="tab-selector-2" /> <label for="tab-1729796948-8" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). map(__.in(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>)).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).fold()). group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">numCoworkers</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">coworkers</span><span class="delimiter">&quot;</span></span>). by(count(local)).by())).next() ==&gt;peter={lop={numCoworkers=<span class="integer">2</span>, coworkers=[marko, josh]}} ==&gt;josh={ripple={numCoworkers=<span class="integer">0</span>, coworkers=<span class="type">[]</span>}, lop={numCoworkers=<span class="integer">2</span>, coworkers=[marko, peter]}} ==&gt;marko={lop={numCoworkers=<span class="integer">2</span>, coworkers=[josh, peter]}}</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">person</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). map(__.in(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>)).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).fold()). group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">p</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">numCoworkers</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">coworkers</span><span class="delimiter">&quot;</span></span>). by(count(local)).by())).next()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-c" class="paragraph"> <p><em>Assuming a graph of students, classes and times, detect students who have a conflicting schedule.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-9" type="radio" name="radio-set-1729796948-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-10" type="radio" name="radio-set-1729796948-9" class="tab-selector-2" /> <label for="tab-1729796948-10" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Pete</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Joe</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Java's GC</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">FP Principles</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Memory Management in C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Memory Management in C++</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">timeslot</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11/25/2016</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">10:00</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11:00</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">timeslot</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11/25/2016</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11:00</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12:00</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>)). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>). where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>)). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>).by(elementMap(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>))). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>).dedup().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).fold())).next() ==&gt;Pete={{id=<span class="integer">12</span>, label=timeslot, fromTime=<span class="integer">10</span>:<span class="octal">00</span>, toTime=<span class="integer">11</span>:<span class="octal">00</span>}=[Java<span class="string"><span class="delimiter">'</span><span class="content">s GC, FP Principles], {id=16, label=timeslot, fromTime=11:00, toTime=12:00}=[Memory Management in C, Memory Management in C++, Java</span><span class="delimiter">'</span></span>s GC]}</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Pete</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Joe</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Java's GC</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">FP Principles</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Memory Management in C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">class</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Memory Management in C++</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">timeslot</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11/25/2016</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">10:00</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11:00</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">timeslot</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11/25/2016</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">11:00</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">12:00</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">s2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c1</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c2</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t1</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c3</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c4</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">t2</span><span class="delimiter">&quot;</span></span>).iterate() g.V().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">student</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">attends</span><span class="delimiter">&quot;</span></span>). where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>)). out(<span class="string"><span class="delimiter">&quot;</span><span class="content">allocated</span><span class="delimiter">&quot;</span></span>). where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>)). group(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">s</span><span class="delimiter">&quot;</span></span>).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(group().by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">t</span><span class="delimiter">&quot;</span></span>).by(elementMap(<span class="string"><span class="delimiter">&quot;</span><span class="content">fromTime</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">toTime</span><span class="delimiter">&quot;</span></span>))). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>).dedup().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>).fold())).next()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-d" class="paragraph"> <p><em>In the "modern" graph, with a duplicate edge added, find the vertex pairs that have more than one edge between them.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-11" type="radio" name="radio-set-1729796948-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-12" type="radio" name="radio-set-1729796948-11" class="tab-selector-2" /> <label for="tab-1729796948-12" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.4d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>) ==&gt;e[<span class="integer">0</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] gremlin&gt; g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). out().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). groupCount(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). unfold(). filter(select(values).is(gt(<span class="integer">1</span>))). select(keys) ==&gt;[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).V(<span class="integer">3</span>).addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>,<span class="float">0.4d</span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() g.V(<span class="integer">1</span>).outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">created</span><span class="delimiter">&quot;</span></span>) g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). out().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). groupCount(). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). unfold(). filter(select(values).is(gt(<span class="integer">1</span>))). select(keys)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The following example assumes that the edges point in the <code>OUT</code> direction. Assuming undirected edges:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-13" type="radio" name="radio-set-1729796948-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-14" type="radio" name="radio-set-1729796948-13" class="tab-selector-2" /> <label for="tab-1729796948-14" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). filter(bothE().where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).otherV().where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>))).aggregate(local, <span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).dedup()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().where(without(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>)).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). outE().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). filter(bothE().where(neq(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>)).otherV().where(eq(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>))).aggregate(local, <span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).dedup()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-e" class="paragraph"> <p><em>In the "crew" graph, find vertices that match on a complete set of multi-properties.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-15" type="radio" name="radio-set-1729796948-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-16" type="radio" name="radio-set-1729796948-15" class="tab-selector-2" /> <label for="tab-1729796948-16" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; places = [<span class="string"><span class="delimiter">&quot;</span><span class="content">centreville</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">dulles</span><span class="delimiter">&quot;</span></span>];<span class="type">[]</span> <span class="comment">// will not match as &quot;purcellville&quot; is missing</span> gremlin&gt; g.V().not(has(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>, without(places))). where(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>).is(within(places)).count().is(places.size())). elementMap() gremlin&gt; places = [<span class="string"><span class="delimiter">&quot;</span><span class="content">centreville</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">dulles</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">purcellville</span><span class="delimiter">&quot;</span></span>];<span class="type">[]</span> gremlin&gt; g.V().not(has(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>, without(places))). where(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>).is(within(places)).count().is(places.size())). elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">7</span>,<span class="key">label</span>:person,<span class="key">name</span>:stephen,<span class="key">location</span>:purcellville]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">places = [<span class="string"><span class="delimiter">&quot;</span><span class="content">centreville</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">dulles</span><span class="delimiter">&quot;</span></span>];<span class="type">[]</span> <span class="comment">// will not match as &quot;purcellville&quot; is missing</span> g.V().not(has(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>, without(places))). where(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>).is(within(places)).count().is(places.size())). elementMap() places = [<span class="string"><span class="delimiter">&quot;</span><span class="content">centreville</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">dulles</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">purcellville</span><span class="delimiter">&quot;</span></span>];<span class="type">[]</span> g.V().not(has(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>, without(places))). where(values(<span class="string"><span class="delimiter">&quot;</span><span class="content">location</span><span class="delimiter">&quot;</span></span>).is(within(places)).count().is(places.size())). elementMap()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-f" class="paragraph"> <p><em>Methods for performing some basic mathematical operations in the "modern" graph.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-17" type="radio" name="radio-set-1729796948-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-18" type="radio" name="radio-set-1729796948-17" class="tab-selector-2" /> <label for="tab-1729796948-18" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sum() <span class="comment">// sum all ages</span> ==&gt;<span class="integer">123</span> gremlin&gt; g.V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold(<span class="integer">1</span>, mult) <span class="comment">// multiply all ages</span> ==&gt;<span class="integer">876960</span> gremlin&gt; g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(sum).by(constant(-<span class="integer">1</span>)).sack() <span class="comment">// subtract 1</span> ==&gt;<span class="integer">28</span> ==&gt;<span class="integer">26</span> ==&gt;<span class="integer">31</span> ==&gt;<span class="integer">34</span> gremlin&gt; g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(sum).sack() <span class="comment">// multiply by 2 (simple)</span> ==&gt;<span class="integer">58</span> ==&gt;<span class="integer">54</span> ==&gt;<span class="integer">64</span> ==&gt;<span class="integer">70</span> gremlin&gt; g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(mult).by(constant(<span class="integer">2</span>)).sack() <span class="comment">// multiply by 2 (generally useful for multiplications by n)</span> ==&gt;<span class="integer">58</span> ==&gt;<span class="integer">54</span> ==&gt;<span class="integer">64</span> ==&gt;<span class="integer">70</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sum() <span class="comment">// sum all ages</span> g.V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).fold(<span class="integer">1</span>, mult) <span class="comment">// multiply all ages</span> g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(sum).by(constant(-<span class="integer">1</span>)).sack() <span class="comment">// subtract 1</span> g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(sum).sack() <span class="comment">// multiply by 2 (simple)</span> g.withSack(<span class="integer">0</span>).V().values(<span class="string"><span class="delimiter">&quot;</span><span class="content">age</span><span class="delimiter">&quot;</span></span>).sack(sum).sack(mult).by(constant(<span class="integer">2</span>)).sack() <span class="comment">// multiply by 2 (generally useful for multiplications by n)</span></code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><em>Method for doing a sum with division.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-19" type="radio" name="radio-set-1729796948-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-20" type="radio" name="radio-set-1729796948-19" class="tab-selector-2" /> <label for="tab-1729796948-20" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; g.withSack(<span class="float">0d</span>). V(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). V(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). by(inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)).count()). by(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).where(inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)).count()). sack(sum).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>). sack(div).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(a,b)</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(b,a)</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(a,b) / #(b,a)</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). by(sack()) ==&gt;[<span class="key">a</span>:v[a],<span class="key">b</span>:v[b],<span class="error">#</span>(a,b):<span class="integer">1</span>,<span class="error">#</span>(b,a):<span class="integer">2</span>,<span class="error">#</span>(a,b) / <span class="error">#</span>(b,a):<span class="float">0.5</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(id, <span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).iterate() g.withSack(<span class="float">0d</span>). V(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). V(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). by(inE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).where(outV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)).count()). by(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">link</span><span class="delimiter">&quot;</span></span>).where(inV().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)).count()). sack(sum).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>). sack(div).by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(a,b)</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(b,a)</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">#(a,b) / #(b,a)</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ab</span><span class="delimiter">&quot;</span></span>). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">ba</span><span class="delimiter">&quot;</span></span>). by(sack())</code></pre> </div> </div> </div> </div> </section> <div id="appendix-g" class="paragraph"> <p><em>Dropping a vertex, as well as the vertices related to that dropped vertex that are connected by a "knows" edge in the "modern" graph</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-21" type="radio" name="radio-set-1729796948-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-22" type="radio" name="radio-set-1729796948-21" class="tab-selector-2" /> <label for="tab-1729796948-22" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).outE() ==&gt;e[<span class="integer">9</span>][<span class="integer">1</span>-created-&gt;<span class="integer">3</span>] ==&gt;e[<span class="integer">7</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">2</span>] ==&gt;e[<span class="integer">8</span>][<span class="integer">1</span>-knows-&gt;<span class="integer">4</span>] gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).sideEffect(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).drop()).drop() gremlin&gt; g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) gremlin&gt; g.V(<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">3</span>) ==&gt;v[<span class="integer">3</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).outE() g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).sideEffect(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).drop()).drop() g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) g.V(<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">3</span>)</code></pre> </div> </div> </div> </div> </section> <div id="appendix-h" class="paragraph"> <p><em>For the specified graph, find all neighbor vertices connected to "A" as filtered by datetime, those neighbor vertices that don&#8217;t have datetime vertices, and those neighbor vertices that have the label "dimon".</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-23" type="radio" name="radio-set-1729796948-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-24" type="radio" name="radio-set-1729796948-23" class="tab-selector-2" /> <label for="tab-1729796948-24" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">B</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">D</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">E</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">F</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">f</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">G</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="integer">20160818</span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">H</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="integer">20160817</span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">f</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="delimiter">&quot;</span></span>).iterate() gremlin&gt; <span class="comment">// D and E have a valid datetime</span> ==&gt;<span class="predefined-constant">true</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, gte(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:B] ==&gt;[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:dimon,<span class="key">name</span>:F] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:D] ==&gt;[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:E] gremlin&gt; <span class="comment">// only E has a valid date</span> ==&gt;<span class="predefined-constant">true</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, lte(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:B] ==&gt;[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:dimon,<span class="key">name</span>:F] ==&gt;[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:E] gremlin&gt; <span class="comment">// only D has a valid date</span> ==&gt;<span class="predefined-constant">true</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, gt(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:B] ==&gt;[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:dimon,<span class="key">name</span>:F] ==&gt;[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:D] gremlin&gt; <span class="comment">// neither D nor E have a valid date</span> ==&gt;<span class="predefined-constant">true</span> gremlin&gt; g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, lt(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() ==&gt;[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:vertex,<span class="key">name</span>:B] ==&gt;[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:dimon,<span class="key">name</span>:F]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">B</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">C</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">D</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">E</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addV(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">F</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">f</span><span class="delimiter">&quot;</span></span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">G</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="integer">20160818</span>). addV().property(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">H</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="delimiter">&quot;</span></span>).property(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, <span class="integer">20160817</span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">f</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">d</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">g</span><span class="delimiter">&quot;</span></span>). addE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).from(<span class="string"><span class="delimiter">&quot;</span><span class="content">e</span><span class="delimiter">&quot;</span></span>).to(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="delimiter">&quot;</span></span>).iterate() <span class="comment">// D and E have a valid datetime</span> g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, gte(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() <span class="comment">// only E has a valid date</span> g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, lte(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() <span class="comment">// only D has a valid date</span> g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, gt(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap() <span class="comment">// neither D nor E have a valid date</span> g.V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">A</span><span class="delimiter">&quot;</span></span>).out(<span class="string"><span class="delimiter">&quot;</span><span class="content">rel</span><span class="delimiter">&quot;</span></span>). union(where(out(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">date</span><span class="delimiter">&quot;</span></span>, lt(<span class="integer">20160817</span>))), __.not(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">occured_at</span><span class="delimiter">&quot;</span></span>)).coalesce(out().hasLabel(<span class="string"><span class="delimiter">&quot;</span><span class="content">dimon</span><span class="delimiter">&quot;</span></span>), identity())). elementMap()</code></pre> </div> </div> </div> </div> </section> <div id="appendix-i" class="paragraph"> <p><em>Use element labels in a <code>select</code>.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-25" type="radio" name="radio-set-1729796948-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-26" type="radio" name="radio-set-1729796948-25" class="tab-selector-2" /> <label for="tab-1729796948-26" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). both(). map(group().by(label).by(unfold())).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). map(union(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)), select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). unfold(). group(). by(select(keys)). by(select(values))) ==&gt;[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">software</span>:v[<span class="integer">3</span>]] ==&gt;[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">person</span>:v[<span class="integer">2</span>]] ==&gt;[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">person</span>:v[<span class="integer">4</span>]] gremlin&gt; g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). both(). map(group().by(label).by(unfold())).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). group(). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). group(). by(select(keys)). by(select(values).fold())). unfold(). map(union(select(keys).project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).by(), select(values)). unfold(). group(). by(select(keys).unfold()). by(select(values).unfold().unfold().fold())) ==&gt;[<span class="key">a</span>:[v[<span class="integer">1</span>]],<span class="key">software</span>:[v[<span class="integer">3</span>]],<span class="key">person</span>:[v[<span class="integer">2</span>],v[<span class="integer">4</span>]]] ==&gt;[<span class="key">a</span>:[v[<span class="integer">2</span>]],<span class="key">person</span>:[v[<span class="integer">1</span>]]] ==&gt;[<span class="key">a</span>:[v[<span class="integer">3</span>]],<span class="key">person</span>:[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">6</span>]]] ==&gt;[<span class="key">a</span>:[v[<span class="integer">4</span>]],<span class="key">software</span>:[v[<span class="integer">5</span>],v[<span class="integer">3</span>]],<span class="key">person</span>:[v[<span class="integer">1</span>]]] ==&gt;[<span class="key">a</span>:[v[<span class="integer">5</span>]],<span class="key">person</span>:[v[<span class="integer">4</span>]]] ==&gt;[<span class="key">a</span>:[v[<span class="integer">6</span>]],<span class="key">software</span>:[v[<span class="integer">3</span>]]]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). both(). map(group().by(label).by(unfold())).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). map(union(project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>)), select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). unfold(). group(). by(select(keys)). by(select(values))) g.V().as(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). both(). map(group().by(label).by(unfold())).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). group(). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>). group(). by(select(keys)). by(select(values).fold())). unfold(). map(union(select(keys).project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).by(), select(values)). unfold(). group(). by(select(keys).unfold()). by(select(values).unfold().unfold().fold()))</code></pre> </div> </div> </div> </div> </section> <div id="appendix-j" class="paragraph"> <p><em>Sum edge weight with a coefficient.</em></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796948-27" type="radio" name="radio-set-1729796948-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729796948-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729796948-28" type="radio" name="radio-set-1729796948-27" class="tab-selector-2" /> <label for="tab-1729796948-28" class="tab-label-2">groovy</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin&gt; g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">9</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">8</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">7</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">6</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">9</span>).iterate() gremlin&gt; g.withSack(<span class="float">1.0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). repeat(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rates</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">tag</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ruby</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). by(inV()). by(sack()). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">value</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). sack(mult).by(constant(<span class="float">0.5</span>))). times(<span class="integer">3</span>).emit(). select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">score</span><span class="delimiter">&quot;</span></span>). by(tail(local, <span class="integer">1</span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(unfold(). sack(assign).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). sack(mult).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>)). sack().sum()) ==&gt;[<span class="key">name</span>:bobby,<span class="key">score</span>:<span class="float">9.0</span>] ==&gt;[<span class="key">name</span>:cindy,<span class="key">score</span>:<span class="float">13.00</span>] ==&gt;[<span class="key">name</span>:david,<span class="key">score</span>:<span class="float">14.750</span>]</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-2"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>). addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">9</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">bobby</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">8</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">cindy</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">7</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">david</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">ruby</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">6</span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">rates</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">alice</span><span class="delimiter">'</span></span>).to(<span class="string"><span class="delimiter">'</span><span class="content">eliza</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">tag</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">java</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">value</span><span class="delimiter">'</span></span>,<span class="integer">9</span>).iterate() g.withSack(<span class="float">1.0</span>).V().has(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">alice</span><span class="delimiter">&quot;</span></span>). repeat(outE(<span class="string"><span class="delimiter">&quot;</span><span class="content">rates</span><span class="delimiter">&quot;</span></span>).has(<span class="string"><span class="delimiter">&quot;</span><span class="content">tag</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">ruby</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>). by(inV()). by(sack()). by(<span class="string"><span class="delimiter">&quot;</span><span class="content">value</span><span class="delimiter">&quot;</span></span>).as(<span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>). sack(mult).by(constant(<span class="float">0.5</span>))). times(<span class="integer">3</span>).emit(). select(all, <span class="string"><span class="delimiter">&quot;</span><span class="content">x</span><span class="delimiter">&quot;</span></span>). project(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">score</span><span class="delimiter">&quot;</span></span>). by(tail(local, <span class="integer">1</span>).select(<span class="string"><span class="delimiter">&quot;</span><span class="content">a</span><span class="delimiter">&quot;</span></span>).values(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>)). by(unfold(). sack(assign).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">b</span><span class="delimiter">&quot;</span></span>)). sack(mult).by(select(<span class="string"><span class="delimiter">&quot;</span><span class="content">c</span><span class="delimiter">&quot;</span></span>)). sack().sum())</code></pre> </div> </div> </div> </div> </section> </div> </div> </div> <div id="footer"> <div id="footer-text"> Last updated 2024-10-24 12:31:41 -0700 </div> </div> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10