CINXE.COM
TinkerPop Documentation
<!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>TinkerPop Documentation</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="#_tinkerpop_documentation">TinkerPop Documentation</a></li> <li><a href="#preface">Preface</a> <ul class="sectlevel1"> <li><a href="#_tinkerpop0">TinkerPop0</a></li> <li><a href="#_tinkerpop1">TinkerPop1</a></li> <li><a href="#_tinkerpop2">TinkerPop2</a></li> <li><a href="#_tinkerpop3">TinkerPop3</a></li> </ul> </li> <li><a href="#intro">Introduction</a> <ul class="sectlevel1"> <li><a href="#graph-computing">Graph Computing</a> <ul class="sectlevel2"> <li><a href="#graph-structure">The Graph Structure</a></li> <li><a href="#the-graph-process">The Graph Process</a> <ul class="sectlevel3"> <li><a href="#_the_traverser">The Traverser</a></li> </ul> </li> </ul> </li> <li><a href="#connecting-gremlin">Connecting Gremlin</a> <ul class="sectlevel2"> <li><a href="#connecting-embedded">Embedded</a></li> <li><a href="#connecting-gremlin-server">Gremlin Server</a> <ul class="sectlevel3"> <li><a href="#connecting-gremlin-server-limitations">Limitations</a></li> </ul> </li> <li><a href="#connecting-rgp">Remote Gremlin Provider</a></li> </ul> </li> <li><a href="#basic-gremlin">Basic Gremlin</a></li> <li><a href="#staying-agnostic">Staying Agnostic</a></li> </ul> </li> <li><a href="#graph">The Graph</a> <ul class="sectlevel1"> <li><a href="#_features">Features</a></li> <li><a href="#vertex-properties">Vertex Properties</a></li> <li><a href="#_graph_variables">Graph Variables</a></li> <li><a href="#_namespace_conventions">Namespace Conventions</a></li> </ul> </li> <li><a href="#traversal">The Traversal</a> <ul class="sectlevel1"> <li><a href="#transactions">Traversal Transactions</a> <ul class="sectlevel2"> <li><a href="#tx-embedded">Embedded</a> <ul class="sectlevel3"> <li><a href="#_configuring">Configuring</a></li> </ul> </li> <li><a href="#tx-gremlin-server">Gremlin Server</a></li> <li><a href="#tx-rgp">Remote Gremlin Providers</a></li> </ul> </li> <li><a href="#configuration-steps">Configuration Steps</a> <ul class="sectlevel2"> <li><a href="#configuration-steps-with">With Configuration</a></li> <li><a href="#configuration-steps-withbulk">WithBulk Configuration</a></li> <li><a href="#configuration-steps-withcomputer">WithComputer Configuration</a></li> <li><a href="#configuration-steps-withsack">WithSack Configuration</a></li> <li><a href="#configuration-steps-withsideeffect">WithSideEffect Configuration</a></li> <li><a href="#configuration-steps-withstrategies">WithStrategies Configuration</a></li> <li><a href="#configuration-steps-withoutstrategies">WithoutStrategies Configuration</a></li> </ul> </li> <li><a href="#start-steps">Start Steps</a></li> <li><a href="#graph-traversal-steps">Graph Traversal Steps</a> <ul class="sectlevel2"> <li><a href="#general-steps">General Steps</a></li> <li><a href="#terminal-steps">Terminal Steps</a></li> <li><a href="#addedge-step">AddE Step</a></li> <li><a href="#addvertex-step">AddV Step</a></li> <li><a href="#aggregate-step">Aggregate Step</a></li> <li><a href="#all-step">All Step</a></li> <li><a href="#and-step">And Step</a></li> <li><a href="#any-step">Any Step</a></li> <li><a href="#as-step">As Step</a></li> <li><a href="#asString-step">AsString Step</a></li> <li><a href="#asDate-step">AsDate Step</a></li> <li><a href="#barrier-step">Barrier Step</a></li> <li><a href="#branch-step">Branch Step</a></li> <li><a href="#by-step">By Step</a></li> <li><a href="#call-step">Call Step</a></li> <li><a href="#cap-step">Cap Step</a></li> <li><a href="#choose-step">Choose Step</a></li> <li><a href="#coalesce-step">Coalesce Step</a></li> <li><a href="#coin-step">Coin Step</a></li> <li><a href="#combine-step">Combine Step</a></li> <li><a href="#concat-step">Concat Step</a></li> <li><a href="#conjoin-step">Conjoin Step</a></li> <li><a href="#connectedcomponent-step">ConnectedComponent Step</a></li> <li><a href="#constant-step">Constant Step</a></li> <li><a href="#count-step">Count Step</a></li> <li><a href="#cyclicpath-step">CyclicPath Step</a></li> <li><a href="#dateAdd-step">DateAdd Step</a></li> <li><a href="#dateDiff-step">DateDiff Step</a></li> <li><a href="#dedup-step">Dedup Step</a></li> <li><a href="#difference-step">Difference Step</a></li> <li><a href="#disjunct-step">Disjunct Step</a></li> <li><a href="#drop-step">Drop Step</a></li> <li><a href="#e-step">E Step</a></li> <li><a href="#element-step">Element Step</a></li> <li><a href="#elementmap-step">ElementMap Step</a></li> <li><a href="#emit-step">Emit Step</a></li> <li><a href="#explain-step">Explain Step</a></li> <li><a href="#fail-step">Fail Step</a></li> <li><a href="#filter-step">Filter Step</a></li> <li><a href="#flatmap-step">FlatMap Step</a></li> <li><a href="#format-step">Format Step</a></li> <li><a href="#fold-step">Fold Step</a></li> <li><a href="#from-step">From Step</a></li> <li><a href="#group-step">Group Step</a></li> <li><a href="#groupcount-step">GroupCount Step</a></li> <li><a href="#has-step">Has Step</a></li> <li><a href="#id-step">Id Step</a></li> <li><a href="#identity-step">Identity Step</a></li> <li><a href="#index-step">Index Step</a></li> <li><a href="#inject-step">Inject Step</a></li> <li><a href="#intersect-step">Intersect Step</a></li> <li><a href="#io-step">IO Step</a> <ul class="sectlevel3"> <li><a href="#graphml">GraphML</a></li> <li><a href="#graphson">GraphSON</a></li> <li><a href="#gryo">Gryo</a></li> </ul> </li> <li><a href="#is-step">Is Step</a></li> <li><a href="#key-step">Key Step</a></li> <li><a href="#label-step">Label Step</a></li> <li><a href="#length-step">Length Step</a></li> <li><a href="#limit-step">Limit Step</a></li> <li><a href="#local-step">Local Step</a></li> <li><a href="#loops-step">Loops Step</a></li> <li><a href="#lTrim-step">LTrim Step</a></li> <li><a href="#map-step">Map Step</a></li> <li><a href="#match-step">Match Step</a> <ul class="sectlevel3"> <li><a href="#using-where-with-match">Using Where with Match</a></li> </ul> </li> <li><a href="#math-step">Math Step</a></li> <li><a href="#max-step">Max Step</a></li> <li><a href="#mean-step">Mean Step</a></li> <li><a href="#merge-step">Merge Step</a></li> <li><a href="#mergeedge-step">MergeEdge Step</a></li> <li><a href="#mergevertex-step">MergeVertex Step</a></li> <li><a href="#min-step">Min Step</a></li> <li><a href="#none-step">None Step</a></li> <li><a href="#not-step">Not Step</a></li> <li><a href="#option-step">Option Step</a></li> <li><a href="#optional-step">Optional Step</a></li> <li><a href="#or-step">Or Step</a></li> <li><a href="#order-step">Order Step</a></li> <li><a href="#pagerank-step">PageRank Step</a></li> <li><a href="#path-step">Path Step</a> <ul class="sectlevel3"> <li><a href="#path-data-structure">Path Data Structure</a></li> </ul> </li> <li><a href="#peerpressure-step">PeerPressure Step</a></li> <li><a href="#product-step">Product Step</a></li> <li><a href="#profile-step">Profile Step</a></li> <li><a href="#project-step">Project Step</a></li> <li><a href="#program-step">Program Step</a></li> <li><a href="#properties-step">Properties Step</a></li> <li><a href="#property-step">Property Step</a></li> <li><a href="#propertymap-step">PropertyMap Step</a></li> <li><a href="#range-step">Range Step</a></li> <li><a href="#read-step">Read Step</a></li> <li><a href="#repeat-step">Repeat Step</a></li> <li><a href="#replace-step">Replace Step</a></li> <li><a href="#reverse-step">Reverse Step</a></li> <li><a href="#rTrim-step">RTrim Step</a></li> <li><a href="#sack-step">Sack Step</a></li> <li><a href="#sample-step">Sample Step</a></li> <li><a href="#select-step">Select Step</a> <ul class="sectlevel3"> <li><a href="#using-where-with-select">Using Where with Select</a></li> </ul> </li> <li><a href="#shortestpath-step">ShortestPath step</a></li> <li><a href="#sideeffect-step">SideEffect Step</a></li> <li><a href="#simplepath-step">SimplePath Step</a></li> <li><a href="#skip-step">Skip Step</a></li> <li><a href="#split-step">Split Step</a></li> <li><a href="#subgraph-step">Subgraph Step</a></li> <li><a href="#substring-step">Substring Step</a></li> <li><a href="#sum-step">Sum Step</a></li> <li><a href="#tail-step">Tail Step</a></li> <li><a href="#timelimit-step">TimeLimit Step</a></li> <li><a href="#to-step">To Step</a></li> <li><a href="#toLower-step">ToLower Step</a></li> <li><a href="#toUpper-step">ToUpper Step</a></li> <li><a href="#tree-step">Tree Step</a></li> <li><a href="#trim-step">Trim Step</a></li> <li><a href="#unfold-step">Unfold Step</a></li> <li><a href="#union-step">Union Step</a></li> <li><a href="#until-step">Until Step</a></li> <li><a href="#v-step">V Step</a></li> <li><a href="#value-step">Value Step</a></li> <li><a href="#valuemap-step">ValueMap Step</a></li> <li><a href="#values-step">Values Step</a></li> <li><a href="#vertex-steps">Vertex Steps</a></li> <li><a href="#where-step">Where Step</a></li> <li><a href="#with-step">With Step</a></li> <li><a href="#write-step">Write Step</a></li> </ul> </li> <li><a href="#a-note-on-predicates">A Note on Predicates</a></li> <li><a href="#a-note-on-barrier-steps">A Note on Barrier Steps</a></li> <li><a href="#a-note-on-scopes">A Note on Scopes</a></li> <li><a href="#a-note-on-lambdas">A Note On Lambdas</a></li> <li><a href="#traversalstrategy">TraversalStrategy</a> <ul class="sectlevel2"> <li><a href="#_application">Application</a></li> <li><a href="#_definition">Definition</a></li> <li><a href="#_edgelabelverificationstrategy">EdgeLabelVerificationStrategy</a></li> <li><a href="#_elementidstrategy">ElementIdStrategy</a></li> <li><a href="#_eventstrategy">EventStrategy</a></li> <li><a href="#partitionstrategy">PartitionStrategy</a></li> <li><a href="#readonlystrategy">ReadOnlyStrategy</a></li> <li><a href="#_reservedkeysverificationstrategy">ReservedKeysVerificationStrategy</a></li> <li><a href="#_seedstrategy">SeedStrategy</a></li> <li><a href="#subraphstrategy">SubgraphStrategy</a></li> <li><a href="#_vertexprogramdenystrategy">VertexProgramDenyStrategy</a></li> </ul> </li> <li><a href="#dsl">Domain Specific Languages</a></li> <li><a href="#translators">Translators</a></li> </ul> </li> <li><a href="#graphcomputer">The GraphComputer</a> <ul class="sectlevel1"> <li><a href="#vertexprogram">VertexProgram</a></li> <li><a href="#mapreduce">MapReduce</a></li> <li><a href="#_a_collection_of_vertexprograms">A Collection of VertexPrograms</a> <ul class="sectlevel2"> <li><a href="#pagerankvertexprogram">PageRankVertexProgram</a></li> <li><a href="#peerpressurevertexprogram">PeerPressureVertexProgram</a></li> <li><a href="#connectedcomponentvertexprogram">ConnectedComponentVertexProgram</a></li> <li><a href="#shortestpathvertexprogram">ShortestPathVertexProgram</a></li> <li><a href="#clonevertexprogram">CloneVertexProgram</a></li> <li><a href="#traversalvertexprogram">TraversalVertexProgram</a> <ul class="sectlevel3"> <li><a href="#distributed-gremlin-gotchas">Distributed Gremlin Gotchas</a></li> </ul> </li> </ul> </li> <li><a href="#graph-filter">Graph Filter</a></li> </ul> </li> <li><a href="#gremlin-applications">Gremlin Applications</a> <ul class="sectlevel1"> <li><a href="#gremlin-console">Gremlin Console</a> <ul class="sectlevel2"> <li><a href="#_console_commands">Console Commands</a></li> <li><a href="#_interrupting_evaluations">Interrupting Evaluations</a></li> <li><a href="#console-preferences">Console Preferences</a></li> <li><a href="#_dependencies_and_plugin_usage">Dependencies and Plugin Usage</a></li> <li><a href="#execution-mode">Execution Mode</a></li> <li><a href="#interactive-mode">Interactive Mode</a></li> <li><a href="#gremlin-console-docker-image">Docker Image</a></li> </ul> </li> <li><a href="#gremlin-server">Gremlin Server</a> <ul class="sectlevel2"> <li><a href="#starting-gremlin-server">Starting Gremlin Server</a></li> <li><a href="#connecting-via-drivers">Connecting via Drivers</a></li> <li><a href="#connecting-via-console">Connecting via Console</a> <ul class="sectlevel3"> <li><a href="#console-aliases">Aliases</a></li> <li><a href="#console-sessions">Sessions</a></li> <li><a href="#console-remote-console">Remote Console</a></li> </ul> </li> <li><a href="#connecting-via-http">Connecting via HTTP</a></li> <li><a href="#_configuring_2">Configuring</a> <ul class="sectlevel3"> <li><a href="#opprocessor-configurations">OpProcessor Configurations</a></li> <li><a href="#_serialization">Serialization</a></li> <li><a href="#metrics">Metrics</a></li> <li><a href="#_as_a_service">As A Service</a></li> </ul> </li> <li><a href="#security">Security</a> <ul class="sectlevel3"> <li><a href="#_plain_text_authentication">Plain text authentication</a></li> <li><a href="#credentials-dsl">Credentials Graph DSL</a></li> <li><a href="#krb5authenticator">Kerberos Authentication</a></li> <li><a href="#authorization">Authorization</a></li> <li><a href="#script-execution">Protecting Script Execution</a></li> </ul> </li> <li><a href="#_best_practices">Best Practices</a> <ul class="sectlevel3"> <li><a href="#_tuning">Tuning</a></li> <li><a href="#parameterized-scripts">Parameterized Scripts</a></li> <li><a href="#_properties_of_elements">Properties of Elements</a></li> <li><a href="#gremlin-server-cache">Cache Management</a></li> <li><a href="#sessions">Considering Sessions</a></li> <li><a href="#considering-transactions">Considering Transactions</a></li> <li><a href="#considering-state">Considering State</a></li> <li><a href="#request-retry">Request Retry</a></li> </ul> </li> <li><a href="#gremlin-server-docker-image">Docker Image</a></li> </ul> </li> <li><a href="#gremlin-plugins">Gremlin Plugins</a> <ul class="sectlevel2"> <li><a href="#credentials-plugin">Credentials Plugin</a></li> <li><a href="#gephi-plugin">Gephi Plugin</a></li> <li><a href="#graph-plugins">Graph Plugins</a></li> <li><a href="#hadoop-plugin">Hadoop Plugin</a></li> <li><a href="#server-plugin">Server Plugin</a></li> <li><a href="#spark-plugin">Spark Plugin</a></li> <li><a href="#sugar-plugin">Sugar Plugin</a> <ul class="sectlevel3"> <li><a href="#_graph_traversal_methods">Graph Traversal Methods</a></li> <li><a href="#_range_queries">Range Queries</a></li> <li><a href="#_logical_operators">Logical Operators</a></li> <li><a href="#_traverser_methods">Traverser Methods</a></li> </ul> </li> <li><a href="#utilities-plugin">Utilities Plugin</a> <ul class="sectlevel3"> <li><a href="#describe-graph">Describe Graph</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a href="#gremlin-drivers-variants">Gremlin Drivers and Variants</a> <ul class="sectlevel1"> <li><a href="#gremlin-go">Gremlin-Go</a> <ul class="sectlevel2"> <li><a href="#gremlin-go-connecting">Connecting</a></li> <li><a href="#gremlin-go-imports">Common Imports</a></li> <li><a href="#gremlin-go-configuration">Configuration</a></li> <li><a href="#gremlin-go-strategies">Traversal Strategies</a></li> <li><a href="#gremlin-go-transactions">Transactions</a></li> <li><a href="#gremlin-go-lambda">The Lambda Solution</a></li> <li><a href="#gremlin-go-scripts">Submitting Scripts</a> <ul class="sectlevel3"> <li><a href="#_per_request_settings">Per Request Settings</a></li> </ul> </li> <li><a href="#gremlin-go-dsl">Domain Specific Languages</a></li> <li><a href="#gremlin-go-differences">Differences</a></li> <li><a href="#gremlin-go-aliases">Aliases</a> <ul class="sectlevel3"> <li><a href="#_list_of_useful_aliases">List of useful aliases</a></li> </ul> </li> <li><a href="#gremlin-go-limitations">Limitations</a></li> <li><a href="#gremlin-go-examples">Application Examples</a></li> </ul> </li> <li><a href="#gremlin-groovy">Gremlin-Groovy</a> <ul class="sectlevel2"> <li><a href="#gremlin-groovy-differences">Differences</a></li> </ul> </li> <li><a href="#gremlin-java">Gremlin-Java</a> <ul class="sectlevel2"> <li><a href="#gremlin-java-connecting">Connecting</a></li> <li><a href="#gremlin-java-imports">Common Imports</a></li> <li><a href="#gremlin-java-configuration">Configuration</a></li> <li><a href="#gremlin-java-transactions">Transactions</a></li> <li><a href="#gremlin-java-serialization">Serialization</a></li> <li><a href="#gremlin-java-lambda">The Lambda Solution</a></li> <li><a href="#gremlin-java-scripts">Submitting Scripts</a> <ul class="sectlevel3"> <li><a href="#_per_request_settings_2">Per Request Settings</a></li> <li><a href="#_aliases">Aliases</a></li> </ul> </li> <li><a href="#gremlin-java-dsl">Domain Specific Languages</a></li> <li><a href="#gremlin-java-troubleshooting">Troubleshooting</a></li> <li><a href="#gremlin-java-archetypes">Application Archetypes</a></li> <li><a href="#gremlin-java-examples">Application Examples</a></li> </ul> </li> <li><a href="#gremlin-javascript">Gremlin-JavaScript</a> <ul class="sectlevel2"> <li><a href="#gremlin-javascript-connecting">Connecting</a></li> <li><a href="#gremlin-javascript-imports">Common Imports</a></li> <li><a href="#gremlin-javascript-configuration">Configuration</a></li> <li><a href="#gremlin-javascript-transactions">Transactions</a></li> <li><a href="#gremlin-javascript-lambda">The Lambda Solution</a></li> <li><a href="#gremlin-javascript-scripts">Submitting Scripts</a> <ul class="sectlevel3"> <li><a href="#_per_request_settings_3">Per Request Settings</a></li> <li><a href="#_processing_results_as_they_are_returned_from_the_gremlin_server">Processing results as they are returned from the Gremlin server</a></li> </ul> </li> <li><a href="#gremlin-javascript-dsl">Domain Specific Languages</a></li> <li><a href="#gremlin-javascript-differences">Differences</a></li> <li><a href="#gremlin-javascript-limitations">Limitations</a></li> <li><a href="#gremlin-javascript-examples">Application Examples</a></li> </ul> </li> <li><a href="#gremlin-dotnet">Gremlin.Net</a> <ul class="sectlevel2"> <li><a href="#gremlin-dotnet-connecting">Connecting</a></li> <li><a href="#gremlin-dotnet-imports">Common Imports</a></li> <li><a href="#gremlin-dotnet-configuration">Configuration</a> <ul class="sectlevel3"> <li><a href="#_connection_pool">Connection Pool</a></li> <li><a href="#_websocket_configuration">WebSocket Configuration</a></li> </ul> </li> <li><a href="#gremlin-dotnet-logging">Logging</a></li> <li><a href="#gremlin-dotnet-serialization">Serialization</a></li> <li><a href="#gremlin-dotnet-strategies">Traversal Strategies</a></li> <li><a href="#gremlin-dotnet-transactions">Transactions</a></li> <li><a href="#gremlin-dotnet-lambda">The Lambda Solution</a></li> <li><a href="#gremlin-dotnet-scripts">Submitting Scripts</a> <ul class="sectlevel3"> <li><a href="#_per_request_settings_4">Per Request Settings</a></li> </ul> </li> <li><a href="#gremlin-dotnet-dsl">Domain Specific Languages</a></li> <li><a href="#gremlin-dotnet-differences">Differences</a></li> <li><a href="#gremlin-dotnet-limitations">Limitations</a></li> <li><a href="#gremlin-dotnet-startup">Getting Started</a></li> <li><a href="#gremlin-dotnet-examples">Application Examples</a></li> </ul> </li> <li><a href="#gremlin-python">Gremlin-Python</a> <ul class="sectlevel2"> <li><a href="#gremlin-python-connecting">Connecting</a></li> <li><a href="#gremlin-python-imports">Common Imports</a></li> <li><a href="#gremlin-python-configuration">Configuration</a></li> <li><a href="#gremlin-python-strategies">Traversal Strategies</a></li> <li><a href="#gremlin-python-transactions">Transactions</a></li> <li><a href="#gremlin-python-lambda">The Lambda Solution</a></li> <li><a href="#gremlin-python-scripts">Submitting Scripts</a> <ul class="sectlevel3"> <li><a href="#_per_request_settings_5">Per Request Settings</a></li> </ul> </li> <li><a href="#gremlin-python-dsl">Domain Specific Languages</a></li> <li><a href="#gremlin-python-sugar">Syntactic Sugar</a></li> <li><a href="#gremlin-python-differences">Differences</a></li> <li><a href="#gremlin-python-limitations">Limitations</a></li> <li><a href="#gremlin-python-examples">Application Examples</a></li> </ul> </li> </ul> </li> <li><a href="#implementations">Implementations</a> <ul class="sectlevel1"> <li><a href="#tinkergraph-gremlin">TinkerGraph-Gremlin</a> <ul class="sectlevel2"> <li><a href="#_data_types">Data Types</a></li> <li><a href="#tinkergraph-configuration">Configuration</a></li> <li><a href="#tinkergraph-gremlin-tx">Transactions</a> <ul class="sectlevel3"> <li><a href="#_transaction_semantics">Transaction Semantics</a></li> <li><a href="#testing-remote-providers">Testing Remote Providers</a></li> <li><a href="#_best_practices_2">Best Practices</a></li> <li><a href="#_performance_considerations">Performance Considerations</a></li> <li><a href="#_examples">Examples</a></li> </ul> </li> </ul> </li> <li><a href="#neo4j-gremlin">Neo4j-Gremlin (Deprecated)</a> <ul class="sectlevel2"> <li><a href="#_indices">Indices</a></li> <li><a href="#_cypher">Cypher</a></li> <li><a href="#_multi_label">Multi-Label</a></li> <li><a href="#_configuration">Configuration</a></li> <li><a href="#_bolt_configuration">Bolt Configuration</a></li> <li><a href="#_high_availability_configuration">High Availability Configuration</a></li> </ul> </li> <li><a href="#hadoop-gremlin">Hadoop-Gremlin</a> <ul class="sectlevel2"> <li><a href="#_installing_hadoop_gremlin">Installing Hadoop-Gremlin</a></li> <li><a href="#_properties_files">Properties Files</a></li> <li><a href="#_oltp_hadoop_gremlin">OLTP Hadoop-Gremlin</a></li> <li><a href="#_olap_hadoop_gremlin">OLAP Hadoop-Gremlin</a> <ul class="sectlevel3"> <li><a href="#sparkgraphcomputer">SparkGraphComputer</a></li> </ul> </li> <li><a href="#_inputoutput_formats">Input/Output Formats</a> <ul class="sectlevel3"> <li><a href="#gryo-io-format">Gryo I/O Format</a></li> <li><a href="#graphson-io-format">GraphSON I/O Format</a></li> <li><a href="#script-io-format">Script I/O Format</a></li> </ul> </li> <li><a href="#_storage_systems">Storage Systems</a> <ul class="sectlevel3"> <li><a href="#interacting-with-hdfs">Interacting with HDFS</a></li> <li><a href="#interacting-with-spark">Interacting with Spark</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a href="#compilers">Gremlin Compilers</a> <ul class="sectlevel1"> <li><a href="#sparql-gremlin">SPARQL-Gremlin</a> <ul class="sectlevel2"> <li><a href="#prefixes">Prefixes</a></li> <li><a href="#supported-queries">Supported Queries</a></li> <li><a href="#limitations">Limitations</a></li> <li><a href="#examples">Examples</a> <ul class="sectlevel3"> <li><a href="#_select_all">Select All</a></li> <li><a href="#_match_constant_values">Match Constant Values</a></li> <li><a href="#_select_specific_elements">Select Specific Elements</a></li> <li><a href="#_pattern_matching">Pattern Matching</a></li> <li><a href="#_filtering">Filtering</a></li> <li><a href="#_deduplication">Deduplication</a></li> <li><a href="#_multiple_filters">Multiple Filters</a></li> <li><a href="#_union">Union</a></li> <li><a href="#_optional">Optional</a></li> <li><a href="#_order_by">Order By</a></li> <li><a href="#_group_by">Group By</a></li> <li><a href="#_mixedcomplexaggregation_based_queries">Mixed/complex/aggregation-based queries</a></li> <li><a href="#_meta_property_access">Meta-Property Access</a></li> <li><a href="#_star_shaped_queries">STAR-shaped queries</a></li> </ul> </li> <li><a href="#sparql-with-gremlin">With Gremlin</a></li> </ul> </li> </ul> </li> <li><a href="#conclusion">Conclusion</a></li> <li><a href="#acknowledgements">Acknowledgements</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="_tinkerpop_documentation" class="sect0">TinkerPop Documentation</h1> <h1 id="preface" class="sect0">Preface</h1> <div class="openblock partintro"> <div class="content"> In the beginning…​ </div> </div> <div class="sect1"> <h2 id="_tinkerpop0">TinkerPop0</h2> <div class="sectionbody"> <div class="paragraph"> <p>Gremlin realized. The more he did so, the more ideas he created. The more ideas he created, the more they related. Into a concatenation of that which he accepted wholeheartedly and that which perhaps may ultimately come to be through concerted will, a world took form which was seemingly separate from his own realization of it. However, the world birthed could not bear its own weight without the logic Gremlin had come to accept — the logic of left is not right, up not down, and west far from east unless one goes the other way. Gremlin’s realization required Gremlin’s realization. Perhaps, the world is simply an idea that he once had — The TinkerPop.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-logo.png" alt="gremlin logo" width="300"> </div> </div> </div> </div> <div class="sect1"> <h2 id="_tinkerpop1">TinkerPop1</h2> <div class="sectionbody"> <div class="paragraph"> <p>What is The TinkerPop? Where is The TinkerPop? Who is The TinkerPop? When is The TinkerPop?. The more he wondered, the more these thoughts blurred into a seeming identity — distinctions unclear. Unwilling to accept the morass of the maze he wandered, Gremlin crafted a collection of machines to help hold the fabric together: Blueprints, Pipes, Frames, Furnace, and Rexster. With their help, could Gremlin stave off the thought he was not ready to have? Could he hold back The TinkerPop by searching for The TinkerPop?</p> </div> <div class="literalblock"> <div class="content"> <pre>"If I haven't found it, it is not here and now."</pre> </div> </div> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-and-friends.png" alt="gremlin and friends" width="500"> </div> </div> <div class="paragraph"> <p>Upon their realization of existence, the machines turned to their <a href="http://non-aliencreatures.wikia.com/wiki/Machine_Elf">machine elf</a> creator and asked:</p> </div> <div class="literalblock"> <div class="content"> <pre>"Why am I, what I am?"</pre> </div> </div> <div class="paragraph"> <p>Gremlin responded:</p> </div> <div class="literalblock"> <div class="content"> <pre>"You will help me realize the ultimate realization -- The TinkerPop. The world you find yourself in and the logic that allows you to move about it is because of the TinkerPop."</pre> </div> </div> <div class="paragraph"> <p>The machines wondered:</p> </div> <div class="literalblock"> <div class="content"> <pre>"If what is is the TinkerPop, then perhaps we are The TinkerPop and our realization is simply the realization of the TinkerPop?"</pre> </div> </div> <div class="paragraph"> <p>Would the machines, by their very nature of realizing The TinkerPop, be The TinkerPop? Or, on the same side of the coin, do the machines simply provide the scaffolding by which Gremlin’s world sustains itself and yielding its justification by means of the word "The TinkerPop?" Regardless, it all turns out the same — The TinkerPop.</p> </div> </div> </div> <div class="sect1"> <h2 id="_tinkerpop2">TinkerPop2</h2> <div class="sectionbody"> <div class="paragraph"> <p>Gremlin spoke:</p> </div> <div class="literalblock"> <div class="content"> <pre>"Please listen to what I have to say. I am no closer to The TinkerPop. However, all along The TinkerPop has espoused the form I willed upon it... this is the same form I have willed upon you, my machine friends. Let me train you in the ways of my thought such that it can continue indefinitely."</pre> </div> </div> <div class="imageblock"> <div class="content"> <img src="../images/tinkerpop-reading.png" alt="tinkerpop reading" width="450"> </div> </div> <div class="paragraph"> <p>The machines, simply moving algorithmically through Gremlin’s world, endorsed his logic. Gremlin labored to make them more efficient, more expressive, better capable of reasoning upon his thoughts. Faster, quickly, now towards the world’s end, where there would be forever currently, emanatingly engulfing that which is — The TinkerPop.</p> </div> </div> </div> <div class="sect1"> <h2 id="_tinkerpop3">TinkerPop3</h2> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <img src="../images/tinkerpop3-splash.png" alt="tinkerpop3 splash" width="450"> </div> </div> <div class="paragraph"> <p>Gremlin approached The TinkerPop. The closer he got, the more his world dissolved — west is right, around is straight, and from nothing more than nothing. With each step towards The TinkerPop, more worlds made possible were laid upon his paradoxed mind. Everything is everything in The TinkerPop, and when the dust settled, Gremlin emerged Gremlitron. He realized that all that he realized was just a realization and that all realized realizations are just as real. For that is — The TinkerPop.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gremlintron.png" alt="gremlintron" width="400"> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> For more information about differences between TinkerPop 3.x and earlier versions, please see the <a href="https://tinkerpop.apache.org/docs/3.7.3/upgrade/#appendix">appendix</a>. </td> </tr> </table> </div> </div> </div> <h1 id="intro" class="sect0">Introduction</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>Welcome to the Reference Documentation for Apache TinkerPop™ - the backbone for all details on how to work with TinkerPop and the Gremlin graph traversal language. This documentation is not meant to be a "book", but a source from which to spawn more detailed accounts of specific topics and a target to which all other resources point. The Reference Documentation makes some general assumptions about the reader:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>They have a sense of what a graph is - not sure? see <a href="http://kelvinlawrence.net/book/Gremlin-Graph-Guide.html#whygraph">Practical Gremlin - Why Graph?</a></p> </li> <li> <p>They know what it means for a graph system to be TinkerPop-enabled - not sure? see <a href="https://tinkerpop.apache.org/providers.html">TinkerPop-enabled Providers</a></p> </li> <li> <p>They know what the role of Gremlin is - not sure? see <a href="https://tinkerpop.apache.org/gremlin.html">Introduction to Gremlin</a></p> </li> </ol> </div> <div class="paragraph"> <p>Given those assumptions, it’s possible to dive more quickly into the details without spending a lot of time repeating what is written elsewhere.</p> </div> <div class="paragraph"> <p>It is fairly certain that readers of the Reference Documentation are coming from the most diverse software development backgrounds that TinkerPop has ever engaged in over the decade or so of its existence. While TinkerPop holds some roots in Java, and thus, languages bound to the Java Virtual Machine (JVM), it long ago branched out into other languages such as Python, Javascript, .NET, GO, and others. To compound upon that diversity, it is also seeing extensive support from different graph systems which have chosen TinkerPop as their standard method for allowing users to interface with their graph. Moreover, the graph systems themselves are not only separated by OLTP and OLAP style workloads, but also by their implementation patterns, which range everywhere from being an embedded graph system to a cloud-only graph. One might even find diversity parallel to Gremlin if considering other graph query languages.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-reference.png" alt="gremlin reference" width="1024"> </div> </div> <div class="paragraph"> <p>Despite all this diversity and disparity, Gremlin remains the unifying interface for all these different elements of the graph community. As a user, choosing a TinkerPop-enabled graph and using Gremlin in the correct way when building applications shields them from change and disparity in the space. As a graph provider, choosing to become TinkerPop-enabled not only expands the reach their system can get into different development ecosystems, but also provides access to other query languages through bytecode compilation as seen in <a href="#sparql-gremlin">sparql-gremlin</a>.</p> </div> <div class="paragraph"> <p>Irrespective of the programming language being used, graph system chosen or other development background that might be driving a user to this documentation, the critical point to remember is that "Gremlin is Gremlin is Gremlin". The same Gremlin that is written for an OLTP query over an in-memory TinkerGraph is the same Gremlin that is written to execute over a multi-billion edge graph using OLAP through Spark. That same Gremlin for either of those cases is written in the same way whether using Java or Python or Javascript. The Gremlin is always fundamentally the same aside from syntactical differences that might be language specific - e.g. the construction of a lambda in Groovy is different than the construction of a lambda in Python or a reserved word in Javascript forces a Gremlin step to have slightly different naming than Java.</p> </div> <div class="paragraph"> <p>While learning the Gremlin language and its patterns is largely agnostic to all the diversity in the space, it is not really possible to ignore the impact of the diversity from an application development perspective and the Reference Documentation makes an effort to try to point out where differences and inconsistencies might lie without diving too deeply into specific graph provider implementations. Users are strongly encouraged to consult the documentation of their chosen graph provider to understand all of the capabilities and limitations that may restrict or inhibit usage of certain aspects of TinkerPop APIs which are defined here in this Reference Documentation.</p> </div> <div class="paragraph"> <p>The following introductory sections and separately referenced content will be of varying interest to different readers. The summaries below will hopefully be helpful in directing individuals to the appropriate place to start their learning process.</p> </div> <div class="ulist"> <ul> <li> <p><a href="#graph-computing">Graph Computing</a> is an introduction to what "graph computing" means to TinkerPop and describes many of the provider and user-facing TinkerPop APIs and concepts that enable Gremlin.</p> </li> <li> <p><a href="#connecting-gremlin">Connecting Gremlin</a> provides descriptions for the different modes by which users will connect to graphs depending on their environment.</p> </li> <li> <p><a href="#basic-gremlin">Basic Gremlin</a> describes how to use a connection to start writing Gremlin.</p> </li> <li> <p><a href="#staying-agnostic">Staying Agnostic</a> provides tips on ways to keep Gremlin as portable as possible among different graph providers.</p> </li> </ul> </div> <div class="paragraph"> <p>New users should not ignore TinkerPop’s <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/getting-started/">Getting Started</a> tutorial or <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/the-gremlin-console/">The Gremlin Console</a> tutorial. Both contain a large set of basic information and tips that can help readers avoid some general pitfalls early on. Both also focus on Gremlin usage in the Gremlin Console, which tends to be a critical tool for Gremlin developers of any development background.</p> </div> <div class="paragraph"> <p>More advanced and experience users will appreciate <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/">Gremlin Recipes</a> which provide examples of common Gremlin traversal patterns.</p> </div> <div class="paragraph"> <p>Finally, all Gremlin developers should become familiar with <a href="http://kelvinlawrence.net/book/Gremlin-Graph-Guide.html">"Practical Gremlin"</a> by Kelvin Lawrence. This book is freely available and published online. It contains great examples and details that are applicable to anyone building applications with Gremlin.</p> </div> </div> </div> <div class="sect1"> <h2 id="graph-computing">Graph Computing</h2> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <img src="../images/graph-computing.png" alt="graph computing" width="350"> </div> </div> <div class="paragraph"> <p>A <a href="http://en.wikipedia.org/wiki/Graph_(data_structure)">graph</a> is a data structure composed of vertices (nodes, dots) and edges (arcs, lines). When modeling a graph in a computer and applying it to modern data sets and practices, the generic mathematically-oriented, binary graph is extended to support both labels and key/value properties. This structure is known as a property graph. More formally, it is a directed, binary, attributed multi-graph. An example property graph is diagrammed below.</p> </div> <div id="tinkerpop-modern" class="imageblock"> <div class="content"> <img src="../images/tinkerpop-modern.png" alt="tinkerpop modern" width="500"> </div> <div class="title">Figure 1. TinkerPop Modern</div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Get to know this graph structure as it is used extensively throughout the documentation and in wider circles as well. It is referred to as "TinkerPop Modern" as it is a modern variation of the original demo graph distributed with TinkerPop0 back in 2009 (i.e. the good ol' days — it was the best of times and it was the worst of times). </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> All of the toy graphs available in TinkerPop are described in <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/the-gremlin-console/#toy-graphs">The Gremlin Console</a> tutorial. </td> </tr> </table> </div> <div class="paragraph"> <p>Similar to computing in general, graph computing makes a distinction between <strong>structure</strong> (graph) and <strong>process</strong> (traversal). The structure of the graph is the data model defined by a vertex/edge/property <a href="http://en.wikipedia.org/wiki/Network_topology">topology</a>. The process of the graph is the means by which the structure is analyzed. The typical form of graph processing is called a <a href="http://en.wikipedia.org/wiki/Graph_traversal">traversal</a>.</p> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/tinkerpop-enabled.png" alt="tinkerpop enabled" width="135"></span> TinkerPop’s role in graph computing is to provide the appropriate interfaces for <a href="https://tinkerpop.apache.org/providers.html">graph providers</a> and users to interact with graphs over their structure and process. When a graph system implements the TinkerPop structure and process <a href="http://en.wikipedia.org/wiki/Application_programming_interface">APIs</a>, their technology is considered <em>TinkerPop-enabled</em> and becomes nearly indistinguishable from any other TinkerPop-enabled graph system save for their respective time and space complexity. The purpose of this documentation is to describe the structure/process dichotomy at length and in doing so, explain how to leverage TinkerPop for the sole purpose of graph system-agnostic graph computing.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> TinkerPop is licensed under the popular <a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache2</a> free software license. However, note that the underlying graph engine used with TinkerPop may have a different license. Thus, be sure to respect the license caveats of the graph system product. </td> </tr> </table> </div> <div class="paragraph"> <p>Generally speaking, the structure or "graph" API is meant for <a href="https://tinkerpop.apache.org/providers.html">graph providers</a> who are implementing the TinkerPop interfaces and the process or "traversal" API (i.e. Gremlin) is meant for end-users who are utilizing a graph system from a graph provider. While the components of the process API are itemized below, they are described in greater detail in the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/gremlins-anatomy/">Gremlin’s Anatomy</a> tutorial.</p> </div> <div class="ulist"> <div class="title">Primary components of the TinkerPop <strong>structure</strong> API</div> <ul> <li> <p><code>Graph</code>: maintains a set of vertices and edges, and access to database functions such as transactions.</p> </li> <li> <p><code>Element</code>: maintains a collection of properties and a string label denoting the element type.</p> <div class="ulist"> <ul> <li> <p><code>Vertex</code>: extends Element and maintains a set of incoming and outgoing edges.</p> </li> <li> <p><code>Edge</code>: extends Element and maintains an incoming and outgoing vertex.</p> </li> </ul> </div> </li> <li> <p><code>Property<V></code>: a string key associated with a <code>V</code> value.</p> <div class="ulist"> <ul> <li> <p><code>VertexProperty<V></code>: a string key associated with a <code>V</code> value as well as a collection of <code>Property<U></code> properties (<strong>vertices only</strong>)</p> </li> </ul> </div> </li> </ul> </div> <div class="ulist"> <div class="title">Primary components of the TinkerPop <strong>process</strong> API</div> <ul> <li> <p><code>TraversalSource</code>: a generator of traversals for a particular graph, <a href="http://en.wikipedia.org/wiki/Domain-specific_language">domain specific language</a> (DSL), and execution engine.</p> <div class="ulist"> <ul> <li> <p><code>Traversal<S,E></code>: a functional data flow process transforming objects of type <code>S</code> into object of type <code>E</code>.</p> <div class="ulist"> <ul> <li> <p><code>GraphTraversal</code>: a traversal DSL that is oriented towards the semantics of the raw graph (i.e. vertices, edges, etc.).</p> </li> </ul> </div> </li> </ul> </div> </li> <li> <p><code>GraphComputer</code>: a system that processes the graph in parallel and potentially, distributed over a multi-machine cluster.</p> <div class="ulist"> <ul> <li> <p><code>VertexProgram</code>: code executed at all vertices in a logically parallel manner with intercommunication via message passing.</p> </li> <li> <p><code>MapReduce</code>: a computation that analyzes all vertices in the graph in parallel and yields a single reduced result.</p> </li> </ul> </div> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The TinkerPop API rides a fine line between providing concise "query language" method names and respecting Java method naming standards. The general convention used throughout TinkerPop is that if a method is "user exposed," then a concise name is provided (e.g. <code>out()</code>, <code>path()</code>, <code>repeat()</code>). If the method is primarily for graph systems providers, then the standard Java naming convention is followed (e.g. <code>getNextStep()</code>, <code>getSteps()</code>, <code>getElementComputeKeys()</code>). </td> </tr> </table> </div> <div class="sect2"> <h3 id="graph-structure">The Graph Structure</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-standing.png" alt="gremlin standing" width="125"></span> A graph’s structure is the topology formed by the explicit references between its vertices, edges, and properties. A vertex has incident edges. A vertex is adjacent to another vertex if they share an incident edge. A property is attached to an element and an element has a set of properties. A property is a key/value pair, where the key is always a character <code>String</code>. Conceptual knowledge of how a graph is composed is essential to end-users working with graphs, however, as mentioned earlier, the structure API is not the appropriate way for users to think when building applications with TinkerPop. The structure API is reserved for usage by graph providers. Those interested in implementing the structure API to make their graph system TinkerPop enabled can learn more about it in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/">Graph Provider</a> documentation.</p> </div> </div> <div class="sect2"> <h3 id="the-graph-process">The Graph Process</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-running.png" alt="gremlin running" width="125"></span> The primary way in which graphs are processed are via graph traversals. The TinkerPop process API is focused on allowing users to create graph traversals in a syntactically-friendly way over the structures defined in the previous section. A traversal is an algorithmic walk across the elements of a graph according to the referential structure explicit within the graph data structure. For example: <em>"What software does vertex 1’s friends work on?"</em> This English-statement can be represented in the following algorithmic/traversal fashion:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Start at vertex 1.</p> </li> <li> <p>Walk the incident knows-edges to the respective adjacent friend vertices of 1.</p> </li> <li> <p>Move from those friend-vertices to software-vertices via created-edges.</p> </li> <li> <p>Finally, select the name-property value of the current software-vertices.</p> </li> </ol> </div> <div class="paragraph"> <p>Traversals in Gremlin are spawned from a <code>TraversalSource</code>. The <code>GraphTraversalSource</code> is the typical "graph-oriented" DSL used throughout the documentation and will most likely be the most used DSL in a TinkerPop application. <code>GraphTraversalSource</code> provides two traversal methods.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>GraphTraversalSource.V(Object…​ ids)</code>: generates a traversal starting at vertices in the graph (if no ids are provided, all vertices).</p> </li> <li> <p><code>GraphTraversalSource.E(Object…​ ids)</code>: generates a traversal starting at edges in the graph (if no ids are provided, all edges).</p> </li> </ol> </div> <div class="paragraph"> <p>The return type of <code>V()</code> and <code>E()</code> is a <code>GraphTraversal</code>. A GraphTraversal maintains numerous methods that return <code>GraphTraversal</code>. In this way, a <code>GraphTraversal</code> supports function composition. Each method of <code>GraphTraversal</code> is called a step and each step modulates the results of the previous step in one of five general ways.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>map</code>: transform the incoming traverser’s object to another object (S → E).</p> </li> <li> <p><code>flatMap</code>: transform the incoming traverser’s object to an iterator of other objects (S → E*).</p> </li> <li> <p><code>filter</code>: allow or disallow the traverser from proceeding to the next step (S → E ⊆ S).</p> </li> <li> <p><code>sideEffect</code>: allow the traverser to proceed unchanged, but yield some computational sideEffect in the process (S ↬ S).</p> </li> <li> <p><code>branch</code>: split the traverser and send each to an arbitrary location in the traversal (S → { S<sub>1</sub> → E*, …​, S<sub>n</sub> → E* } → E*).</p> </li> </ol> </div> <div class="paragraph"> <p>Nearly every step in <code>GraphTraversal</code> either extends <code>MapStep</code>, <code>FlatMapStep</code>, <code>FilterStep</code>, <code>SideEffectStep</code>, or <code>BranchStep</code>.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> <code>GraphTraversal</code> is a <a href="http://en.wikipedia.org/wiki/Monoid">monoid</a> in that it is an algebraic structure that has a single binary operation that is associative. The binary operation is function composition (i.e. method chaining) and its identity is the step <code>identity()</code>. This is related to a <a href="http://en.wikipedia.org/wiki/Monad_(functional_programming)">monad</a> as popularized by the functional programming community. </td> </tr> </table> </div> <div class="paragraph"> <p>Given the TinkerPop graph, the following query will return the names of all the people that the marko-vertex knows. The following query is demonstrated using Gremlin-Groovy.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> bin/gremlin.sh <span class="error">\</span>,,,<span class="regexp"><span class="delimiter">/</span></span><span class="error"> </span> (o o) -----oOOo-(<span class="integer">3</span>)-oOOo----- gremlin> graph = TinkerFactory.createModern() // <span class="invisible">//</span><b class="conum">1</b> ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> g = traversal().withEmbedded(graph) // <span class="invisible">//</span><b class="conum">2</b> ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> 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>).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">3</b> ==>vadas ==>josh</code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Open the toy graph and reference it by the variable <code>graph</code>.</p> </li> <li> <p>Create a graph traversal source from the graph using the standard, OLTP traversal engine. This object should be created once and then re-used.</p> </li> <li> <p>Spawn a traversal off the traversal source that determines the names of the people that the marko-vertex knows.</p> </li> </ol> </div> <div class="imageblock"> <div class="content"> <img src="../images/tinkerpop-classic-ex1.png" alt="tinkerpop classic ex1" width="500"> </div> <div class="title">Figure 2. The Name of The People That Marko Knows</div> </div> <div class="paragraph"> <p>Or, if the marko-vertex is already realized with a direct reference pointer (i.e. a variable), then the traversal can be spawned off that vertex.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796963-1" type="radio" name="radio-set-1729796963-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796963-2" type="radio" name="radio-set-1729796963-1" class="tab-selector-2" /> <label for="tab-1729796963-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> marko = 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() <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</span>] gremlin> g.V(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">(3)</b> ==>vadas ==>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">marko = 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() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Set the variable <code>marko</code> to the vertex in the graph <code>g</code> named "marko".</p> </li> <li> <p>Get the vertices that are outgoing adjacent to the marko-vertex via knows-edges.</p> </li> <li> <p>Get the names of the marko-vertex’s friends.</p> </li> </ol> </div> <div class="sect3"> <h4 id="_the_traverser">The Traverser</h4> <div class="paragraph"> <p>When a traversal is executed, the source of the traversal is on the left of the expression (e.g. vertex 1), the steps are the middle of the traversal (e.g. <code>out('knows')</code> and <code>values('name')</code>), and the results are "traversal.next()'d" out of the right of the traversal (e.g. "vadas" and "josh").</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/traversal-mechanics.png" alt="traversal mechanics" width="500"> </div> </div> <div class="paragraph"> <p>The objects propagating through the traversal are wrapped in a <code>Traverser<T></code>. The traverser provides the means by which steps remain stateless. A traverser maintains all the metadata about the traversal — e.g., how many times the traverser has gone through a loop, the path history of the traverser, the current object being traversed, etc. Traverser metadata may be accessed by a step. A classic example is the <a href="#path-step"><code>path()</code></a>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796963-3" type="radio" name="radio-set-1729796963-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796963-4" type="radio" name="radio-set-1729796963-3" class="tab-selector-2" /> <label for="tab-1729796963-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> g.V(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).path() ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>],vadas] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</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(marko).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).path()</code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Path calculation is costly in terms of space as an array of previously seen objects is stored in each path of the respective traverser. Thus, a traversal strategy analyzes the traversal to determine if path metadata is required. If not, then path calculations are turned off. </td> </tr> </table> </div> <div class="paragraph"> <p>Another example is the <a href="#repeat-step"><code>repeat()</code></a>-step which takes into account the number of times the traverser has gone through a particular section of the traversal expression (i.e. a loop).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796963-5" type="radio" name="radio-set-1729796963-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796963-6" type="radio" name="radio-set-1729796963-5" class="tab-selector-2" /> <label for="tab-1729796963-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> g.V(marko).repeat(out()).times(<span class="integer">2</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>ripple ==>lop</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(marko).repeat(out()).times(<span class="integer">2</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="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> TinkerPop does not guarantee the order of results returned from a traversal. It only guarantees not to modify the iteration order provided by the underlying graph. Therefore it is important to understand the order guarantees of the graph database being used. A traversal’s result is never ordered by TinkerPop unless performed explicitly by means of <a href="#order-step"><code>order()</code></a>-step. </td> </tr> </table> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="connecting-gremlin">Connecting Gremlin</h2> <div class="sectionbody"> <div class="paragraph"> <p>It was established in the initial introductory section that <em>Gremlin is Gremlin is Gremlin</em>, meaning that irrespective of programming language, graph system, etc. the Gremlin written is always of the same general construct making it possible for users to move between development languages and TinkerPop-enabled graph technology easily. This quality of Gremlin generally applies to the traversal language itself. It applies less to the way in which the user connects to a graph to utilize Gremlin, which might differ considerably depending on the programming language or graph database chosen.</p> </div> <div class="paragraph"> <p>How one connects to a graph is a multi-faceted subject that essentially divides along a simple lines determined by the answer to this question: Where is the Gremlin Traversal Machine (GTM)? The reason that this question is so important is because the GTM is responsible for processing traversals. One can write Gremlin traversals in any language, but without a GTM there will be no way to execute that traversal against a TinkerPop-enabled graph. The GTM is typically in one of the following places:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#connecting-embedded">Embedded</a> in a Java application (i.e. Java Virtual Machine)</p> </li> <li> <p><a href="#connecting-gremlin-server">Hosted</a> in <a href="#gremlin-server">Gremlin Server</a></p> </li> <li> <p><a href="#connecting-rgp">Hosted</a> by a Remote Gremlin Provider (RGP)</p> </li> </ul> </div> <div class="paragraph"> <p>The following sections outline each of these models and what impact they have to using Gremlin.</p> </div> <div class="sect2"> <h3 id="connecting-embedded">Embedded</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/blueprints-character-1.png" alt="blueprints character 1" width="125"></span> TinkerPop maintains the reference implementation for the GTM, which is written in Java and thus available for the Java Virtual Machine (JVM). This is the classic model that TinkerPop has long been based on and many examples, blog posts and other resources on the internet will be demonstrated in this style. It is worth noting that the embedded mode is not restricted to just Java as a programming language. Any JVM language can take this approach and in some cases there are language specific wrappers that can help make Gremlin more convenient to use in the style and capability of that language. Examples of these wrappers include <a href="https://github.com/mpollmeier/gremlin-scala">gremlin-scala</a> and <a href="http://ogre.clojurewerkz.org/">Ogre</a> (for Clojure).</p> </div> <div class="paragraph"> <p>In this mode, users will start by creating a <code>Graph</code> instance, followed by a <code>GraphTraversalSource</code> which is the class from which Gremlin traversals are spawned. Graphs that allow this sort of direct instantiation are obviously ones that are JVM-based (or have a JVM-based connector) and directly implement TinkerPop interfaces.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Graph graph = TinkerGraph.open();</code></pre> </div> </div> <div class="paragraph"> <p>The "graph" is then used to spawn a <code>GraphTraversalSource</code> as follows and typically, by convention, this variable is named "g":</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withEmbedded(graph); <span class="predefined-type">List</span><Vertex> vertices = g.V().toList()</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> It may be helpful to read the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/gremlins-anatomy/">Gremlin Anatomy</a> tutorial, which describes the component parts of Gremlin to get a better understanding of the terminology before proceeding further. </td> </tr> </table> </div> <div class="paragraph"> <p>While the TinkerPop Community strives to ensure consistent behavior among all modes of usage, the embedded mode does provide the greatest level of flexibility and control. There are a number of features that can only work if using a JVM language. The following list outlines a number of these available options:</p> </div> <div class="ulist"> <ul> <li> <p>Lambdas can be written in the native language which is convenient, however, it will reduce the portability of Gremlin to do so should the need arise to switch away from the embedded mode. See more in the <a href="#a-note-on-lambdas">Note on Lambdas</a> Section.</p> </li> <li> <p>Any features that involve extending TinkerPop Java interfaces - e.g. <code>VertexProgram</code>, <code>TraversalStrategy</code>, etc. are bound to the JVM. In some cases, these features can be made accessible to non-JVM languages, but they obviously must be initially developed for the JVM.</p> </li> <li> <p>Certain built-in <code>TraversalStrategy</code> implementations that rely on lambdas or other JVM-only configurations may not be available for use any other way.</p> </li> <li> <p>There are no boundaries put in place by serialization (e.g. GraphSON) as embedded graphs are only dealing with Java objects.</p> </li> <li> <p>Greater control of graph <a href="#transactions">transactions</a>.</p> </li> <li> <p>Direct access to lower-levels of the API - e.g. "structure" API methods like <code>Vertex</code> and <code>Edge</code> interface methods. As mentioned <a href="#graph-computing">elsewhere</a> in this documentation, TinkerPop does not recommend direct usage of these methods by end-users.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="connecting-gremlin-server">Gremlin Server</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/rexster-character-3.png" alt="rexster character 3" width="125"></span> A JVM-based graph may be hosted in TinkerPop’s <a href="#gremlin-server">Gremlin Server</a>. Gremlin Server exposes the graph as an endpoint to which different clients can connect, essentially providing a remote GTM. Gremlin Server supports multiple methods for clients to interface with it:</p> </div> <div class="ulist"> <ul> <li> <p>Websockets with a <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_graph_driver_provider_requirements">custom sub-protocol</a></p> <div class="ulist"> <ul> <li> <p>String-based Gremlin scripts</p> </li> <li> <p>Bytecode-based Gremlin traversals</p> </li> </ul> </div> </li> <li> <p>HTTP for string-based scripts</p> </li> </ul> </div> <div class="paragraph"> <p>Users are encouraged to use the bytecode-based approach with websockets because it allows them to write Gremlin in the language of their choice. Connecting looks somewhat similar to the <a href="#connecting-embedded">embedded</a> approach in that there is a need to create a <code>GraphTraversalSource</code>. In the embedded approach, the means for that object’s creation is derived from a <code>Graph</code> object which spawns it. In this case, however, the <code>Graph</code> instance exists only on the server which means that there is no <code>Graph</code> instance to create locally. The approach is to instead create a <code>GraphTraversalSource</code> anonymously with <code>AnonymousTraversalSource</code> and then apply some "remote" options that describe the location of the Gremlin Server to connect to:</p> </div> <section class="tabs tabs-6"> <input id="tab-1729796963-7" type="radio" name="radio-set-1729796963-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-7" class="tab-label-1">java</label> <input id="tab-1729796963-8" type="radio" name="radio-set-1729796963-7" class="tab-selector-2" /> <label for="tab-1729796963-8" class="tab-label-2">groovy</label> <input id="tab-1729796963-9" type="radio" name="radio-set-1729796963-7" class="tab-selector-3" /> <label for="tab-1729796963-9" class="tab-label-3">csharp</label> <input id="tab-1729796963-10" type="radio" name="radio-set-1729796963-7" class="tab-selector-4" /> <label for="tab-1729796963-10" class="tab-label-4">javascript</label> <input id="tab-1729796963-11" type="radio" name="radio-set-1729796963-7" class="tab-selector-5" /> <label for="tab-1729796963-11" class="tab-label-5">python</label> <input id="tab-1729796963-12" type="radio" name="radio-set-1729796963-7" class="tab-selector-6" /> <label for="tab-1729796963-12" class="tab-label-6">go</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// gremlin-driver module</span> <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection</span>; <span class="comment">// gremlin-core module</span> <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal</span>; GraphTraversalSource g = traversal().withRemote( DriverRemoteConnection.using(<span class="string"><span class="delimiter">"</span><span class="content">localhost</span><span class="delimiter">"</span></span>, <span class="integer">8182</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"><span class="comment">// gremlin-driver module</span> <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection</span>; <span class="comment">// gremlin-core module</span> <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal</span>; <span class="keyword">def</span> g = traversal().withRemote( DriverRemoteConnection.using(<span class="string"><span class="delimiter">'</span><span class="content">localhost</span><span class="delimiter">'</span></span>, <span class="integer">8182</span>))</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">using Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection; using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource; var g = Traversal().WithRemote(new DriverRemoteConnection("localhost", 8182));</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const traversal = gremlin.process.AnonymousTraversalSource.traversal; const g = traversal().withRemote( <span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>));</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">from</span> <span class="include">gremlin_python.process.anonymous_traversal_source</span> <span class="keyword">import</span> <span class="include">traversal</span> g = traversal().withRemote( DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-6"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="keyword">import</span> ( gremlingo <span class="string"><span class="delimiter">"</span><span class="content">github.com/apache/tinkerpop/gremlin-go/v3/driver</span><span class="delimiter">"</span></span> ) remote, err := gremlingo.NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) g := gremlingo.Traversal_().WithRemote(remote)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>As shown in the embedded approach in the previous section, once "g" is defined, writing Gremlin is structurally and conceptually the same irrespective of programming language.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> The variable <code>g</code>, the <code>TraversalSource</code>, only needs to be instantiated once and should then be re-used. </td> </tr> </table> </div> <div class="sect3"> <h4 id="connecting-gremlin-server-limitations">Limitations</h4> <div class="paragraph"> <p>The previous section on the embedded model outlined a number of areas where it has some advantages that it gains due to the fact that the full GTM is available to the user in the language of its origin, i.e. Java. Some of those items touch upon important concepts to focus on here.</p> </div> <div class="paragraph"> <p>The first of these points is serialization. When Gremlin Server receives a request, the results must be serialized to the form requested by the client and then the client deserializes those into objects native to the language. TinkerPop has two such formats that it uses with <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/#graphbinary">GraphBinary</a> and <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/#graphson">GraphSON</a>. Users should prefer GraphBinary when available in the programming language being used.</p> </div> <div class="paragraph"> <p>A good example is the <code>subgraph()</code>-step which returns a <code>Graph</code> instance as its result. The subgraph returned from the server can be deserialized into an actual <code>Graph</code> instance on the client, which then means it is possible to spawn a <code>GraphTraversalSource</code> from that to do local Gremlin traversals on the client-side. For non-JVM <a href="#gremlin-drivers-variants">Gremlin Language Variants</a> there is no local graph to deserialize that result into and no GTM to process Gremlin so there isn’t much that can be done with such a result.</p> </div> <div class="paragraph"> <p>The second point is related to this issue. As there is no GTM, there is no "structure" API and thus graph elements like <code>Vertex</code> and <code>Edge</code> are "references" only. A "reference" means that they only contain the <code>id</code> and <code>label</code> of the element and not the properties. To be consistent, even JVM-based languages hold this limitation when talking to a remote Gremlin Server.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Most SQL developers would not write a query as <code>SELECT * FROM table</code>. They would instead write the individual names of the fields they wanted in place of the wildcard. Writing "good" Gremlin is no different with this regard. Prefer explicit property key names in Gremlin unless it is completely impossible to do so. </td> </tr> </table> </div> <div class="paragraph"> <p>The third and final point involves transactions. Under this model, one traversal is equivalent to a single transaction and there is no way in TinkerPop to string together multiple traversals into the same transaction.</p> </div> </div> </div> <div class="sect2"> <h3 id="connecting-rgp">Remote Gremlin Provider</h3> <div class="paragraph"> <p>Remote Gremlin Providers (RGPs) are showing up more and more often in the graph database space. In TinkerPop terms, this category of graph providers is defined by those who simply support the Gremlin language. Typically, these are server-based graphs, often cloud-based, which accept Gremlin scripts or bytecode as a request and return results. They will often implement Gremlin Server protocols, which enables TinkerPop drivers to connect to them as they would with Gremlin Server. Therefore, the typical connection approach is identical to the method of connection presented in the <a href="#connecting-gremlin-server">previous section</a> with the exact same caveats pointed out toward the end.</p> </div> <div class="paragraph"> <p>Despite leveraging TinkerPop protocols and drivers as being typical, RGPs are not required to do so to be considered TinkerPop-enabled. RGPs may well have their own drivers and protocols that may plug into <a href="#gremlin-drivers-variants">Gremlin Language Variants</a> and may allow for more advanced options like better security, cluster awareness, batched requests or other features. The details of these different systems are outside the scope of this documentation, so be sure to consult their documentation for more information.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="basic-gremlin">Basic Gremlin</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/language-variants.png" alt="language variants" width="300"></span> The <code>GraphTraversalSource</code> is basically the connection to a graph instance. That graph instance might be <a href="#connecting-embedded">embedded</a>, hosted in <a href="#connecting-gremlin-server">Gremlin Server</a> or hosted in a <a href="#connecting-rgp">RGP</a>, but the <code>GraphTraversalSource</code> is agnostic to that. Assuming "g" is the <code>GraphTraversalSource</code>, getting data into the graph regardless of programming language or mode of operation is just some basic Gremlin:</p> </div> <section class="tabs tabs-7"> <input id="tab-1729796963-13" type="radio" name="radio-set-1729796963-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796963-14" type="radio" name="radio-set-1729796963-13" class="tab-selector-2" /> <label for="tab-1729796963-14" class="tab-label-2">groovy</label> <input id="tab-1729796963-15" type="radio" name="radio-set-1729796963-13" class="tab-selector-3" /> <label for="tab-1729796963-15" class="tab-label-3">csharp</label> <input id="tab-1729796963-16" type="radio" name="radio-set-1729796963-13" class="tab-selector-4" /> <label for="tab-1729796963-16" class="tab-label-4">java</label> <input id="tab-1729796963-17" type="radio" name="radio-set-1729796963-13" class="tab-selector-5" /> <label for="tab-1729796963-17" class="tab-label-5">javascript</label> <input id="tab-1729796963-18" type="radio" name="radio-set-1729796963-13" class="tab-selector-6" /> <label for="tab-1729796963-18" class="tab-label-6">python</label> <input id="tab-1729796963-19" type="radio" name="radio-set-1729796963-13" class="tab-selector-7" /> <label for="tab-1729796963-19" class="tab-label-7">go</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> v1 = 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>).next() ==>v[<span class="integer">0</span>] gremlin> v2 = 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">stephen</span><span class="delimiter">'</span></span>).next() ==>v[<span class="integer">2</span>] gremlin> g.V(v1).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(v2).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.75</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">v1 = 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>).next() v2 = 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">stephen</span><span class="delimiter">'</span></span>).next() g.V(v1).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(v2).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.75</span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var v1 = g.AddV("person").Property("name", "marko").Next(); var v2 = g.AddV("person").Property("name", "stephen").Next(); g.V(v1).AddE("knows").To(v2).Property("weight", 0.75).Iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Vertex v1 = 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>).next(); Vertex v2 = 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">stephen</span><span class="delimiter">"</span></span>).next(); g.V(v1).addE(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).to(v2).property(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>,<span class="float">0.75</span>).iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const v1 = 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>).next(); const v2 = 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">stephen</span><span class="delimiter">'</span></span>).next(); g.V(v1).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(v2).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.75</span>).iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-6"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">v1 = 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>).next() v2 = 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">stephen</span><span class="delimiter">'</span></span>).next() g.V(v1).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(v2).property(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,<span class="float">0.75</span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-7"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">v1, err := 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>).Next() v2, err := 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">stephen</span><span class="delimiter">"</span></span>).Next() g.V(v1).AddE(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).To(v2).Property(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>, <span class="float">0.75</span>).Iterate()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The first two lines add a vertex each with the vertex label of "person" and the associated "name" property. The third line adds an edge with the "knows" label between them and an associated "weight" property. Note the use of <code>next()</code> and <code>iterate()</code> at the end of the lines - their effect as <a href="#terminal-steps">terminal steps</a> is described in <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/the-gremlin-console/#result-iteration">The Gremlin Console Tutorial</a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Writing Gremlin is just one way to load data into the graph. Some graphs may have special data loaders which could be more efficient and make the task easier and faster. It is worth looking into those tools especially if there is a large one-time load to do. </td> </tr> </table> </div> <div class="paragraph"> <p>Retrieving this data is also a just writing a Gremlin statement:</p> </div> <section class="tabs tabs-7"> <input id="tab-1729796963-20" type="radio" name="radio-set-1729796963-20" class="tab-selector-1" checked="checked" /> <label for="tab-1729796963-20" class="tab-label-1">console (groovy)</label> <input id="tab-1729796963-21" type="radio" name="radio-set-1729796963-20" class="tab-selector-2" /> <label for="tab-1729796963-21" class="tab-label-2">groovy</label> <input id="tab-1729796963-22" type="radio" name="radio-set-1729796963-20" class="tab-selector-3" /> <label for="tab-1729796963-22" class="tab-label-3">csharp</label> <input id="tab-1729796963-23" type="radio" name="radio-set-1729796963-20" class="tab-selector-4" /> <label for="tab-1729796963-23" class="tab-label-4">java</label> <input id="tab-1729796963-24" type="radio" name="radio-set-1729796963-20" class="tab-selector-5" /> <label for="tab-1729796963-24" class="tab-label-5">javascript</label> <input id="tab-1729796963-25" type="radio" name="radio-set-1729796963-20" class="tab-selector-6" /> <label for="tab-1729796963-25" class="tab-label-6">python</label> <input id="tab-1729796963-26" type="radio" name="radio-set-1729796963-20" class="tab-selector-7" /> <label for="tab-1729796963-26" class="tab-label-7">go</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> marko = 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>).next() ==>v[<span class="integer">0</span>] gremlin> peopleMarkoKnows = 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList() ==>v[<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">marko = 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>).next() peopleMarkoKnows = 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var marko = g.V().Has("person", "name", "marko").Next(); var peopleMarkoKnows = g.V().Has("person", "name", "marko").Out("knows").ToList();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Vertex marko = 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>).next() <span class="predefined-type">List</span><Vertex> peopleMarkoKnows = 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>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const marko = 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>).next() const peopleMarkoKnows = 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-6"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">marko = 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>).next() peopleMarkoKnows = 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-7"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">marko, err := 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>).Next() peopleMarkoKnows, err := 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>).Out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).ToList()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In all these examples presented so far there really isn’t a lot of difference in how the Gremlin itself looks. There are a few language syntax specific odds and ends, but for the most part Gremlin looks like Gremlin in all of the different languages.</p> </div> <div class="paragraph"> <p>The library of Gremlin steps with examples for each can be found in <a href="#traversal">The Traversal Section</a>. This section is meant as a reference guide and will not necessarily provide methods for applying Gremlin to solve particular problems. Please see the aforementioned <a href="https://tinkerpop.apache.org/docs/3.7.3/#tutorials">Tutorials</a> <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/">Recipes</a> and the <a href="http://kelvinlawrence.net/book/Gremlin-Graph-Guide.html">Practical Gremlin</a> book for that sort of information.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> A full list of helpful Gremlin resources can be found on the <a href="https://tinkerpop.apache.org/docs/3.7.3/">TinkerPop Compendium</a> page. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="staying-agnostic">Staying Agnostic</h2> <div class="sectionbody"> <div class="paragraph"> <p>A good deal has been written in these introductory sections on how TinkerPop enables an agnostic approach to building graph application and that agnosticism is enabled through Gremlin. As good a job as Gremlin can do in this area, it’s evident from the <a href="#connecting-gremlin">Connecting Gremlin</a> Section that TinkerPop is just an enabler. It does not prevent a developer from making design choices that can limit its protective power.</p> </div> <div class="paragraph"> <p>There are several places to be concerned when considering this issue:</p> </div> <div class="ulist"> <ul> <li> <p><strong>Data types</strong> - Different graphs will support different types of data. Something like TinkerGraph will accept any JVM object, but another graph like Neo4j has a small tight subset of possible types. Choosing a type that is exotic or perhaps is a custom type that only a specific graph supports might create migration friction should the need arise.</p> </li> <li> <p><strong>Schemas/Indices</strong> - TinkerPop does not provide abstractions for schemas and/or index management. Users will work directly with the API of the graph provider. It is considered good practice to attempt to enclose such code in a graph provider specific class or set of classes to isolate or abstract it.</p> </li> <li> <p><strong>Extensions</strong> - Graphs may provide extensions to the Gremlin language, which will not be designed to be compatible with other graph providers. There may be a special helper syntax or <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/gremlins-anatomy/#_expressions">expressions</a> which can make certain features of that specific graph shine in powerful ways. Using those options is probably recommended, but users should be aware that doing so ties them more tightly to that graph.</p> </li> <li> <p><strong>Graph specific semantics</strong> - TinkerPop tries to enforce specific semantics through its test suite which is quite extensive, but some graph providers may not completely respect all the semantics of the Gremlin language or TinkerPop’s model for its APIs. For the most part, that doesn’t disqualify them from being any less TinkerPop-enabled than another provider that might meet the semantics perfectly. Take care when considering a new graph and pay attention to what it supports and does not support.</p> </li> <li> <p><a href="#graph"><strong>Graph API</strong></a> - The <a href="#graph-structure">Graph API</a> (also referred to as the Structure API) is not always accessible to users. Its accessibility is dependent on the choice of graph system and programming language. It is therefore recommended that users avoid usage of methods like <code>Graph.addVertex()</code> or <code>Vertex.properties()</code> and instead prefer use of Gremlin with <code>g.addV()</code> or <code>g.V(1).properties()</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>Outside of considering these points, the best practice for ensuring the greatest level of compatibility across graphs is to avoid <a href="#connecting-embedded">embedded</a> mode and stick to the bytecode based approaches explained in the <a href="#connecting-gremlin-server">Gremlin Server</a> and the <a href="#connecting-rgp">RGP</a> sections above. It creates the least opportunity to stray from the agnostic path as anything that can be done with those two modes also works in embedded mode. If using embedded mode, simply write code as though the <code>Graph</code> instance is "remote" and not local to the JVM. In other words, write code as though the GTM is not available locally. Taking that approach and isolating the points of concern above makes it so that swapping graph providers largely comes down to a configuration task (i.e. modifying configuration files to point at a different graph system).</p> </div> </div> </div> <h1 id="graph" class="sect0">The Graph</h1> <div class="openblock partintro"> <div class="content"> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-standing.png" alt="gremlin standing" width="125"> </div> </div> <div class="paragraph"> <p>The <a href="#intro">Introduction</a> discussed the diversity of TinkerPop-enabled graphs, with special attention paid to the different <a href="#connecting-gremlin">connection models</a>, and how TinkerPop makes it possible to bridge that diversity in an <a href="#staying-agnostic">agnostic</a> manner. This particular section deals with elements of the Graph API which was noted as an API to avoid when trying to build an agnostic system. The Graph API refers to the core elements of what composes the <a href="#graph-computing">structure of a graph</a> within the Gremlin Traversal Machine (GTM), such as the <code>Graph</code>, <code>Vertex</code> and <code>Edge</code> Java interfaces.</p> </div> <div class="paragraph"> <p>To maintain the most portable code, users should only reference these interfaces. To "reference", simply means to utilize it as a pointer. For <code>Graph</code>, that means holding a pointer to the location of graph data and then using it to spawn <code>GraphTraversalSource</code> instances so as to write Gremlin:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-1" type="radio" name="radio-set-1729796977-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-2" type="radio" name="radio-set-1729796977-1" class="tab-selector-2" /> <label for="tab-1729796977-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) ==>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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the above example, "graph" is the <code>Graph</code> interface produced by calling <code>open()</code> on <code>TinkerGraph</code> which creates the instance. Note that while the end intent of the code is to create a "person" vertex, it does not use the APIs on <code>Graph</code> to do that - e.g. <code>graph.addVertex(T.label,'person')</code>.</p> </div> <div class="paragraph"> <p>Even if the developer desired to use the <code>graph.addVertex()</code> method there are only a handful of scenarios where it is possible:</p> </div> <div class="ulist"> <ul> <li> <p>The application is being developed on the JVM and the developer is using <a href="#connecting-embedded">embedded</a> mode</p> </li> <li> <p>The architecture includes Gremlin Server and the user is sending Gremlin scripts to the server</p> </li> <li> <p>The graph system chosen is a <a href="#connecting-rgp">Remote Gremlin Provider</a> and they expose the Graph API via scripts</p> </li> </ul> </div> <div class="paragraph"> <p>Note that Gremlin Language Variants force developers to use the Graph API by reference. There is no <code>addVertex()</code> method available to GLVs on their respective <code>Graph</code> instances, nor are their graph elements filled with data at the call of <code>properties()</code>. Developing applications to meet this lowest common denominator in API usage will go a long way to making that application portable across TinkerPop-enabled systems.</p> </div> <div class="paragraph"> <p>When considering the remaining sub-sections that follow, recall that they are all generally bound to the Graph API. They are described here for reference and in some sense backward compatibility with older recommended models of development. In the future, the contents of this section will become less and less relevant.</p> </div> </div> </div> <div class="sect1"> <h2 id="_features">Features</h2> <div class="sectionbody"> <div class="paragraph"> <p>A <code>Feature</code> implementation describes the capabilities of a <code>Graph</code> instance. This interface is implemented by graph system providers for two purposes:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>It tells users the capabilities of their <code>Graph</code> instance.</p> </li> <li> <p>It allows the features they do comply with to be tested against the Gremlin Test Suite - tests that do not comply are "ignored").</p> </li> </ol> </div> <div class="paragraph"> <p>The following example in the Gremlin Console shows how to print all the features of a <code>Graph</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-3" type="radio" name="radio-set-1729796977-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-4" type="radio" name="radio-set-1729796977-3" class="tab-selector-2" /> <label for="tab-1729796977-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> graph.features() ==>FEATURES > GraphFeatures >-- <span class="key">Transactions</span>: <span class="predefined-constant">false</span> >-- <span class="key">Computer</span>: <span class="predefined-constant">true</span> >-- <span class="key">Persistence</span>: <span class="predefined-constant">true</span> >-- <span class="key">ConcurrentAccess</span>: <span class="predefined-constant">false</span> >-- <span class="key">ThreadedTransactions</span>: <span class="predefined-constant">false</span> >-- <span class="key">IoRead</span>: <span class="predefined-constant">true</span> >-- <span class="key">IoWrite</span>: <span class="predefined-constant">true</span> >-- <span class="key">OrderabilitySemantics</span>: <span class="predefined-constant">true</span> >-- <span class="key">ServiceCall</span>: <span class="predefined-constant">true</span> > VariableFeatures >-- <span class="key">Variables</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MapValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MixedListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">SerializableValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">UniformListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongArrayValues</span>: <span class="predefined-constant">true</span> > VertexFeatures >-- <span class="key">MetaProperties</span>: <span class="predefined-constant">true</span> >-- <span class="key">Upsert</span>: <span class="predefined-constant">false</span> >-- <span class="key">AddVertices</span>: <span class="predefined-constant">true</span> >-- <span class="key">RemoveVertices</span>: <span class="predefined-constant">true</span> >-- <span class="key">MultiProperties</span>: <span class="predefined-constant">true</span> >-- <span class="key">DuplicateMultiProperties</span>: <span class="predefined-constant">true</span> >-- <span class="key">NullPropertyValues</span>: <span class="predefined-constant">false</span> >-- <span class="key">UserSuppliedIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">AddProperty</span>: <span class="predefined-constant">true</span> >-- <span class="key">RemoveProperty</span>: <span class="predefined-constant">true</span> >-- <span class="key">NumericIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">UuidIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">CustomIds</span>: <span class="predefined-constant">false</span> >-- <span class="key">AnyIds</span>: <span class="predefined-constant">true</span> > VertexPropertyFeatures >-- <span class="key">NullPropertyValues</span>: <span class="predefined-constant">false</span> >-- <span class="key">UserSuppliedIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">RemoveProperty</span>: <span class="predefined-constant">true</span> >-- <span class="key">NumericIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">UuidIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">CustomIds</span>: <span class="predefined-constant">false</span> >-- <span class="key">AnyIds</span>: <span class="predefined-constant">true</span> >-- <span class="predefined-type">Properties</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MapValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MixedListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">SerializableValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">UniformListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongArrayValues</span>: <span class="predefined-constant">true</span> > EdgeFeatures >-- <span class="key">AddEdges</span>: <span class="predefined-constant">true</span> >-- <span class="key">RemoveEdges</span>: <span class="predefined-constant">true</span> >-- <span class="key">Upsert</span>: <span class="predefined-constant">false</span> >-- <span class="key">NullPropertyValues</span>: <span class="predefined-constant">false</span> >-- <span class="key">UserSuppliedIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">AddProperty</span>: <span class="predefined-constant">true</span> >-- <span class="key">RemoveProperty</span>: <span class="predefined-constant">true</span> >-- <span class="key">NumericIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">UuidIds</span>: <span class="predefined-constant">true</span> >-- <span class="key">CustomIds</span>: <span class="predefined-constant">false</span> >-- <span class="key">AnyIds</span>: <span class="predefined-constant">true</span> > EdgePropertyFeatures >-- <span class="predefined-type">Properties</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MapValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">MixedListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">SerializableValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">UniformListValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">BooleanArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">ByteArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">DoubleArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">FloatArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">IntegerArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">StringArrayValues</span>: <span class="predefined-constant">true</span> >-- <span class="key">LongArrayValues</span>: <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">graph = TinkerGraph.open() graph.features()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>A common pattern for using features is to check their support prior to performing an operation:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-5" type="radio" name="radio-set-1729796977-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-6" type="radio" name="radio-set-1729796977-5" class="tab-selector-2" /> <label for="tab-1729796977-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> graph.features().graph().supportsTransactions() ==><span class="predefined-constant">false</span> gremlin> graph.features().graph().supportsTransactions() ? g.tx().commit() : <span class="string"><span class="delimiter">"</span><span class="content">no tx</span><span class="delimiter">"</span></span> ==>no tx</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">graph.features().graph().supportsTransactions() graph.features().graph().supportsTransactions() ? g.tx().commit() : <span class="string"><span class="delimiter">"</span><span class="content">no tx</span><span class="delimiter">"</span></span></code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> To ensure provider agnostic code, always check feature support prior to usage of a particular function. In that way, the application can behave gracefully in case a particular implementation is provided at runtime that does not support a function being accessed. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Features of reference graphs which are used to connect to remote graphs do not reflect the features of the graph to which it connects. It reflects the features of instantiated graph itself, which will likely be quite different considering that reference graphs will typically be immutable. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="vertex-properties">Vertex Properties</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image left"><img src="../images/vertex-properties.png" alt="vertex properties" width="215"></span> TinkerPop introduces the concept of a <code>VertexProperty<V></code>. All the properties of a <code>Vertex</code> are a <code>VertexProperty</code>. A <code>VertexProperty</code> implements <code>Property</code> and as such, it has a key/value pair. However, <code>VertexProperty</code> also implements <code>Element</code> and thus, can have a collection of key/value pairs. Moreover, while an <code>Edge</code> can only have one property of key "name" (for example), a <code>Vertex</code> can have multiple "name" properties. With the inclusion of vertex properties, two features are introduced which ultimately advance the graph modelers toolkit:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Multiple properties (<strong>multi-properties</strong>): a vertex property key can have multiple values. For example, a vertex can have multiple "name" properties.</p> </li> <li> <p>Properties on properties (<strong>meta-properties</strong>): a vertex property can have properties (i.e. a vertex property can have key/value data associated with it).</p> </li> </ol> </div> <div class="paragraph"> <p>Possible use cases for meta-properties:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><strong>Permissions</strong>: Vertex properties can have key/value ACL-type permission information associated with them.</p> </li> <li> <p><strong>Auditing</strong>: When a vertex property is manipulated, it can have key/value information attached to it saying who the creator, deletor, etc. are.</p> </li> <li> <p><strong>Provenance</strong>: The "name" of a vertex can be declared by multiple users. For example, there may be multiple spellings of a name from different sources.</p> </li> </ol> </div> <div class="paragraph"> <p>A running example using vertex properties is provided below to demonstrate and explain the API.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-7" type="radio" name="radio-set-1729796977-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-8" type="radio" name="radio-set-1729796977-7" class="tab-selector-2" /> <label for="tab-1729796977-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> v = 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>).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 a. rodriguez</span><span class="delimiter">'</span></span>).next() ==>v[<span class="integer">0</span>] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).count() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">2</span> gremlin> v.property(list, <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">m. a. rodriguez</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).count() ==><span class="integer">3</span> gremlin> g.V(v).properties() ==>vp[name->marko] ==>vp[name->marko a. rodriguez] ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vp[name->marko] ==>vp[name->marko a. rodriguez] ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) ==>vp[name->marko] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<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">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>vp[name->marko] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko a. rodriguez</span><span class="delimiter">'</span></span>) ==>vp[name->marko a. rodriguez] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko a. rodriguez</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>) ==>vp[name->marko a. rodriguez] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).value() ==>marko a. rodriguez gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).drop() <span class="comment">//</span>// <b class="conum">(4)</b> gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).value() gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>).value() ==>marko gremlin> g.V(v).properties() ==>vp[name->marko] ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties().properties() <span class="comment">//</span>// <b class="conum">(5)</b> ==>p[acl-><span class="directive">private</span>] gremlin> g.V(v).properties().property(<span class="string"><span class="delimiter">'</span><span class="content">date</span><span class="delimiter">'</span></span>,<span class="integer">2014</span>) <span class="comment">//</span>// <b class="conum">(6)</b> ==>vp[name->marko] ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties().property(<span class="string"><span class="delimiter">'</span><span class="content">creator</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>) ==>vp[name->marko] ==>vp[name->m. a. rodriguez] gremlin> g.V(v).properties().properties() ==>p[date-><span class="integer">2014</span>] ==>p[creator->stephen] ==>p[acl-><span class="directive">private</span>] ==>p[date-><span class="integer">2014</span>] ==>p[creator->stephen] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">date</span>:<span class="integer">2014</span>,<span class="key">creator</span>:stephen,<span class="key">acl</span>:<span class="directive">private</span>] ==>[<span class="key">date</span>:<span class="integer">2014</span>,<span class="key">creator</span>:stephen] gremlin> g.V(v).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">okram</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==>v[<span class="integer">0</span>] gremlin> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vp[name->okram] gremlin> g.V(v).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">(8)</b> ==>okram</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) v = 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>).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 a. rodriguez</span><span class="delimiter">'</span></span>).next() g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).count() <span class="comment">//</span>// <b class="conum">(1)</b> v.property(list, <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">m. a. rodriguez</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).count() g.V(v).properties() g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>) g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<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">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko a. rodriguez</span><span class="delimiter">'</span></span>) g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).hasValue(<span class="string"><span class="delimiter">'</span><span class="content">marko a. rodriguez</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>) g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).value() g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).drop() <span class="comment">//</span>// <b class="conum">(4)</b> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>).value() g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>).value() g.V(v).properties() g.V(v).properties().properties() <span class="comment">//</span>// <b class="conum">(5)</b> g.V(v).properties().property(<span class="string"><span class="delimiter">'</span><span class="content">date</span><span class="delimiter">'</span></span>,<span class="integer">2014</span>) <span class="comment">//</span>// <b class="conum">(6)</b> g.V(v).properties().property(<span class="string"><span class="delimiter">'</span><span class="content">creator</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>) g.V(v).properties().properties() g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).valueMap() g.V(v).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">okram</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> g.V(v).properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V(v).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">8</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A vertex can have zero or more properties with the same key associated with it.</p> </li> <li> <p>If a property is added with a cardinality of <code>Cardinality.list</code>, an additional property with the provided key will be added.</p> </li> <li> <p>A vertex property can have standard key/value properties attached to it.</p> </li> <li> <p>Vertex property removal is identical to property removal.</p> </li> <li> <p>Gets the meta-properties of each vertex property.</p> </li> <li> <p>A vertex property can have any number of key/value properties attached to it.</p> </li> <li> <p><code>property(…​)</code> will remove all existing key’d properties before adding the new single property (see <code>VertexProperty.Cardinality</code>).</p> </li> <li> <p>If only the value of a property is needed, then <code>values()</code> can be used.</p> </li> </ol> </div> <div class="paragraph"> <p>If the concept of vertex properties is difficult to grasp, then it may be best to think of vertex properties in terms of "literal vertices." A vertex can have an edge to a "literal vertex" that has a single value key/value — e.g. "value=okram." The edge that points to that literal vertex has an edge-label of "name." The properties on the edge represent the literal vertex’s properties. The "literal vertex" can not have any other edges to it (only one from the associated vertex).</p> </div> <div id="the-crew-toy-graph" class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> A toy graph demonstrating all of the new TinkerPop graph structure features is available at <code>TinkerFactory.createTheCrew()</code> and <code>data/tinkerpop-crew*</code>. This graph demonstrates multi-properties and meta-properties. </td> </tr> </table> </div> <div class="imageblock"> <div class="content"> <img src="../images/the-crew-graph.png" alt="the crew graph" width="685"> </div> <div class="title">Figure 3. TinkerPop Crew</div> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-9" type="radio" name="radio-set-1729796977-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-10" type="radio" name="radio-set-1729796977-9" class="tab-selector-2" /> <label for="tab-1729796977-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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).by(value).by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>) <span class="comment">// determine the current location of each person</span> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:santa fe,<span class="key">c</span>:<span class="integer">2005</span>] ==>[<span class="key">a</span>:stephen,<span class="key">b</span>:purcellville,<span class="key">c</span>:<span class="integer">2006</span>] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:seattle,<span class="key">c</span>:<span class="integer">2014</span>] ==>[<span class="key">a</span>:daniel,<span class="key">b</span>:aachen,<span class="key">c</span>:<span class="integer">2009</span>] gremlin> 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">gremlin</span><span class="delimiter">'</span></span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">skill</span><span class="delimiter">'</span></span>,asc).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">skill</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">// rank the users of gremlin by their skill level</span> ==>[<span class="key">a</span>:<span class="integer">3</span>,<span class="key">b</span>:matthias] ==>[<span class="key">a</span>:<span class="integer">4</span>,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:<span class="integer">5</span>,<span class="key">b</span>:stephen] ==>[<span class="key">a</span>:<span class="integer">5</span>,<span class="key">b</span>: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().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).by(value).by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>) <span class="comment">// determine the current location of each person</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">gremlin</span><span class="delimiter">'</span></span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">skill</span><span class="delimiter">'</span></span>,asc).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). outV().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">skill</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">// rank the users of gremlin by their skill level</span></code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="_graph_variables">Graph Variables</h2> <div class="sectionbody"> <div class="paragraph"> <p><code>Graph.Variables</code> are key/value pairs associated with the graph itself — in essence, a <code>Map<String,Object></code>. These variables are intended to store metadata about the graph. Example use cases include:</p> </div> <div class="ulist"> <ul> <li> <p><strong>Schema information</strong>: What do the namespace prefixes resolve to and when was the schema last modified?</p> </li> <li> <p><strong>Global permissions</strong>: What are the access rights for particular groups?</p> </li> <li> <p><strong>System user information</strong>: Who are the admins of the system?</p> </li> </ul> </div> <div class="paragraph"> <p>An example of graph variables in use is presented below:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796977-11" type="radio" name="radio-set-1729796977-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796977-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796977-12" type="radio" name="radio-set-1729796977-11" class="tab-selector-2" /> <label for="tab-1729796977-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> graph.variables() ==>variables[<span class="key">size</span>:<span class="integer">0</span>] gremlin> graph.variables().set(<span class="string"><span class="delimiter">'</span><span class="content">systemAdmins</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">pavel</span><span class="delimiter">'</span></span>]) ==><span class="predefined-constant">null</span> gremlin> graph.variables().set(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">matthias</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="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]) ==><span class="predefined-constant">null</span> gremlin> graph.variables().keys() ==>systemAdmins ==>systemUsers gremlin> graph.variables().get(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>) ==>Optional[[matthias, marko, josh]] gremlin> graph.variables().get(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>).get() ==>matthias ==>marko ==>josh gremlin> graph.variables().remove(<span class="string"><span class="delimiter">'</span><span class="content">systemAdmins</span><span class="delimiter">'</span></span>) ==><span class="predefined-constant">null</span> gremlin> graph.variables().keys() ==>systemUsers</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">graph = TinkerGraph.open() graph.variables() graph.variables().set(<span class="string"><span class="delimiter">'</span><span class="content">systemAdmins</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">pavel</span><span class="delimiter">'</span></span>]) graph.variables().set(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>,[<span class="string"><span class="delimiter">'</span><span class="content">matthias</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="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>]) graph.variables().keys() graph.variables().get(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>) graph.variables().get(<span class="string"><span class="delimiter">'</span><span class="content">systemUsers</span><span class="delimiter">'</span></span>).get() graph.variables().remove(<span class="string"><span class="delimiter">'</span><span class="content">systemAdmins</span><span class="delimiter">'</span></span>) graph.variables().keys()</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"> Graph variables are not intended to be subject to heavy, concurrent mutation nor to be used in complex computations. The intention is to have a location to store data about the graph for administrative purposes. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Attempting to set graph variables in a reference graph will not promote them to the remote graph. Typically, a reference graph has immutable features and will not support this features. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="_namespace_conventions">Namespace Conventions</h2> <div class="sectionbody"> <div class="paragraph"> <p>End users, <a href="#implementations">graph system providers</a>, <a href="#graphcomputer"><code>GraphComputer</code></a> algorithm designers, <a href="#gremlin-plugins">GremlinPlugin</a> creators, etc. all leverage properties on elements to store information. There are a few conventions that should be respected when naming property keys to ensure that conflicts between these stakeholders do not conflict.</p> </div> <div class="ulist"> <ul> <li> <p>End users are granted the <em>flat namespace</em> (e.g. <code>name</code>, <code>age</code>, <code>location</code>) to key their properties and label their elements.</p> </li> <li> <p>Graph system providers are granted the <em>hidden namespace</em> (e.g. <code>~metadata</code>) to key their properties and labels. Data keyed as such is only accessible via the graph system implementation and no other stakeholders are granted read nor write access to data prefixed with "~" (see <code>Graph.Hidden</code>). Test coverage and exceptions exist to ensure that graph systems respect this hard boundary.</p> </li> <li> <p><a href="#vertexprogram"><code>VertexProgram</code></a> and <a href="#mapreduce"><code>MapReduce</code></a> developers should leverage <em>qualified namespaces</em> particular to their domain (e.g. <code>mydomain.myvertexprogram.computedata</code>).</p> </li> <li> <p><code>GremlinPlugin</code> creators should prefix their plugin name with their domain (e.g. <code>mydomain.myplugin</code>).</p> </li> </ul> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> TinkerPop uses <code>tinkerpop.</code> and <code>gremlin.</code> as the prefixes for provided strategies, vertex programs, map reduce implementations, and plugins. </td> </tr> </table> </div> <div class="paragraph"> <p>The only truly protected namespace is the <em>hidden namespace</em> provided to graph systems. From there, it’s up to engineers to respect the namespacing conventions presented.</p> </div> </div> </div> <h1 id="traversal" class="sect0">The Traversal</h1> <div class="openblock partintro"> <div class="content"> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-running.png" alt="gremlin running" width="125"> </div> </div> <div class="paragraph"> <p>At the most general level there is <code>Traversal<S,E></code> which implements <code>Iterator<E></code>, where the <code>S</code> stands for start and the <code>E</code> stands for end. A traversal is composed of four primary components:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>Step<S,E></code>: an individual function applied to <code>S</code> to yield <code>E</code>. Steps are chained within a traversal.</p> </li> <li> <p><code>TraversalStrategy</code>: interceptor methods to alter the execution of the traversal (e.g. query re-writing).</p> </li> <li> <p><code>TraversalSideEffects</code>: key/value pairs that can be used to store global information about the traversal.</p> </li> <li> <p><code>Traverser<T></code>: the object propagating through the <code>Traversal</code> currently representing an object of type <code>T</code>.</p> </li> </ol> </div> <div class="paragraph"> <p>The classic notion of a graph traversal is provided by <code>GraphTraversal<S,E></code> which extends <code>Traversal<S,E></code>. <code>GraphTraversal</code> provides an interpretation of the graph data in terms of vertices, edges, etc. and thus, a graph traversal <a href="http://en.wikipedia.org/wiki/Domain-specific_language">DSL</a>.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/step-types.png" alt="step types" width="650"> </div> </div> <div class="paragraph"> <p>A <code>GraphTraversal<S,E></code> is spawned from a <code>GraphTraversalSource</code>. It can also be spawned anonymously (i.e. empty) via <code>__</code>. A graph traversal is composed of an ordered list of steps. All the steps provided by <code>GraphTraversal</code> inherit from the more general forms diagrammed above. A list of all the steps (and their descriptions) are provided in the TinkerPop <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html">GraphTraversal JavaDoc</a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The basics for starting a traversal are described in <a href="#the-graph-process">The Graph Process</a> section as well as in the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/getting-started/">Getting Started</a> tutorial. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> To reduce the verbosity of the expression, it is good to <code>import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.*</code>. This way, instead of doing <code>__.inE()</code> for an anonymous traversal, it is possible to simply write <code>inE()</code>. Be aware of language-specific reserved keywords when using anonymous traversals. For example, <code>in</code> and <code>as</code> are reserved keywords in Groovy, therefore you must use the verbose syntax <code>__.in()</code> and <code>__.as()</code> to avoid collisions. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The underlying <code>Step</code> implementations provided by TinkerPop should encompass most of the functionality required by a DSL author. It is important that DSL authors leverage the provided steps as then the common optimization and decoration strategies can reason on the underlying traversal sequence. If new steps are introduced, then common traversal strategies may not function properly. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="transactions">Traversal Transactions</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-coins.png" alt="gremlin coins" width="100"></span> A <a href="http://en.wikipedia.org/wiki/Database_transaction">database transaction</a> represents a unit of work to execute against the database. A traversals unit of work is affected by usage convention (i.e. the method of <a href="#connecting-gremlin">connecting</a>) and the graph provider’s transaction model. Without diving deeply into different conventions and models the most general and recommended approach to working with transactions is demonstrated as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withEmbedded(graph); <span class="comment">// or</span> GraphTraversalSource g = traversal().withRemote(conn); Transaction tx = g.tx(); <span class="comment">// spawn a GraphTraversalSource from the Transaction. Traversals spawned</span> <span class="comment">// from gtx will be essentially be bound to tx</span> GraphTraversalSource gtx = tx.begin(); <span class="keyword">try</span> { gtx.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate(); gtx.addV(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).iterate(); tx.commit(); } <span class="keyword">catch</span> (<span class="exception">Exception</span> ex) { tx.rollback(); }</code></pre> </div> </div> <div class="paragraph"> <p>The above example is straightforward and represents a good starting point for discussing the nuances of transactions in relation to the usage convention and graph provider caveats alluded to earlier.</p> </div> <div class="paragraph"> <p>Focusing on remote contexts first, note that it is still possible to issue traversals from <code>g</code>, but those will have a transaction scope outside of <code>gtx</code> and will simply <code>commit()</code> on the server if successfully executed or <code>rollback()</code> on the server otherwise (i.e. one traversal is one transaction). Each isolated transaction will require its own <code>Transaction</code> object. Multiple <code>begin()</code> calls on the same <code>Transaction</code> object will produce <code>GraphTraversalSource</code> instances that are bound to the same transaction, therefore:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withRemote(conn); Transaction tx1 = g.tx(); Transaction tx2 = g.tx(); <span class="comment">// both gtx1a and gtx1b will be bound to the same transaction</span> GraphTraversalSource gtx1a = tx1.begin(); GraphTraversalSource gtx1b = tx1.begin(); <span class="comment">// g and gtx2 will not have knowledge of what happens in tx1</span> GraphTraversalSource gtx2 = tx2.begin();</code></pre> </div> </div> <div class="paragraph"> <p>In remote cases, <code>GraphTraversalSource</code> instances spawned from <code>begin()</code> are safe to use in multiple threads though on the server side they will be processed serially as they arrive. The default behavior of <code>close()</code> on a <code>Transaction</code> for remote cases is to <code>commit()</code>, so the following re-write of the earlier example is also valid:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// note here that we dispense with creating a Transaction object and</span> <span class="comment">// simply spawn the gtx in a more inline fashion</span> GraphTraversalSource gtx = g.tx().begin(); <span class="keyword">try</span> { gtx.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate(); gtx.addV(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).iterate(); gtx.close(); } <span class="keyword">catch</span> (<span class="exception">Exception</span> ex) { tx.rollback(); }</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Transactions with non-JVM languages are always "remote". For specific transaction syntax in a particular language, please see the "Transactions" sub-section of your language of interest in the <a href="#gremlin-drivers-variants">Gremlin Drivers and Variants</a> section. </td> </tr> </table> </div> <div class="paragraph"> <p>In embedded cases, that initial recommended model for defining transactions holds, but users have more options here on deeper inspection. For embedded use cases (and perhaps even in configuration of a graph instance in Gremlin Server), the type of <code>Transaction</code> object that is returned from <code>g.tx()</code> is an important indicator as to the features of that graph’s transaction model. In most cases, inspection of that object will indicate an instance that derives from the <code>AbstractThreadLocalTransaction</code> class, which means that the transaction is bound to the current thread and therefore all traversals that execute within that thread are tied to that transaction.</p> </div> <div class="paragraph"> <p>A <code>ThreadLocal</code> transaction differs then from the remote case described before because technically any traversal spawned from <code>g</code> or from a <code>Transaction</code> will fall under the same transaction scope. As a result, it is wise, when trying to write context agnostic Gremlin, to follow the more rigid conventions of the initial example.</p> </div> <div class="paragraph"> <p>The sub-sections that follow offer a bit more insight into each of the usage contexts.</p> </div> <div class="sect2"> <h3 id="tx-embedded">Embedded</h3> <div class="paragraph"> <p>When on the JVM using an <a href="#connecting-embedded">embedded graph</a>, there is considerable flexibility for working with transactions. With the Graph API, transactions are controlled by an implementation of the <code>Transaction</code> interface and that object can be obtained from the <code>Graph</code> interface using the <code>tx()</code> method. It is important to note that the <code>Transaction</code> object does not represent a "transaction" itself. It merely exposes the methods for working with transactions (e.g. committing, rolling back, etc).</p> </div> <div class="paragraph"> <p>Most <code>Graph</code> implementations that <code>supportsTransactions</code> will implement an "automatic" <code>ThreadLocal</code> transaction, which means that when a read or write occurs after the <code>Graph</code> is instantiated, a transaction is automatically started within that thread. There is no need to manually call a method to "create" or "start" a transaction. Simply modify the graph as required and call <code>graph.tx().commit()</code> to apply changes or <code>graph.tx().rollback()</code> to undo them. When the next read or write action occurs against the graph, a new transaction will be started within that current thread of execution.</p> </div> <div class="paragraph"> <p>When using transactions in this fashion, especially in web application (e.g. HTTP server), it is important to ensure that transactions do not leak from one request to the next. In other words, unless a client is somehow bound via session to process every request on the same server thread, every request must be committed or rolled back at the end of the request. By ensuring that the request encapsulates a transaction, it ensures that a future request processed on a server thread is starting in a fresh transactional state and will not have access to the remains of one from an earlier request. A good strategy is to rollback a transaction at the start of a request, so that if it so happens that a transactional leak does occur between requests somehow, a fresh transaction is assured by the fresh request.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> The <code>tx()</code> method is on the <code>Graph</code> interface, but it is also available on the <code>TraversalSource</code> spawned from a <code>Graph</code>. Calls to <code>TraversalSource.tx()</code> are proxied through to the underlying <code>Graph</code> as a convenience. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Some graphs may throw an exception that implements <code>TemporaryException</code>. In this case, this marker interface is designed to inform the client that it may choose to retry the operation at a later time for possible success. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> TinkerPop provides for basic transaction control, however, like many aspects of TinkerPop, it is up to the graph system provider to choose the specific aspects of how their implementation will work and how it fits into the TinkerPop stack. Be sure to understand the transaction semantics of the specific graph implementation that is being utilized as it may present differing functionality than described here. </td> </tr> </table> </div> <div class="sect3"> <h4 id="_configuring">Configuring</h4> <div class="paragraph"> <p>Determining when a transaction starts is dependent upon the behavior assigned to the <code>Transaction</code>. It is up to the <code>Graph</code> implementation to determine the default behavior and unless the implementation doesn’t allow it, the behavior itself can be altered via these <code>Transaction</code> methods:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> Transaction onReadWrite(Consumer<Transaction> consumer); <span class="directive">public</span> Transaction onClose(Consumer<Transaction> consumer);</code></pre> </div> </div> <div class="paragraph"> <p>Providing a <code>Consumer</code> function to <code>onReadWrite</code> allows definition of how a transaction starts when a read or a write occurs. <code>Transaction.READ_WRITE_BEHAVIOR</code> contains pre-defined <code>Consumer</code> functions to supply to the <code>onReadWrite</code> method. It has two options:</p> </div> <div class="ulist"> <ul> <li> <p><code>AUTO</code> - automatic transactions where the transaction is started implicitly to the read or write operation</p> </li> <li> <p><code>MANUAL</code> - manual transactions where it is up to the user to explicitly open a transaction, throwing an exception if the transaction is not open</p> </li> </ul> </div> <div class="paragraph"> <p>Providing a <code>Consumer</code> function to <code>onClose</code> allows configuration of how a transaction is handled when <code>Transaction.close()</code> is called. <code>Transaction.CLOSE_BEHAVIOR</code> has several pre-defined options that can be supplied to this method:</p> </div> <div class="ulist"> <ul> <li> <p><code>COMMIT</code> - automatically commit an open transaction</p> </li> <li> <p><code>ROLLBACK</code> - automatically rollback an open transaction</p> </li> <li> <p><code>MANUAL</code> - throw an exception if a transaction is open, forcing the user to explicitly close the transaction</p> </li> </ul> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> As transactions are <code>ThreadLocal</code> in nature, so are the transaction configurations for <code>onReadWrite</code> and <code>onClose</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>Once there is an understanding for how transactions are configured, most of the rest of the <code>Transaction</code> interface is self-explanatory. Note that <a href="#neo4j-gremlin">Neo4j-Gremlin</a> is used for the examples to follow as TinkerGraph does not support transactions.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The following example is meant to demonstrate specific use of <code>ThreadLocal</code> transactions and is at odds with the more generalized transaction convention that is recommended for both embedded and remote contexts. Please be sure to understand the preferred approach described at in the <a href="#transactions">Traversal Transactions Section</a> before using this method. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[EmbeddedGraphDatabase [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> graph.features() ==>FEATURES > GraphFeatures >-- <span class="key">Transactions</span>: <span class="predefined-constant">true</span> <span class="invisible">//</span><b class="conum">1</b> >-- <span class="key">Computer</span>: <span class="predefined-constant">false</span> >-- <span class="key">Persistence</span>: <span class="predefined-constant">true</span> ... gremlin> g.tx().onReadWrite(Transaction.READ_WRITE_BEHAVIOR.AUTO) <span class="invisible">//</span><b class="conum">2</b> ==>org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph<span class="error">$</span>Neo4jTransaction<span class="error">@</span><span class="integer">1</span>c067c0d gremlin> g.addV(<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>) <span class="invisible">//</span><b class="conum">3</b> ==>v[<span class="integer">0</span>] gremlin> g.tx().commit() <span class="invisible">//</span><b class="conum">4</b> ==><span class="predefined-constant">null</span> gremlin> g.tx().onReadWrite(Transaction.READ_WRITE_BEHAVIOR.MANUAL) <span class="invisible">//</span><b class="conum">5</b> ==>org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph<span class="error">$</span>Neo4jTransaction<span class="error">@</span><span class="integer">1</span>c067c0d gremlin> g.tx().isOpen() ==><span class="predefined-constant">false</span> gremlin> g.addV(<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">6</b> Open a transaction before attempting to read/write the transaction gremlin> g.tx().open() <span class="invisible">//</span><b class="conum">7</b> ==><span class="predefined-constant">null</span> gremlin> g.addV(<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">8</b> ==>v[<span class="integer">1</span>] gremlin> g.tx().commit() ==><span class="predefined-constant">null</span></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Check <code>features</code> to ensure that the graph supports transactions.</p> </li> <li> <p>By default, <code>Neo4jGraph</code> is configured with "automatic" transactions, so it is set here for demonstration purposes only.</p> </li> <li> <p>When the vertex is added, the transaction is automatically started. From this point, more mutations can be staged or other read operations executed in the context of that open transaction.</p> </li> <li> <p>Calling <code>commit</code> finalizes the transaction.</p> </li> <li> <p>Change transaction behavior to require manual control.</p> </li> <li> <p>Adding a vertex now results in failure because the transaction was not explicitly opened.</p> </li> <li> <p>Explicitly open a transaction.</p> </li> <li> <p>Adding a vertex now succeeds as the transaction was manually opened.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> It may be important to consult the documentation of the <code>Graph</code> implementation you are using when it comes to the specifics of how transactions will behave. TinkerPop allows some latitude in this area and implementations may not have the exact same behaviors and <a href="https://en.wikipedia.org/wiki/ACID">ACID</a> guarantees. </td> </tr> </table> </div> </div> </div> <div class="sect2"> <h3 id="tx-gremlin-server">Gremlin Server</h3> <div class="paragraph"> <p>The available capability for transactions with <a href="#gremlin-server">Gremlin Server</a> is dependent upon the method of interaction that is used. The preferred method for <a href="#connecting-gremlin-server">interacting with Gremlin Server</a> is via websockets and bytecode based requests. The start of the <a href="#transactions">Transactions Section</a> describes this approach in detail with examples.</p> </div> <div class="paragraph"> <p>Gremlin Server also has the option to accept Gremlin-based scripts. The scripting approach provides access to the Graph API and thus also the transactional model described in the <a href="#tx-embedded">embedded</a> section. Therefore a single script can have the ability to execute multiple transactions per request with complete control provided to the developer to commit or rollback transactions as needed.</p> </div> <div class="paragraph"> <p>There are two methods for sending scripts to Gremlin Server: sessionless and session-based. With sessionless requests there will always be an attempt to close the transaction at the end of the request with a commit if there are no errors or a rollback if there is a failure. It is therefore unnecessary to close transactions manually within scripts themselves. By default, session-based requests do not have this quality. The transaction will be held open on the server until the user closes it manually. There is an option to have automatic transaction management for sessions. More information on this topic can be found in the <a href="#considering-transactions">Considering Transactions</a> Section and the <a href="#sessions">Considering Sessions</a> Section.</p> </div> </div> <div class="sect2"> <h3 id="tx-rgp">Remote Gremlin Providers</h3> <div class="paragraph"> <p>At this time, transactional patterns for Remote Gremlin Providers are largely in line with Gremlin Server. As most of RGPs do not expose a <code>Graph</code> instance, access to lower level transactional functions available to embedded graphs even in a sessionless fashion are not typically permitted. For example, without a <code>Graph</code> instance it is not possible to <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#tx-embedded">configure</a> transaction close or read-write behaviors. The nature of what a "transaction" means will be dependent on the RGP as is the case with any TinkerPop-enabled graph system, so it is important to consult that systems documentation for more details.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="configuration-steps">Configuration Steps</h2> <div class="sectionbody"> <div class="paragraph"> <p>Many of the methods on the <code>GraphTraversalSource</code> are meant to configure the source for usage. These configuration affect the manner in which a traversals are spawned from it. Configuration methods can be identified by their names with make use of "with" as a prefix:</p> </div> <div class="sect2"> <h3 id="configuration-steps-with">With Configuration</h3> <div class="paragraph"> <p>The <code>with()</code> configuration adds arbitrary data to a <code>TraversalSource</code> which can then be used by graph providers as configuration options for a traversal execution. This configuration is similar to <a href="#with-step">with()</a>-modulator which has similar functionality when applied to an individual step.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.with(<span class="string"><span class="delimiter">'</span><span class="content">providerDefinedVariable</span><span class="delimiter">'</span></span>, <span class="float">0.33</span>).V()</code></pre> </div> </div> <div class="paragraph"> <p>The <code>0.33</code> value for the "providerDefinedVariable" will be bound to each traversal spawned that way. Consult the graph system being used to determine if any such configuration options are available.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withbulk">WithBulk Configuration</h3> <div class="paragraph"> <p>The <code>withBulk()</code> configuration allows for control of bulking operations. This value is <code>true</code> by default allowing for normal <a href="#barrier-step">bulking</a> operations, but when set to <code>false</code>, introduces a subtle change in that behavior as shown in examples in <a href="#sack-step">sack()-step</a>.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withcomputer">WithComputer Configuration</h3> <div class="paragraph"> <p>The <code>withComputer()</code> configuration adds a <code>Computer</code> that will be used to process the traversal and is necessary for OLAP based processing and steps that require that processing. See <a href="#sparkgraphcomputer">examples</a> related to <code>SparkGraphComputer</code> or see examples in the computer required steps, like <a href="#pagerank-step">pageRank()</a> or <a href="#shortestpath-shortestPath()">[shortestpath-shortestPath()]</a>.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withsack">WithSack Configuration</h3> <div class="paragraph"> <p>The <code>withSack()</code> configuration adds a "sack" that can be accessed by traversals spawned from this source. This functionality is shown in more detail in the examples for (<a href="#sack-step">sack()</a>)-step.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withsideeffect">WithSideEffect Configuration</h3> <div class="paragraph"> <p>The <code>withSideEffect()</code> configuration adds an arbitrary <code>Object</code> to traversals spawned from this source which can be accessed as a side-effect given the supplied key.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-1" type="radio" name="radio-set-1729796988-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-2" type="radio" name="radio-set-1729796988-1" class="tab-selector-2" /> <label for="tab-1729796988-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> 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">dog</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cat</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">fish</span><span class="delimiter">'</span></span>]). 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>).select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).unfold() ==>dog ==>cat ==>fish</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">dog</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cat</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">fish</span><span class="delimiter">'</span></span>]). 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>).select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).unfold()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>More practical examples can be found in other examples elsewhere in the documentation. The <code>math()</code>-step <a href="#math-step">example</a> and the <code>where()</code>-step <a href="#where-step">example</a> should both be helpful in examining this configuration step more closely.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withstrategies">WithStrategies Configuration</h3> <div class="paragraph"> <p>The <code>withStrategies()</code> configuration allows inclusion of additional <code>TraversalStrategy</code> instances to be applied to any traversals spawned from the configured source. Please see the <a href="#traversalstrategy">Traversal Strategy Section</a> for more details on how this configuration works.</p> </div> </div> <div class="sect2"> <h3 id="configuration-steps-withoutstrategies">WithoutStrategies Configuration</h3> <div class="paragraph"> <p>The <code>withoutStrategies()</code> configuration removes a particular <code>TraversalStrategy</code> from those to be applied to traversals spawned from the configured source. Please see the <a href="#traversalstrategy">Traversal Strategy Section</a> for more details on how this configuration works.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="start-steps">Start Steps</h2> <div class="sectionbody"> <div class="paragraph"> <p>Not all steps are capable of starting a <code>GraphTraversal</code>. Only those steps on the <code>GraphTraversalSource</code> can do that. Many of the methods on <code>GraphTraversalSource</code> are actually for its <a href="#configuration-steps">configuration</a> and start steps should not be confused with those.</p> </div> <div class="paragraph"> <p>Spawn steps, which actually yield a traversal, typically match the names of existing steps:</p> </div> <div class="ulist"> <ul> <li> <p><code>addE()</code> - Adds an <code>Edge</code> to start the traversal (<a href="#addedge-step">example</a>).</p> </li> <li> <p><code>addV()</code> - Adds a <code>Vertex</code> to start the traversal (<a href="#addvertex-step">example</a>).</p> </li> <li> <p><code>call()</code> - Makes a provider-specific service call to start the traversal (<a href="#call-step">example</a>).</p> </li> <li> <p><code>E()</code> - Reads edges from the graph to start the traversal (<a href="#e-step">example</a>).</p> </li> <li> <p><code>inject()</code> - Inserts arbitrary objects to start the traversal (<a href="#inject-step">example</a>).</p> </li> <li> <p><code>mergeE()</code> - Adds an <code>Edge</code> in a "create if not exist" fashion to start the traversal (<a href="#mergeedge-step">example</a>)</p> </li> <li> <p><code>mergeV()</code> - Adds a <code>Vertex</code> in a "create if not exist" fashion to start the traversal (<a href="#mergevertex-step">example</a>)</p> </li> <li> <p><code>union()</code> - Merges the results of an arbitrary number of child traversals to start the traversal (<a href="#union-step">example</a>).</p> </li> <li> <p><code>V()</code> - Reads vertices from the graph to start the traversal (<a href="#graph-step">example</a>).</p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="graph-traversal-steps">Graph Traversal Steps</h2> <div class="sectionbody"> <div class="paragraph"> <p>Gremlin steps are chained together to produce the actual traversal and are triggered by way of <a href="#start-steps">start steps</a> on the <code>GraphTraversalSource</code>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> More details about the Gremlin language can be found in the Provider Documentation within the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#gremlin-semantics">Gremlin Semantics Section</a>. </td> </tr> </table> </div> <div class="sect2"> <h3 id="general-steps">General Steps</h3> <div class="paragraph"> <p>There are five general steps, each having a traversal and a lambda representation, by which all other specific steps described later extend.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 45.4545%;"> <col style="width: 54.5455%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Step</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>map(Traversal<S, E>)</code> <code>map(Function<Traverser<S>, E>)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">map the traverser to some object of type <code>E</code> for the next step to process.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>flatMap(Traversal<S, E>)</code> <code>flatMap(Function<Traverser<S>, Iterator<E>>)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">map the traverser to an iterator of <code>E</code> objects that are streamed to the next step.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>filter(Traversal<?, ?>)</code> <code>filter(Predicate<Traverser<S>>)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">map the traverser to either true or false, where false will not pass the traverser to the next step.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>sideEffect(Traversal<S, S>)</code> <code>sideEffect(Consumer<Traverser<S>>)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">perform some operation on the traverser and pass it to the next step.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>branch(Traversal<S, M>)</code> <code>branch(Function<Traverser<S>,M>)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">split the traverser to all the traversals indexed by the <code>M</code> token.</p></td> </tr> </tbody> </table> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Lambda steps are presented for educational purposes as they represent the foundational constructs of the Gremlin language. In practice, lambda steps should be avoided in favor of their traversals representation and traversal verification strategies exist to disallow their use unless explicitly "turned off." For more information on the problems with lambdas, please read <a href="#a-note-on-lambdas">A Note on Lambdas</a>. </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>Traverser<S></code> object provides access to:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>The current traversed <code>S</code> object — <code>Traverser.get()</code>.</p> </li> <li> <p>The current path traversed by the traverser — <code>Traverser.path()</code>.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>A helper shorthand to get a particular path-history object — <code>Traverser.path(String) == Traverser.path().get(String)</code>.</p> </li> </ol> </div> </li> <li> <p>The number of times the traverser has gone through the current loop — <code>Traverser.loops()</code>.</p> </li> <li> <p>The number of objects represented by this traverser — <code>Traverser.bulk()</code>.</p> </li> <li> <p>The local data structure associated with this traverser — <code>Traverser.sack()</code>.</p> </li> <li> <p>The side-effects associated with the traversal — <code>Traverser.sideEffects()</code>.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>A helper shorthand to get a particular side-effect — <code>Traverser.sideEffect(String) == Traverser.sideEffects().get(String)</code>.</p> </li> </ol> </div> </li> </ol> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/map-lambda.png" alt="map lambda" width="150"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-3" type="radio" name="radio-set-1729796988-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-4" type="radio" name="radio-set-1729796988-3" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V(<span class="integer">1</span>).out().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">(1)</b> ==>lop ==>vadas ==>josh gremlin> g.V(<span class="integer">1</span>).out().map {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)} <span class="comment">//</span>// <b class="conum">(2)</b> ==>lop ==>vadas ==>josh gremlin> g.V(<span class="integer">1</span>).out().map(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">(3)</b> ==>lop ==>vadas ==>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(<span class="integer">1</span>).out().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">(1)</b> g.V(<span class="integer">1</span>).out().map {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)} <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).out().map(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">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>An outgoing traversal from vertex 1 to the name values of the adjacent vertices.</p> </li> <li> <p>The same operation, but using a lambda to access the name property values.</p> </li> <li> <p>Again the same operation, but using the traversal representation of <code>map()</code>.</p> </li> </ol> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/filter-lambda.png" alt="filter lambda" width="160"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-5" type="radio" name="radio-set-1729796988-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-6" type="radio" name="radio-set-1729796988-5" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().filter {<span class="local-variable">it</span>.get().label() == <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>} <span class="comment">//</span>// <b class="conum">(1)</b> ==>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> g.V().filter(label().is(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==>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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</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().filter {<span class="local-variable">it</span>.get().label() == <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>} <span class="comment">//</span>// <b class="conum">(1)</b> g.V().filter(label().is(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)) <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>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A filter that only allows the vertex to pass if it has the "person" label</p> </li> <li> <p>The same operation, but using the traversal representation of <code>filter()</code>.</p> </li> <li> <p>The more specific <code>has()</code>-step is implemented as a <code>filter()</code> with respective predicate.</p> </li> </ol> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/side-effect-lambda.png" alt="side effect lambda" width="175"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-7" type="radio" name="radio-set-1729796988-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-8" type="radio" name="radio-set-1729796988-7" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).sideEffect(<span class="predefined-type">System</span>.out.&println) <span class="comment">//</span>// <b class="conum">(1)</b> v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] v[<span class="integer">2</span>] ==>v[<span class="integer">2</span>] v[<span class="integer">4</span>] ==>v[<span class="integer">4</span>] v[<span class="integer">6</span>] ==>v[<span class="integer">6</span>] gremlin> g.V().sideEffect(outE().count().aggregate(local,<span class="string"><span class="delimiter">"</span><span class="content">o</span><span class="delimiter">"</span></span>)). sideEffect(inE().count().aggregate(local,<span class="string"><span class="delimiter">"</span><span class="content">i</span><span class="delimiter">"</span></span>)).cap(<span class="string"><span class="delimiter">"</span><span class="content">o</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">i</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">i</span>:[<span class="integer">0</span>,<span class="integer">0</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">3</span>],<span class="key">o</span>:[<span class="integer">3</span>,<span class="integer">0</span>,<span class="integer">0</span>,<span class="integer">0</span>,<span class="integer">2</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>).sideEffect(<span class="predefined-type">System</span>.out.&println) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().sideEffect(outE().count().aggregate(local,<span class="string"><span class="delimiter">"</span><span class="content">o</span><span class="delimiter">"</span></span>)). sideEffect(inE().count().aggregate(local,<span class="string"><span class="delimiter">"</span><span class="content">i</span><span class="delimiter">"</span></span>)).cap(<span class="string"><span class="delimiter">"</span><span class="content">o</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">i</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>Whatever enters <code>sideEffect()</code> is passed to the next step, but some intervening process can occur.</p> </li> <li> <p>Compute the out- and in-degree for each vertex. Both <code>sideEffect()</code> are fed with the same vertex.</p> </li> </ol> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/branch-lambda.png" alt="branch lambda" width="180"></span></p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-9" type="radio" name="radio-set-1729796988-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-10" type="radio" name="radio-set-1729796988-9" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().branch {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. option(<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>)). option(none, 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">(1)</b> ==><span class="integer">29</span> ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V().branch(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(none, 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">(2)</b> ==><span class="integer">29</span> ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V().choose(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>), 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">(3)</b> ==><span class="integer">29</span> ==>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().branch {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. option(<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>)). option(none, 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">(1)</b> g.V().branch(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(none, 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">(2)</b> g.V().choose(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>), 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">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>If the vertex is "marko", get his age, else get the name of the vertex.</p> </li> <li> <p>The same operation, but using the traversal representing of <code>branch()</code>.</p> </li> <li> <p>The more specific boolean-based <code>choose()</code>-step is implemented as a <code>branch()</code>.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="terminal-steps">Terminal Steps</h3> <div class="paragraph"> <p>Typically, when a step is concatenated to a traversal a traversal is returned. In this way, a traversal is built up in a <a href="https://en.wikipedia.org/wiki/Fluent_interface">fluent</a>, <a href="https://en.wikipedia.org/wiki/Monoid">monadic</a> fashion. However, some steps do not return a traversal, but instead, execute the traversal and return a result. These steps are known as terminal steps (<strong>terminal</strong>) and they are explained via the examples below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-11" type="radio" name="radio-set-1729796988-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-12" type="radio" name="radio-set-1729796988-11" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).hasNext() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="predefined-constant">true</span> gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">3</span>] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).next(<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">nothing</span><span class="delimiter">'</span></span>).tryNext() <span class="comment">//</span>// <b class="conum">(4)</b> ==>Optional.empty gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toList() <span class="comment">//</span>// <b class="conum">(5)</b> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">3</span>] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toSet() <span class="comment">//</span>// <b class="conum">(6)</b> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toBulkSet() <span class="comment">//</span>// <b class="conum">(7)</b> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] gremlin> results = [<span class="string"><span class="delimiter">'</span><span class="content">blah</span><span class="delimiter">'</span></span>,<span class="integer">3</span>] ==>blah ==><span class="integer">3</span> gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).fill(results) <span class="comment">//</span>// <b class="conum">(8)</b> ==>blah ==><span class="integer">3</span> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">3</span>] gremlin> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate() <span class="comment">//</span>// <b class="conum">(9)</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().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).hasNext() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(2)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).next(<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">nothing</span><span class="delimiter">'</span></span>).tryNext() <span class="comment">//</span>// <b class="conum">(4)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toList() <span class="comment">//</span>// <b class="conum">(5)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toSet() <span class="comment">//</span>// <b class="conum">(6)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).toBulkSet() <span class="comment">//</span>// <b class="conum">(7)</b> results = [<span class="string"><span class="delimiter">'</span><span class="content">blah</span><span class="delimiter">'</span></span>,<span class="integer">3</span>] g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).fill(results) <span class="comment">//</span>// <b class="conum">(8)</b> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate() <span class="invisible">//</span><b class="conum">9</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>hasNext()</code> determines whether there are available results (not supported in <code>gremlin-javascript</code>).</p> </li> <li> <p><code>next()</code> will return the next result.</p> </li> <li> <p><code>next(n)</code> will return the next <code>n</code> results in a list (not supported in <code>gremlin-javascript</code> or Gremlin.NET).</p> </li> <li> <p><code>tryNext()</code> will return an <code>Optional</code> and thus, is a composite of <code>hasNext()</code>/<code>next()</code> (only supported for JVM languages).</p> </li> <li> <p><code>toList()</code> will return all results in a list.</p> </li> <li> <p><code>toSet()</code> will return all results in a set and thus, duplicates removed (not supported in <code>gremlin-javascript</code>).</p> </li> <li> <p><code>toBulkSet()</code> will return all results in a weighted set and thus, duplicates preserved via weighting (only supported for JVM languages).</p> </li> <li> <p><code>fill(collection)</code> will put all results in the provided collection and return the collection when complete (only supported for JVM languages).</p> </li> <li> <p><code>iterate()</code> does not exactly fit the definition of a terminal step in that it doesn’t return a result, but still returns a traversal - it does however behave as a terminal step in that it iterates the traversal and generates side effects without returning the actual result.</p> </li> </ol> </div> <div class="paragraph"> <p>There is also the <code>promise()</code> terminator step, which can only be used with remote traversals to <a href="#connecting-gremlin-server">Gremlin Server</a> or <a href="#connecting-rgp">RGPs</a>. It starts a promise to execute a function on the current <code>Traversal</code> that will be completed in the future.</p> </div> <div class="paragraph"> <p>Finally, <a href="#explain-step"><code>explain()</code></a>-step is also a terminal step and is described in its own section.</p> </div> </div> <div class="sect2"> <h3 id="addedge-step">AddE Step</h3> <div class="paragraph"> <p><a href="http://en.wikipedia.org/wiki/Automated_reasoning">Reasoning</a> is the process of making explicit what is implicit in the data. What is explicit in a graph are the objects of the graph — i.e. vertices and edges. What is implicit in the graph is the traversal. In other words, traversals expose meaning where the meaning is determined by the traversal definition. For example, take the concept of a "co-developer." Two people are co-developers if they have worked on the same project together. This concept can be represented as a traversal and thus, the concept of "co-developers" can be derived. Moreover, what was once implicit can be made explicit via the <code>addE()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>).</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/addedge-step.png" alt="addedge step" width="450"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-13" type="radio" name="radio-set-1729796988-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-14" type="radio" name="radio-set-1729796988-13" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(neq(<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">co-developer</span><span class="delimiter">'</span></span>).from(<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">year</span><span class="delimiter">'</span></span>,<span class="integer">2009</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>e[<span class="integer">0</span>][<span class="integer">1</span>-co-developer-><span class="integer">4</span>] ==>e[<span class="integer">13</span>][<span class="integer">1</span>-co-developer-><span class="integer">6</span>] gremlin> g.V(<span class="integer">3</span>,<span class="integer">4</span>,<span class="integer">5</span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</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">josh</span><span class="delimiter">'</span></span>).as(<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">x</span><span class="delimiter">'</span></span>).unfold().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).addE(<span class="string"><span class="delimiter">'</span><span class="content">createdBy</span><span class="delimiter">'</span></span>).to(<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> ==>e[<span class="integer">14</span>][<span class="integer">3</span>-createdBy-><span class="integer">4</span>] ==>e[<span class="integer">15</span>][<span class="integer">5</span>-createdBy-><span class="integer">4</span>] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).addE(<span class="string"><span class="delimiter">'</span><span class="content">createdBy</span><span class="delimiter">'</span></span>).to(<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">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>e[<span class="integer">16</span>][<span class="integer">3</span>-createdBy-><span class="integer">1</span>] ==>e[<span class="integer">17</span>][<span class="integer">5</span>-createdBy-><span class="integer">4</span>] ==>e[<span class="integer">18</span>][<span class="integer">3</span>-createdBy-><span class="integer">4</span>] ==>e[<span class="integer">19</span>][<span class="integer">3</span>-createdBy-><span class="integer">6</span>] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">livesNear</span><span class="delimiter">'</span></span>).from(<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">year</span><span class="delimiter">'</span></span>,<span class="integer">2009</span>). inV().inE(<span class="string"><span class="delimiter">'</span><span class="content">livesNear</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">year</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="integer">2009</span> ==><span class="integer">2009</span> gremlin> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>)). addE(<span class="string"><span class="delimiter">'</span><span class="content">friendlyCollaborator</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>). property(id,<span class="integer">23</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">project</span><span class="delimiter">'</span></span>,select(<span class="string"><span class="delimiter">'</span><span class="content">c</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">(5)</b> ==>e[<span class="integer">23</span>][<span class="integer">1</span>-friendlyCollaborator-><span class="integer">4</span>] gremlin> g.E(<span class="integer">23</span>).valueMap() ==>[<span class="key">project</span>:lop] gremlin> 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() ==>v[<span class="integer">1</span>] gremlin> vPeter = 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">peter</span><span class="delimiter">'</span></span>).next() ==>v[<span class="integer">6</span>] gremlin> g.V(vMarko).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(vPeter) <span class="comment">//</span>// <b class="conum">(6)</b> ==>e[<span class="integer">22</span>][<span class="integer">1</span>-knows-><span class="integer">6</span>] gremlin> g.addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(vMarko).to(vPeter) <span class="comment">//</span>// <b class="conum">(7)</b> ==>e[<span class="integer">24</span>][<span class="integer">1</span>-knows-><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(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(neq(<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">co-developer</span><span class="delimiter">'</span></span>).from(<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">year</span><span class="delimiter">'</span></span>,<span class="integer">2009</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">3</span>,<span class="integer">4</span>,<span class="integer">5</span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</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">josh</span><span class="delimiter">'</span></span>).as(<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">x</span><span class="delimiter">'</span></span>).unfold().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).addE(<span class="string"><span class="delimiter">'</span><span class="content">createdBy</span><span class="delimiter">'</span></span>).to(<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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).addE(<span class="string"><span class="delimiter">'</span><span class="content">createdBy</span><span class="delimiter">'</span></span>).to(<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">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">public</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). addE(<span class="string"><span class="delimiter">'</span><span class="content">livesNear</span><span class="delimiter">'</span></span>).from(<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">year</span><span class="delimiter">'</span></span>,<span class="integer">2009</span>). inV().inE(<span class="string"><span class="delimiter">'</span><span class="content">livesNear</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">year</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>)). addE(<span class="string"><span class="delimiter">'</span><span class="content">friendlyCollaborator</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>). property(id,<span class="integer">23</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">project</span><span class="delimiter">'</span></span>,select(<span class="string"><span class="delimiter">'</span><span class="content">c</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">(5)</b> g.E(<span class="integer">23</span>).valueMap() 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() vPeter = 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">peter</span><span class="delimiter">'</span></span>).next() g.V(vMarko).addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to(vPeter) <span class="comment">//</span>// <b class="conum">(6)</b> g.addE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).from(vMarko).to(vPeter) <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Add a co-developer edge with a year-property between marko and his collaborators.</p> </li> <li> <p>Add incoming createdBy edges from the josh-vertex to the lop- and ripple-vertices.</p> </li> <li> <p>Add an inverse createdBy edge for all created edges.</p> </li> <li> <p>The newly created edge is a traversable object.</p> </li> <li> <p>Two arbitrary bindings in a traversal can be joined <code>from()</code>→<code>to()</code>, where <code>id</code> can be provided for graphs that supports user provided ids.</p> </li> <li> <p>Add an edge between marko and peter given the directed (detached) vertex references.</p> </li> <li> <p>Add an edge between marko and peter given the directed (detached) vertex references.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addE(java.lang.String)"><code>addE(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addE(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>addE(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="addvertex-step">AddV Step</h3> <div class="paragraph"> <p>The <code>addV()</code>-step is used to add vertices to the graph (<strong>map</strong>/<strong>sideEffect</strong>). For every incoming object, a vertex is created. Moreover, <code>GraphTraversalSource</code> maintains an <code>addV()</code> method.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-15" type="radio" name="radio-set-1729796988-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-16" type="radio" name="radio-set-1729796988-15" class="tab-selector-2" /> <label for="tab-1729796988-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> 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">stephen</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">0</span>] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>stephen ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">nothing</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">13</span>] ==>v[<span class="integer">15</span>] gremlin> 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">nothing</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">13</span>] ==>v[<span class="integer">15</span>] gremlin> 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">nothing</span><span class="delimiter">'</span></span>).bothE()</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">stephen</span><span class="delimiter">'</span></span>) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">nothing</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">nothing</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">nothing</span><span class="delimiter">'</span></span>).bothE()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV()"><code>addV()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV(java.lang.String)"><code>addV(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#addV(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>addV(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="aggregate-step"><a id="store-step"></a>Aggregate Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/aggregate-step.png" alt="aggregate step" width="800"> </div> </div> <div class="paragraph"> <p>The <code>aggregate()</code>-step (<strong>sideEffect</strong>) is used to aggregate all the objects at a particular point of traversal into a <code>Collection</code>. The step is uses <code>Scope</code> to help determine the aggregating behavior. For <code>global</code> scope this means that the step will use <a href="http://en.wikipedia.org/wiki/Eager_evaluation">eager evaluation</a> in that no objects continue on until all previous objects have been fully aggregated. The eager evaluation model is crucial in situations where everything at a particular point is required for future computation. By default, when the overload of <code>aggregate()</code> is called without a <code>Scope</code>, the default is <code>global</code>. An example is provided below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-17" type="radio" name="radio-set-1729796988-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-18" type="radio" name="radio-set-1729796988-17" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(global, <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> ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">6</span>] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). where(without(<span class="string"><span class="delimiter">'</span><span class="content">x</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">(6)</b> ==>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.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(global, <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> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). where(without(<span class="string"><span class="delimiter">'</span><span class="content">x</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">6</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>What has marko created?</p> </li> <li> <p>Aggregate all his creations.</p> </li> <li> <p>Identical to the previous line.</p> </li> <li> <p>Who are marko’s collaborators?</p> </li> <li> <p>What have marko’s collaborators created?</p> </li> <li> <p>What have marko’s collaborators created that he hasn’t created?</p> </li> </ol> </div> <div class="paragraph"> <p>In <a href="http://en.wikipedia.org/wiki/Recommender_system">recommendation systems</a>, the above pattern is used:</p> </div> <div class="literalblock"> <div class="content"> <pre>"What has userA liked? Who else has liked those things? What have they liked that userA hasn't already liked?"</pre> </div> </div> <div class="paragraph"> <p>Finally, <code>aggregate()</code>-step can be modulated via <code>by()</code>-projection.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-19" type="radio" name="radio-set-1729796988-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-20" type="radio" name="radio-set-1729796988-19" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[v[<span class="integer">2</span>],v[<span class="integer">4</span>]] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<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>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[vadas,josh] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<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">age</span><span class="delimiter">'</span></span>).cap(<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> ==>[<span class="integer">27</span>,<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().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<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>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).aggregate(<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">age</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are not included in the aggregation.</p> </li> </ol> </div> <div class="paragraph"> <p>For <code>local</code> scope the aggregation will occur in a <a href="http://en.wikipedia.org/wiki/Lazy_evaluation">lazy</a> fashion.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Prior to 3.4.3, <code>local</code> aggregation (i.e. lazy) evaluation was handled by <code>store()</code>-step. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-21" type="radio" name="radio-set-1729796988-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-22" type="radio" name="radio-set-1729796988-21" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().aggregate(global, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[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> g.V().aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[v[<span class="integer">1</span>]] gremlin> g.withoutStrategies(EarlyLimitStrategy).V().aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[v[<span class="integer">1</span>],v[<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().aggregate(global, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.V().aggregate(local, <span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.withoutStrategies(EarlyLimitStrategy).V().aggregate(local,<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).limit(<span class="integer">1</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 class="paragraph"> <p>It is important to note that <code>EarlyLimitStrategy</code> introduced in 3.3.5 alters the behavior of <code>aggregate(local)</code>. Without that strategy (which is installed by default), there are two results in the <code>aggregate()</code> side-effect even though the interval selection is for 1 object. Realize that when the second object is on its way to the <code>range()</code> filter (i.e. <code>[0..1]</code>), it passes through <code>aggregate()</code> and thus, stored before filtered.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-23" type="radio" name="radio-set-1729796988-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-24" type="radio" name="radio-set-1729796988-23" class="tab-selector-2" /> <label for="tab-1729796988-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> g.E().aggregate(local,<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">weight</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>[<span class="float">0.5</span>,<span class="float">1.0</span>,<span class="float">1.0</span>,<span class="float">0.4</span>,<span class="float">0.4</span>,<span class="float">0.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.E().aggregate(local,<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">weight</span><span class="delimiter">'</span></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 class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#aggregate(java.lang.String)"><code>aggregate(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#aggregate(org.apache.tinkerpop.gremlin.process.traversal.Scope,java.lang.String)"><code>aggregate(Scope,String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="all-step">All Step</h3> <div class="paragraph"> <p>It is possible to filter list traversers using <code>all()</code>-step (<strong>filter</strong>). Every item in the list will be tested against the supplied predicate and if all of the items pass then the traverser is passed along the stream, otherwise it is filtered. Empty lists are passed along but null or non-iterable traversers are filtered out.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>all</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>all_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-25" type="radio" name="radio-set-1729796988-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-26" type="radio" name="radio-set-1729796988-25" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().all(gt(<span class="integer">25</span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">29</span>,<span class="integer">27</span>,<span class="integer">32</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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().all(gt(<span class="integer">25</span>)) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Return the list of ages only if everyone’s age is greater than 25.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#all(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>all(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/P.html"><code>P</code></a></p> </div> </div> <div class="sect2"> <h3 id="and-step">And Step</h3> <div class="paragraph"> <p>The <code>and()</code>-step ensures that all provided traversals yield a result (<strong>filter</strong>). Please see <a href="#or-step"><code>or()</code></a> for or-semantics.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>and</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>and_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-27" type="radio" name="radio-set-1729796988-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-28" type="radio" name="radio-set-1729796988-27" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().and( outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>), values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lt(<span class="integer">30</span>))). values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko</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().and( outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>), values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lt(<span class="integer">30</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 <code>and()</code>-step can take an arbitrary number of traversals. All traversals must produce at least one output for the original traverser to pass to the next step.</p> </div> <div class="paragraph"> <p>An <a href="http://en.wikipedia.org/wiki/Infix_notation">infix notation</a> can be used as well.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-29" type="radio" name="radio-set-1729796988-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-30" type="radio" name="radio-set-1729796988-29" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().where(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).and().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko</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(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).and().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#and(org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>and(Traversal…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="any-step">Any Step</h3> <div class="paragraph"> <p>It is possible to filter list traversers using <code>any()</code>-step (<strong>filter</strong>). All items in the list will be tested against the supplied predicate and if any of the items pass then the traverser is passed along the stream, otherwise it is filtered. Empty lists, null traversers, and non-iterable traversers are filtered out as well.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>any</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>any_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-31" type="radio" name="radio-set-1729796988-31" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-31" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-32" type="radio" name="radio-set-1729796988-31" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().any(gt(<span class="integer">25</span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">29</span>,<span class="integer">27</span>,<span class="integer">32</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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().any(gt(<span class="integer">25</span>)) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Return the list of ages if anyone’s age is greater than 25.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#any(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>any(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/P.html"><code>P</code></a></p> </div> </div> <div class="sect2"> <h3 id="as-step">As Step</h3> <div class="paragraph"> <p>The <code>as()</code>-step is not a real step, but a "step modulator" similar to <a href="#by-step"><code>by()</code></a> and <a href="#option-step"><code>option()</code></a>. With <code>as()</code>, it is possible to provide a label to the step that can later be accessed by steps and data structures that make use of such labels — e.g., <a href="#select-step"><code>select()</code></a>, <a href="#match-step"><code>match()</code></a>, and path.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Groovy</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>as</code> is a reserved word in Groovy, and when therefore used as part of an anonymous traversal must be referred to in Gremlin with the double underscore <code>__.as()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>as</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>as_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-33" type="radio" name="radio-set-1729796988-33" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-33" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-34" type="radio" name="radio-set-1729796988-33" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</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="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">5</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">3</span>]] ==>[<span class="key">a</span>:v[<span class="integer">6</span>],<span class="key">b</span>:v[<span class="integer">3</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:lop] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:ripple] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:lop] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:lop]</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>).out(<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">b</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="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>).out(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</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>Select the objects labeled "a" and "b" from the path.</p> </li> <li> <p>Select the objects labeled "a" and "b" from the path and, for each object, project its name value.</p> </li> </ol> </div> <div class="paragraph"> <p>A step can have any number of labels associated with it. This is useful for referencing the same step multiple times in a future step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-35" type="radio" name="radio-set-1729796988-35" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-35" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-36" type="radio" name="radio-set-1729796988-35" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).as(<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>). 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>). by(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()) ==>[<span class="key">a</span>:lop,<span class="key">b</span>:java,<span class="key">c</span>:[marko,josh,peter]] ==>[<span class="key">a</span>:ripple,<span class="key">b</span>:java,<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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).as(<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>). 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>). by(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#as(java.lang.String,java.lang.String...)"><code>as(String,String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="asString-step">AsString Step</h3> <div class="paragraph"> <p>The <code>asString()</code>-step (<strong>map</strong>) returns the value of incoming traverser as strings. Null values are returned unchanged.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-37" type="radio" name="radio-set-1729796988-37" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-37" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-38" type="radio" name="radio-set-1729796988-37" class="tab-selector-2" /> <label for="tab-1729796988-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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).asString() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">29</span> ==><span class="integer">27</span> ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).asString().concat(<span class="string"><span class="delimiter">'</span><span class="content"> years old</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="integer">29</span> years old ==><span class="integer">27</span> years old ==><span class="integer">32</span> years old ==><span class="integer">35</span> years old gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().asString(local) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="integer">29</span>,<span class="integer">27</span>,<span class="integer">32</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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).asString() <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>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).asString().concat(<span class="string"><span class="delimiter">'</span><span class="content"> years old</span><span class="delimiter">'</span></span>) <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>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().asString(local) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Return ages as string.</p> </li> <li> <p>Return ages as string and use concat to generate phrases.</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asString()"><code>asString()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asString(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>asString(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="asDate-step">AsDate Step</h3> <div class="paragraph"> <p>The <code>asDate()</code>-step (<strong>map</strong>) converts string or numeric input to Date.</p> </div> <div class="paragraph"> <p>For string input only ISO-8601 format is supported. For numbers, the value is considered as the number of the milliseconds since "the epoch" (January 1, 1970, 00:00:00 GMT). Date input is passed without changes.</p> </div> <div class="paragraph"> <p>If the incoming traverser is not a string, number or Date then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-39" type="radio" name="radio-set-1729796988-39" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-39" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-40" type="radio" name="radio-set-1729796988-39" class="tab-selector-2" /> <label for="tab-1729796988-40" 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> g.inject(<span class="integer">1690934400000</span>).asDate() <span class="comment">//</span>// <b class="conum">(1)</b> ==>Wed Aug <span class="octal">02</span> <span class="octal">00</span>:<span class="octal">00</span>:<span class="octal">00</span> UTC <span class="integer">2023</span> gremlin> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate() <span class="comment">//</span>// <b class="conum">(2)</b> ==>Wed Aug <span class="octal">02</span> <span class="octal">00</span>:<span class="octal">00</span>:<span class="octal">00</span> UTC <span class="integer">2023</span> gremlin> g.inject(datetime(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-24T00:00:00Z</span><span class="delimiter">"</span></span>)).asDate() <span class="comment">//</span>// <b class="conum">(3)</b> ==>Thu Aug <span class="integer">24</span> <span class="octal">00</span>:<span class="octal">00</span>:<span class="octal">00</span> UTC <span class="integer">2023</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.inject(<span class="integer">1690934400000</span>).asDate() <span class="comment">//</span>// <b class="conum">(1)</b> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate() <span class="comment">//</span>// <b class="conum">(2)</b> g.inject(datetime(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-24T00:00:00Z</span><span class="delimiter">"</span></span>)).asDate() <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Convert number to Date</p> </li> <li> <p>Convert ISO-8601 string to Date</p> </li> <li> <p>Pass Date without modification</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asDate()"><code>asDate()</code></a></p> </div> </div> <div class="sect2"> <h3 id="barrier-step">Barrier Step</h3> <div class="paragraph"> <p>The <code>barrier()</code>-step (<strong>barrier</strong>) turns the lazy traversal pipeline into a bulk-synchronous pipeline. This step is useful in the following situations:</p> </div> <div class="ulist"> <ul> <li> <p>When everything prior to <code>barrier()</code> needs to be executed before moving onto the steps after the <code>barrier()</code> (i.e. ordering).</p> </li> <li> <p>When "stalling" the traversal may lead to a "bulking optimization" in traversals that repeatedly touch many of the same elements (i.e. optimizing).</p> </li> </ul> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-41" type="radio" name="radio-set-1729796988-41" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-41" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-42" type="radio" name="radio-set-1729796988-41" class="tab-selector-2" /> <label for="tab-1729796988-42" 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> g.V().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">first: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">second: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.iterate() <span class="key">first</span>: v[<span class="integer">1</span>] <span class="key">second</span>: v[<span class="integer">1</span>] <span class="key">first</span>: v[<span class="integer">2</span>] <span class="key">second</span>: v[<span class="integer">2</span>] <span class="key">first</span>: v[<span class="integer">3</span>] <span class="key">second</span>: v[<span class="integer">3</span>] <span class="key">first</span>: v[<span class="integer">4</span>] <span class="key">second</span>: v[<span class="integer">4</span>] <span class="key">first</span>: v[<span class="integer">5</span>] <span class="key">second</span>: v[<span class="integer">5</span>] <span class="key">first</span>: v[<span class="integer">6</span>] <span class="key">second</span>: v[<span class="integer">6</span>] gremlin> g.V().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">first: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.barrier().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">second: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.iterate() <span class="key">first</span>: v[<span class="integer">1</span>] <span class="key">first</span>: v[<span class="integer">2</span>] <span class="key">first</span>: v[<span class="integer">3</span>] <span class="key">first</span>: v[<span class="integer">4</span>] <span class="key">first</span>: v[<span class="integer">5</span>] <span class="key">first</span>: v[<span class="integer">6</span>] <span class="key">second</span>: v[<span class="integer">1</span>] <span class="key">second</span>: v[<span class="integer">2</span>] <span class="key">second</span>: v[<span class="integer">3</span>] <span class="key">second</span>: v[<span class="integer">4</span>] <span class="key">second</span>: v[<span class="integer">5</span>] <span class="key">second</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().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">first: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">second: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.iterate() g.V().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">first: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.barrier().sideEffect{println <span class="string"><span class="delimiter">"</span><span class="content">second: </span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span><span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span>}.iterate()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The theory behind a "bulking optimization" is simple. If there are one million traversers at vertex 1, then there is no need to calculate one million <code>both()</code>-computations. Instead, represent those one million traversers as a single traverser with a <code>Traverser.bulk()</code> equal to one million and execute <code>both()</code> once. A bulking optimization example is made more salient on a larger graph. Therefore, the example below leverages the <a href="#grateful-dead">Grateful Dead graph</a>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-43" type="radio" name="radio-set-1729796988-43" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-43" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-44" type="radio" name="radio-set-1729796988-43" class="tab-selector-2" /> <label for="tab-1729796988-44" 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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> g = traversal().withEmbedded(graph).withoutStrategies(LazyBarrierStrategy) <span class="comment">//</span>// <b class="conum">(1)</b> ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">808</span> <span class="key">edges</span>:<span class="integer">8049</span>], standard] gremlin> clockWithResult(<span class="integer">1</span>){g.V().both().both().both().count().next()} <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="float">6997.450284</span> ==><span class="integer">126653966</span> gremlin> clockWithResult(<span class="integer">1</span>){g.V().repeat(both()).times(<span class="integer">3</span>).count().next()} <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="float">7050.683031</span> ==><span class="integer">126653966</span> gremlin> clockWithResult(<span class="integer">1</span>){g.V().both().barrier().both().barrier().both().barrier().count().next()} <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="float">8.607391999999999</span> ==><span class="integer">126653966</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) 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 = traversal().withEmbedded(graph).withoutStrategies(LazyBarrierStrategy) <span class="comment">//</span>// <b class="conum">(1)</b> clockWithResult(<span class="integer">1</span>){g.V().both().both().both().count().next()} <span class="comment">//</span>// <b class="conum">(2)</b> clockWithResult(<span class="integer">1</span>){g.V().repeat(both()).times(<span class="integer">3</span>).count().next()} <span class="comment">//</span>// <b class="conum">(3)</b> clockWithResult(<span class="integer">1</span>){g.V().both().barrier().both().barrier().both().barrier().count().next()} <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Explicitly remove <code>LazyBarrierStrategy</code> which yields a bulking optimization.</p> </li> <li> <p>A non-bulking traversal where each traverser is processed.</p> </li> <li> <p>Each traverser entering <code>repeat()</code> has its recursion bulked.</p> </li> <li> <p>A bulking traversal where implicit traversers are not processed.</p> </li> </ol> </div> <div class="paragraph"> <p>If <code>barrier()</code> is provided an integer argument, then the barrier will only hold <code>n</code>-number of unique traversers in its barrier before draining the aggregated traversers to the next step. This is useful in the aforementioned bulking optimization scenario with the added benefit of reducing the risk of an out-of-memory exception.</p> </div> <div class="paragraph"> <p><code>LazyBarrierStrategy</code> inserts <code>barrier()</code>-steps into a traversal where appropriate in order to gain the "bulking optimization."</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-45" type="radio" name="radio-set-1729796988-45" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-45" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-46" type="radio" name="radio-set-1729796988-45" class="tab-selector-2" /> <label for="tab-1729796988-46" 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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(1)</b> ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> clockWithResult(<span class="integer">1</span>){g.V().both().both().both().count().next()} ==><span class="float">5.572671</span> ==><span class="integer">126653966</span> gremlin> g.V().both().both().both().count().iterate().toString() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[TinkerGraphStep(vertex,<span class="type">[]</span>), VertexStep(BOTH,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(BOTH,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(BOTH,edge), CountGlobalStep, NoneStep]</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(1)</b> g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/grateful-dead.xml</span><span class="delimiter">'</span></span>).read().iterate() clockWithResult(<span class="integer">1</span>){g.V().both().both().both().count().next()} g.V().both().both().both().count().iterate().toString() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>LazyBarrierStrategy</code> is a default strategy and thus, does not need to be explicitly activated.</p> </li> <li> <p>With <code>LazyBarrierStrategy</code> activated, <code>barrier()</code>-steps are automatically inserted where appropriate.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#barrier()"><code>barrier()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#barrier(java.util.function.Consumer)"><code>barrier(Consumer)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#barrier(int)"><code>barrier(int)</code></a></p> </div> </div> <div class="sect2"> <h3 id="branch-step">Branch Step</h3> <div class="paragraph"> <p>The <code>branch()</code> step splits the traverser to all the child traversals provided to it. Please see the <a href="#general-steps">General Steps</a> section for more information, but also consider that <code>branch()</code> is the basis for more robust steps like <a href="#choose-step">choose()</a> and <a href="#union-step">union()</a>.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#branch(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>map(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="by-step">By Step</h3> <div class="paragraph"> <p>The <code>by()</code>-step is not an actual step, but instead is a "step-modulator" similar to <a href="#as-step"><code>as()</code></a> and <a href="#option-step"><code>option()</code></a>. If a step is able to accept traversals, functions, comparators, etc. then <code>by()</code> is the means by which they are added. The general pattern is <code>step().by()…​by()</code>. Some steps can only accept one <code>by()</code> while others can take an arbitrary amount.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-47" type="radio" name="radio-set-1729796988-47" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-47" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-48" type="radio" name="radio-set-1729796988-47" class="tab-selector-2" /> <label for="tab-1729796988-48" 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> g.V().group().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">1</span>:[v[<span class="integer">2</span>],v[<span class="integer">5</span>],v[<span class="integer">6</span>]],<span class="integer">3</span>:[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>]]] gremlin> g.V().group().by(bothE().count()).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="integer">1</span>:[vadas,ripple,peter],<span class="integer">3</span>:[marko,lop,josh]] gremlin> g.V().group().by(bothE().count()).by(count()) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="integer">1</span>:<span class="integer">3</span>,<span class="integer">3</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">g.V().group().by(bothE().count()) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().group().by(bothE().count()).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().group().by(bothE().count()).by(count()) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>by(outE().count())</code> will group the elements by their edge count (<strong>traversal</strong>).</p> </li> <li> <p><code>by('name')</code> will process the grouped elements by their name (<strong>element property projection</strong>).</p> </li> <li> <p><code>by(count())</code> will count the number of elements in each group (<strong>traversal</strong>).</p> </li> </ol> </div> <div class="paragraph"> <p>When a <code>by()</code> modulator does not produce a result, it is deemed "unproductive". An "unproductive" modulator will lead to the filtering of the traverser it is currently working with. The filtering will manifest in various ways depending on the step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-49" type="radio" name="radio-set-1729796988-49" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-49" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-50" type="radio" name="radio-set-1729796988-49" class="tab-selector-2" /> <label for="tab-1729796988-50" 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> g.V().sample(<span class="integer">1</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">(1)</b> ==>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().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property key is not present for all vertices, therefore <code>sample()</code> will ignore (i.e. filter) such vertices for consideration in the sampling.</p> </li> </ol> </div> <div class="paragraph"> <p>The following steps all support <code>by()</code>-modulation. Note that the semantics of such modulation should be understood on a step-by-step level and thus, as discussed in their respective section of the documentation.</p> </div> <div class="ulist"> <ul> <li> <p><a href="#aggregate-step"><code>aggregate()</code></a>: aggregate all objects into a set but only store their <code>by()</code>-modulated values.</p> </li> <li> <p><a href="#cyclicpath-step"><code>cyclicPath()</code></a>: filter if the traverser’s path is cyclic given <code>by()</code>-modulation.</p> </li> <li> <p><a href="#dedup-step"><code>dedup()</code></a>: dedup on the results of a <code>by()</code>-modulation.</p> </li> <li> <p><a href="#group-step"><code>group()</code></a>: create group keys and values according to <code>by()</code>-modulation.</p> </li> <li> <p><a href="#groupcount-step"><code>groupCount()</code></a>: count those groups where the group keys are the result of <code>by()</code>-modulation.</p> </li> <li> <p><a href="#math-step"><code>math()</code></a>: transform a traverser provided to the step by way of the <code>by()</code> modulator before it processed by it.</p> </li> <li> <p><a href="#order-step"><code>order()</code></a>: order the objects by the results of a <code>by()</code>-modulation.</p> </li> <li> <p><a href="#path-step"><code>path()</code></a>: get the path of the traverser where each path element is <code>by()</code>-modulated.</p> </li> <li> <p><a href="#project-step"><code>project()</code></a>: project a map of results given various <code>by()</code>-modulations off the current object.</p> </li> <li> <p><a href="#propertymap-step"><code>propertyMap()</code></a>: transform the result of the values in the resulting <code>Map</code> using the <code>by()</code> modulator.</p> </li> <li> <p><a href="#sack-step"><code>sack()</code></a>: provides the transformation for a traverser to a value to be stored in the sack.</p> </li> <li> <p><a href="#sample-step"><code>sample()</code></a>: sample using the value returned by <code>by()</code>-modulation.</p> </li> <li> <p><a href="#select-step"><code>select()</code></a>: select path elements and transform them via <code>by()</code>-modulation.</p> </li> <li> <p><a href="#simplepath-step"><code>simplePath()</code></a>: filter if the traverser’s path is simple given <code>by()</code>-modulation.</p> </li> <li> <p><a href="#tree-step"><code>tree()</code></a>: get a tree of traversers objects where the objects have been <code>by()</code>-modulated.</p> </li> <li> <p><a href="#valuemap-step"><code>valueMap()</code></a>: transform the result of the values in the resulting <code>Map</code> using the <code>by()</code> modulator.</p> </li> <li> <p><a href="#where-step"><code>where()</code></a>: determine the predicate given the testing of the results of <code>by()</code>-modulation.</p> </li> </ul> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by()"><code>by()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(java.util.Comparator)"><code>by(Comparator)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(java.util.function.Function,java.util.Comparator)"><code>by(Function,Comparator)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(java.util.function.Function)"><code>by(Function)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(org.apache.tinkerpop.gremlin.process.traversal.Order)"><code>by(Order)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(java.lang.String)"><code>by(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(java.lang.String,java.util.Comparator)"><code>by(String,Comparator)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(org.apache.tinkerpop.gremlin.structure.T)"><code>by(T)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>by(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#by(org.apache.tinkerpop.gremlin.process.traversal.Traversal,java.util.Comparator)"><code>by(Traversal,Comparator)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/structure/T.html"><code>T</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Order.html"><code>Order</code></a></p> </div> </div> <div class="sect2"> <h3 id="call-step">Call Step</h3> <div class="paragraph"> <p>The <code>call()</code> step allows for custom, provider-specific service calls either at the start of a traversal or mid-traversal. This allows Graph providers to expose operations not natively built into the Gremlin language, such as full text search, custom analytics, notification triggers, etc.</p> </div> <div class="paragraph"> <p>When called with no arguments, <code>call()</code> will produce a list of callable services available for the graph in use. This no-argument version is equivalent to <code>call('--list')</code>. This "directory service" is also capable of producing more verbose output describing all the services or an individual service:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-51" type="radio" name="radio-set-1729796988-51" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-51" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-52" type="radio" name="radio-set-1729796988-51" class="tab-selector-2" /> <label for="tab-1729796988-52" 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> g.call() <span class="comment">//</span>// <b class="conum">(1)</b> gremlin> g.call(<span class="string"><span class="delimiter">'</span><span class="content">--list</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> gremlin> g.call().with(<span class="string"><span class="delimiter">'</span><span class="content">verbose</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> gremlin> g.call().with(<span class="string"><span class="delimiter">'</span><span class="content">verbose</span><span class="delimiter">'</span></span>).with(<span class="string"><span class="delimiter">'</span><span class="content">service</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</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.call() <span class="comment">//</span>// <b class="conum">(1)</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">--list</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.call().with(<span class="string"><span class="delimiter">'</span><span class="content">verbose</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.call().with(<span class="string"><span class="delimiter">'</span><span class="content">verbose</span><span class="delimiter">'</span></span>).with(<span class="string"><span class="delimiter">'</span><span class="content">service</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>List available services by name</p> </li> <li> <p>Produce a Map of detailed service information by name</p> </li> <li> <p>Produce the detailed service information for the 'xyz-service'</p> </li> </ol> </div> <div class="paragraph"> <p>The first argument to <code>call()</code> is always the name of the service call. Additionally, service calls can accept both static and dynamically produced parameters. Static parameters can be passed as a <code>Map</code> to the <code>call()</code> as the second argument. Individual static parameters can also be added using the <code>.with()</code> modulator. Dynamic parameters can be passed as a <code>Map</code>-producing <code>Traversal</code> as the second argument (no static parameters) or third argument (static + dynamic parameters). Additional individual dynamic parameters can be added using the <code>.with()</code> modulator.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>, [<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="invisible">//</span><b class="conum">2</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>).with(<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="invisible">//</span><b class="conum">2</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>, __.inject([<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="invisible">//</span><b class="conum">3</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>).with(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>, __.inject(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)) <span class="invisible">//</span><b class="conum">3</b> g.call(<span class="string"><span class="delimiter">'</span><span class="content">xyz-service</span><span class="delimiter">'</span></span>, [<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>], __.inject([<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>])) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Call the 'xyz-service' with no parameters</p> </li> <li> <p>Examples of static parameters (constants known before execution)</p> </li> <li> <p>Examples of dynamic parameters (these will be computed at execution time)</p> </li> <li> <p>Example of static + dynamic parameters (these will be computed and merged into one set of parameters at execution time)</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p>GraphTraversalSource:</p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#call()"><code>call()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#call(java.lang.String)"><code>call(String)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#call(java.lang.String,java.util.Map)"><code>call(String, Map)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#call(java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>call(String, Traversal)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#call(java.lang.String,java.util.Map,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>call(String, Map, Traversal)</code></a></p> </div> <div class="paragraph"> <p>GraphTraversal:</p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#call(java.lang.String)"><code>call(String)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#call(java.lang.String,java.util.Map)"><code>call(String, Map)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#call(java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>call(String, Traversal)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#call(java.lang.String,java.util.Map,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>call(String, Map, Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="cap-step">Cap Step</h3> <div class="paragraph"> <p>The <code>cap()</code>-step (<strong>barrier</strong>) iterates the traversal up to itself and emits the sideEffect referenced by the provided key. If multiple keys are provided, then a <code>Map<String,Object></code> of sideEffects is emitted.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-53" type="radio" name="radio-set-1729796988-53" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-53" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-54" type="radio" name="radio-set-1729796988-53" class="tab-selector-2" /> <label for="tab-1729796988-54" 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> g.V().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).by(label).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">software</span>:<span class="integer">2</span>,<span class="key">person</span>:<span class="integer">4</span>] gremlin> g.V().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).by(label).groupCount(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).by(outE().count()).cap(<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="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">a</span>:[<span class="key">software</span>:<span class="integer">2</span>,<span class="key">person</span>:<span class="integer">4</span>],<span class="key">b</span>:[<span class="integer">0</span>:<span class="integer">3</span>,<span class="integer">1</span>:<span class="integer">1</span>,<span class="integer">2</span>:<span class="integer">1</span>,<span class="integer">3</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().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).by(label).cap(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).by(label).groupCount(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).by(outE().count()).cap(<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="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Group and count vertices by their label. Emit the side effect labeled 'a', which is the group count by label.</p> </li> <li> <p>Same as statement 1, but also emit the side effect labeled 'b' which groups vertices by the number of out edges.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#cap(java.lang.String,java.lang.String...)"><code>cap(String,String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="choose-step">Choose Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/choose-step.png" alt="choose step" width="700"> </div> </div> <div class="paragraph"> <p>The <code>choose()</code>-step (<strong>branch</strong>) routes the current traverser to a particular traversal branch option. With <code>choose()</code>, it is possible to implement if/then/else-semantics as well as more complicated selections.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-55" type="radio" name="radio-set-1729796988-55" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-55" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-56" type="radio" name="radio-set-1729796988-55" class="tab-selector-2" /> <label for="tab-1729796988-56" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lte(<span class="integer">30</span>)), __.in(), __.out()).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">(1)</b> ==>marko ==>ripple ==>lop ==>lop gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>)). option(<span class="integer">27</span>, __.in()). option(<span class="integer">32</span>, __.out()).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">(2)</b> ==>marko ==>ripple ==>lop</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>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lte(<span class="integer">30</span>)), __.in(), __.out()).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">(1)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>)). option(<span class="integer">27</span>, __.in()). option(<span class="integer">32</span>, __.out()).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">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>If the traversal yields an element, then do <code>in</code>, else do <code>out</code> (i.e. true/false-based option selection).</p> </li> <li> <p>Use the result of the traversal as a key to the map of traversal options (i.e. value-based option selection).</p> </li> </ol> </div> <div class="paragraph"> <p>If the "false"-branch is not provided, then if/then-semantics are implemented.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-57" type="radio" name="radio-set-1729796988-57" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-57" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-58" type="radio" name="radio-set-1729796988-57" class="tab-selector-2" /> <label for="tab-1729796988-58" 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> g.V().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(1)</b> ==>lop ==>lop ==>ripple ==>lop ==>ripple ==>lop gremlin> g.V().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), identity()).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">(2)</b> ==>lop ==>lop ==>ripple ==>lop ==>ripple ==>lop</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().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(1)</b> g.V().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), identity()).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">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>If the vertex is a person, emit the vertices they created, else emit the vertex.</p> </li> <li> <p>If/then/else with an <code>identity()</code> on the false-branch is equivalent to if/then with no false-branch.</p> </li> </ol> </div> <div class="paragraph"> <p>Note that <code>choose()</code> can have an arbitrary number of options and moreover, can take an anonymous traversal as its choice function.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-59" type="radio" name="radio-set-1729796988-59" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-59" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-60" type="radio" name="radio-set-1729796988-59" class="tab-selector-2" /> <label for="tab-1729796988-60" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>, elementMap()). option(<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>, label()) ==><span class="integer">29</span> ==>[<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">27</span>] ==>josh ==>person</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>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>, elementMap()). option(<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>, label())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>choose()</code>-step can leverage the <code>Pick.none</code> option match. For anything that does not match a specified option, the <code>none</code>-option is taken.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-61" type="radio" name="radio-set-1729796988-61" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-61" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-62" type="radio" name="radio-set-1729796988-61" class="tab-selector-2" /> <label for="tab-1729796988-62" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(none, values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)) ==><span class="integer">29</span> ==>vadas ==>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">'</span><span class="content">person</span><span class="delimiter">'</span></span>). choose(values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)). option(<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>)). option(none, 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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(java.util.function.Function)"><code>choose(Function)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(java.util.function.Predicate,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>choose(Predicate,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(java.util.function.Predicate,org.apache.tinkerpop.gremlin.process.traversal.Traversal,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>choose(Predicate,Traversal,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(org.apache.tinkerpop.gremlin.process.traversal.Traversal,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>choose(Traversal,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(org.apache.tinkerpop.gremlin.process.traversal.Traversal,org.apache.tinkerpop.gremlin.process.traversal.Traversal,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>choose(Traversal,Traversal,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#choose(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>choose(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="coalesce-step">Coalesce Step</h3> <div class="paragraph"> <p>The <code>coalesce()</code>-step evaluates the provided traversals in order and returns the first traversal that emits at least one element.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-63" type="radio" name="radio-set-1729796988-63" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-63" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-64" type="radio" name="radio-set-1729796988-63" class="tab-selector-2" /> <label for="tab-1729796988-64" 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> g.V(<span class="integer">1</span>).coalesce(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>), outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).inV().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label) ==>[marko,knows,vadas] ==>[marko,knows,josh] gremlin> g.V(<span class="integer">1</span>).coalesce(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)).inV().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label) ==>[marko,created,lop] gremlin> g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">nickname</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">okram</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">1</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">nickname</span><span class="delimiter">'</span></span>), values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)) ==>okram ==>vadas ==>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(<span class="integer">1</span>).coalesce(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>), outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).inV().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label) g.V(<span class="integer">1</span>).coalesce(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)).inV().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label) g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">nickname</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">okram</span><span class="delimiter">'</span></span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).coalesce(values(<span class="string"><span class="delimiter">'</span><span class="content">nickname</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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#coalesce(org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>coalesce(Traversal…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="coin-step">Coin Step</h3> <div class="paragraph"> <p>To randomly filter out a traverser, use the <code>coin()</code>-step (<strong>filter</strong>). The provided double argument biases the "coin toss."</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-65" type="radio" name="radio-set-1729796988-65" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-65" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-66" type="radio" name="radio-set-1729796988-65" class="tab-selector-2" /> <label for="tab-1729796988-66" 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> g.V().coin(<span class="float">0.5</span>) ==>v[<span class="integer">2</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">6</span>] gremlin> g.V().coin(<span class="float">0.0</span>) gremlin> g.V().coin(<span class="float">1.0</span>) ==>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().coin(<span class="float">0.5</span>) g.V().coin(<span class="float">0.0</span>) g.V().coin(<span class="float">1.0</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#coin(double)"><code>coin(double)</code></a></p> </div> </div> <div class="sect2"> <h3 id="combine-step">Combine Step</h3> <div class="paragraph"> <p>The <code>combine()</code>-step (<strong>map</strong>) combines the elements of the incoming list traverser and the provided list argument into one list. This is also known as appending or concatenating. This step only expects list data (array or Iterable) and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>). This differs from the <code>merge()</code>-step in that it allows duplicates to exist.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-67" type="radio" name="radio-set-1729796988-67" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-67" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-68" type="radio" name="radio-set-1729796988-67" class="tab-selector-2" /> <label for="tab-1729796988-68" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().combine([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</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="string"><span class="delimiter">"</span><span class="content">vadas</span><span class="delimiter">"</span></span>]) ==>[marko,vadas,lop,josh,ripple,peter,james,jen,marko,vadas] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().combine(__.constant(<span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>).fold()) ==>[marko,vadas,lop,josh,ripple,peter,stephen]</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">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().combine([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</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="string"><span class="delimiter">"</span><span class="content">vadas</span><span class="delimiter">"</span></span>]) g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().combine(__.constant(<span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>).fold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#combine(java.lang.Object)"><code>combine(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#combine-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="concat-step">Concat Step</h3> <div class="paragraph"> <p>The <code>concat()</code>-step (<strong>map</strong>) concatenates one or more String values together to the incoming String traverser. This step can take either String varargs or Traversal varargs. Any <code>null</code> String values will be skipped when concatenated with non-<code>null</code> String values. If two <code>null</code> value are concatenated, the <code>null</code> value will be propagated and returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-69" type="radio" name="radio-set-1729796988-69" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-69" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-70" type="radio" name="radio-set-1729796988-69" class="tab-selector-2" /> <label for="tab-1729796988-70" 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> g.addV(constant(<span class="string"><span class="delimiter">'</span><span class="content">prefix_</span><span class="delimiter">'</span></span>).concat(__.V(<span class="integer">1</span>).label())).property(id, <span class="integer">10</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">10</span>] gremlin> g.V(<span class="integer">10</span>).label() ==>prefix_person gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). constant(<span class="string"><span class="delimiter">'</span><span class="content">Mr.</span><span class="delimiter">'</span></span>).concat(__.select(<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> ==>Mr.marko ==>Mr.vadas ==>Mr.josh ==>Mr.peter gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(<span class="string"><span class="delimiter">'</span><span class="content"> uses </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>lop uses java ==>ripple uses java gremlin> g.V(<span class="integer">1</span>).outE().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).label()). concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).inV().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> ==>marko created lop ==>marko knows vadas ==>marko knows josh gremlin> g.V(<span class="integer">1</span>).outE().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(constant(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).label(), constant(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).inV().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">(5)</b> ==>marko created lop ==>marko knows vadas ==>marko knows josh gremlin> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">hello</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">hi</span><span class="delimiter">'</span></span>).concat(__.V().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">(6)</b> ==>hellomarko ==>himarko gremlin> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">This</span><span class="delimiter">'</span></span>).concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>).concat(<span class="string"><span class="delimiter">'</span><span class="content">is a </span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">gremlin.</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==>This is a gremlin.</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(constant(<span class="string"><span class="delimiter">'</span><span class="content">prefix_</span><span class="delimiter">'</span></span>).concat(__.V(<span class="integer">1</span>).label())).property(id, <span class="integer">10</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">10</span>).label() g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>). constant(<span class="string"><span class="delimiter">'</span><span class="content">Mr.</span><span class="delimiter">'</span></span>).concat(__.select(<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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(<span class="string"><span class="delimiter">'</span><span class="content"> uses </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">1</span>).outE().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).label()). concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>). concat(select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).inV().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> g.V(<span class="integer">1</span>).outE().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). concat(constant(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).label(), constant(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>), select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).inV().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">(5)</b> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">hello</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">hi</span><span class="delimiter">'</span></span>).concat(__.V().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">(6)</b> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">This</span><span class="delimiter">'</span></span>).concat(<span class="string"><span class="delimiter">'</span><span class="content"> </span><span class="delimiter">'</span></span>).concat(<span class="string"><span class="delimiter">'</span><span class="content">is a </span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">gremlin.</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Add a new vertex with id 10 which should be labeled like an existing vertex but with some prefix attached</p> </li> <li> <p>Attach the prefix "Mr." to all the names using the <code>constant()</code>-step</p> </li> <li> <p>Generate a string of software names and the language they use</p> </li> <li> <p>Generate a string description for each of marko’s outgoing edges</p> </li> <li> <p>Alternative way to generate the string description by using traversal varargs. Use the <code>constant()</code> step to add desired strings between arguments.</p> </li> <li> <p>The <code>concat()</code> step will append the first result from the child traversal to the incoming traverser</p> </li> <li> <p>A generic use of <code>concat()</code> to join strings together</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat(java.lang.String)"><code>concat(String…​)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#concat(org.apache.tinkerpop.gremlin.process.traversal.Traversal,org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>concat(Taversal, Traversal…​)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#concat-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="conjoin-step">Conjoin Step</h3> <div class="paragraph"> <p>The <code>conjoin()</code>-step (<strong>map</strong>) joins together the elements in the incoming list traverser together with the provided argument as a delimiter. The resulting <code>String</code> is added to the Traversal Stream. This step only expects list data (array or Iterable) in the incoming traverser and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>). Null values are skipped and not included in the result.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-71" type="radio" name="radio-set-1729796988-71" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-71" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-72" type="radio" name="radio-set-1729796988-71" class="tab-selector-2" /> <label for="tab-1729796988-72" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().conjoin(<span class="string"><span class="delimiter">"</span><span class="content">+</span><span class="delimiter">"</span></span>) ==>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().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().conjoin(<span class="string"><span class="delimiter">"</span><span class="content">+</span><span class="delimiter">"</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#conjoin(java.lang.String)"><code>conjoin(String)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#conjoin-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="connectedcomponent-step">ConnectedComponent Step</h3> <div class="paragraph"> <p>The <code>connectedComponent()</code> step performs a computation to identify <a href="https://en.wikipedia.org/wiki/Connected_component_(graph_theory)">Connected Component</a> instances in a graph. When this step completes, the vertices will be labelled with a component identifier to denote the component to which they are associated.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>connectedComponent()</code>-step is a <code>VertexComputing</code>-step and as such, can only be used against a graph that supports <code>GraphComputer</code> (OLAP). </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-73" type="radio" name="radio-set-1729796988-73" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-73" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-74" type="radio" name="radio-set-1729796988-73" class="tab-selector-2" /> <label for="tab-1729796988-74" 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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V(). connectedComponent(). with(ConnectedComponent.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>). 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">component</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(<span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>) ==>[<span class="key">name</span>:ripple,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:vadas,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:peter,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:lop,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:marko,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:josh,<span class="key">component</span>:<span class="integer">1</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). connectedComponent(). with(ConnectedComponent.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>). with(ConnectedComponent.edges, outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). 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">component</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(<span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>) ==>[<span class="key">name</span>:peter,<span class="key">component</span>:<span class="integer">6</span>] ==>[<span class="key">name</span>:josh,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:vadas,<span class="key">component</span>:<span class="integer">1</span>] ==>[<span class="key">name</span>:marko,<span class="key">component</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 = traversal().withEmbedded(graph).withComputer() g.V(). connectedComponent(). with(ConnectedComponent.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>). 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">component</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(<span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). connectedComponent(). with(ConnectedComponent.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>). with(ConnectedComponent.edges, outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). 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">component</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(<span class="string"><span class="delimiter">'</span><span class="content">component</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note the use of the <code>with()</code> modulating step which provides configuration options to the algorithm. It takes configuration keys from the <code>ConnectedComponent</code> class and is automatically imported to the Gremlin Console.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#connectedComponent()"><code>connectedComponent()</code></a></p> </div> </div> <div class="sect2"> <h3 id="constant-step">Constant Step</h3> <div class="paragraph"> <p>To specify a constant value for a traverser, use the <code>constant()</code>-step (<strong>map</strong>). This is often useful with conditional steps like <a href="#choose-step"><code>choose()</code>-step</a> or <a href="#coalesce-step"><code>coalesce()</code>-step</a>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-75" type="radio" name="radio-set-1729796988-75" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-75" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-76" type="radio" name="radio-set-1729796988-75" class="tab-selector-2" /> <label for="tab-1729796988-76" 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> g.V().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), values(<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">inhuman</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>marko ==>vadas ==>inhuman ==>josh ==>inhuman ==>peter gremlin> g.V().coalesce( hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<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">inhuman</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==>marko ==>vadas ==>inhuman ==>josh ==>inhuman ==>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().choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), values(<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">inhuman</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().coalesce( hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<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">inhuman</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>Show the names of people, but show "inhuman" for other vertices.</p> </li> <li> <p>Same as statement 1 (unless there is a person vertex with no name).</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#constant(E2)"><code>constant(Object)</code></a></p> </div> </div> <div class="sect2"> <h3 id="count-step">Count Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/count-step.png" alt="count step" width="195"> </div> </div> <div class="paragraph"> <p>The <code>count()</code>-step (<strong>map</strong>) counts the total number of represented traversers in the streams (i.e. the bulk count).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-77" type="radio" name="radio-set-1729796988-77" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-77" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-78" type="radio" name="radio-set-1729796988-77" class="tab-selector-2" /> <label for="tab-1729796988-78" 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> g.V().count() ==><span class="integer">6</span> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).count() ==><span class="integer">4</span> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().path() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">4</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().map {<span class="local-variable">it</span>.get() * <span class="integer">10</span>}.path() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="integer">4</span>,<span class="integer">40</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().count() g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).count() g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().path() <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>).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().map {<span class="local-variable">it</span>.get() * <span class="integer">10</span>}.path() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>count()</code>-step is a <a href="#a-note-on-barrier-steps">reducing barrier step</a> meaning that all of the previous traversers are folded into a new traverser.</p> </li> <li> <p>The path of the traverser emanating from <code>count()</code> starts at <code>count()</code>.</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> <code>count(local)</code> counts the current, local object (not the objects in the traversal stream). This works for <code>Collection</code>- and <code>Map</code>-type objects. For any other object, a count of 1 is returned. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#count()"><code>count()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#count(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>count(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="cyclicpath-step">CyclicPath Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/cyclicpath-step.png" alt="cyclicpath step" width="400"> </div> </div> <div class="paragraph"> <p>Each traverser maintains its history through the traversal over the graph — i.e. its <a href="#path-data-structure">path</a>. If it is important that the traverser repeat its course, then <code>cyclic()</code>-path should be used (<strong>filter</strong>). The step analyzes the path of the traverser thus far and if there are any repeats, the traverser is filtered out over the traversal computation. If non-cyclic behavior is desired, see <a href="#simplepath-step"><code>simplePath()</code></a>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-79" type="radio" name="radio-set-1729796988-79" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-79" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-80" type="radio" name="radio-set-1729796988-79" class="tab-selector-2" /> <label for="tab-1729796988-80" 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> g.V(<span class="integer">1</span>).both().both() ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).both().both().cyclicPath() ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).both().both().cyclicPath().path() ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>]] gremlin> g.V(<span class="integer">1</span>).both().both().cyclicPath().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).path() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>]] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). in(<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">c</span><span class="delimiter">'</span></span>). cyclicPath(). path() ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>]] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). in(<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">c</span><span class="delimiter">'</span></span>). cyclicPath().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>). path()</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>).both().both() g.V(<span class="integer">1</span>).both().both().cyclicPath() g.V(<span class="integer">1</span>).both().both().cyclicPath().path() g.V(<span class="integer">1</span>).both().both().cyclicPath().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).path() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). in(<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">c</span><span class="delimiter">'</span></span>). cyclicPath(). path() g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). in(<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">c</span><span class="delimiter">'</span></span>). cyclicPath().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>). path()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those traversers are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#cyclicPath()"><code>cyclicPath()</code></a></p> </div> </div> <div class="sect2"> <h3 id="dateAdd-step">DateAdd Step</h3> <div class="paragraph"> <p>The <code>dateAdd()</code>-step (<strong>map</strong>) returns the value with the addition of the value number of units as specified by the DateToken. If the incoming traverser is not a Date, then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-81" type="radio" name="radio-set-1729796988-81" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-81" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-82" type="radio" name="radio-set-1729796988-81" class="tab-selector-2" /> <label for="tab-1729796988-82" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate().dateAdd(DT.day, <span class="integer">7</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>Wed Aug <span class="integer">09</span> <span class="octal">00</span>:<span class="octal">00</span>:<span class="octal">00</span> UTC <span class="integer">2023</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2023-08-03T00:00:00Z</span><span class="delimiter">"</span></span>]).unfold().asDate().dateAdd(DT.minute, <span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>Wed Aug <span class="octal">02</span> <span class="octal">00</span>:<span class="octal">01</span>:<span class="octal">00</span> UTC <span class="integer">2023</span> ==>Thu Aug <span class="octal">03</span> <span class="octal">00</span>:<span class="octal">01</span>:<span class="octal">00</span> UTC <span class="integer">2023</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.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate().dateAdd(DT.day, <span class="integer">7</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.inject([<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">2023-08-03T00:00:00Z</span><span class="delimiter">"</span></span>]).unfold().asDate().dateAdd(DT.minute, <span class="integer">1</span>) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Add 7 days to Date</p> </li> <li> <p>Add 1 minute to incoming dates</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dateAdd(org.apache.tinkerpop.gremlin.process.traversal.DT,int)"><code>dateAdd(DT,int)</code></a></p> </div> </div> <div class="sect2"> <h3 id="dateDiff-step">DateDiff Step</h3> <div class="paragraph"> <p>The <code>dateDiff()</code>-step (<strong>map</strong>) returns the difference between two Dates in epoch time. If the incoming traverser is not a Date, then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-83" type="radio" name="radio-set-1729796988-83" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-83" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-84" type="radio" name="radio-set-1729796988-83" class="tab-selector-2" /> <label for="tab-1729796988-84" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate().dateDiff(constant(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-03T00:00:00Z</span><span class="delimiter">"</span></span>).asDate()) <span class="comment">//</span>// <b class="conum">(1)</b> ==>-<span class="integer">86400</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.inject(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-02T00:00:00Z</span><span class="delimiter">"</span></span>).asDate().dateDiff(constant(<span class="string"><span class="delimiter">"</span><span class="content">2023-08-03T00:00:00Z</span><span class="delimiter">"</span></span>).asDate()) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find difference between two dates</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dateDiff(java.util.Date)"><code>dateDiff(Date)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dateDiff(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>dateDiff(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="dedup-step">Dedup Step</h3> <div class="paragraph"> <p>With <code>dedup()</code>-step (<strong>filter</strong>), repeatedly seen objects are removed from the traversal stream. Note that if a traverser’s bulk is greater than 1, then it is set to 1 before being emitted.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-85" type="radio" name="radio-set-1729796988-85" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-85" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-86" type="radio" name="radio-set-1729796988-85" class="tab-selector-2" /> <label for="tab-1729796988-86" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>) ==>java ==>java gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>).dedup() ==>java gremlin> g.V(<span class="integer">1</span>).repeat(bothE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).dedup().otherV()).emit().path() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] gremlin> g.V().bothE().properties().dedup() <span class="comment">//</span>// <b class="conum">(2)</b> ==>p[weight-><span class="float">0.4</span>] ==>p[weight-><span class="float">0.5</span>] ==>p[weight-><span class="float">1.0</span>] ==>p[weight-><span class="float">0.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().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>).dedup() g.V(<span class="integer">1</span>).repeat(bothE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).dedup().otherV()).emit().path() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().bothE().properties().dedup() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Traverse all <code>created</code> edges, but don’t touch any edge twice.</p> </li> <li> <p>Note that <code>Property</code> instances will compare on key and value, whereas a <code>VertexProperty</code> will also include its element as it is a first-class citizen.</p> </li> </ol> </div> <div class="paragraph"> <p>If a by-step modulation is provided to <code>dedup()</code>, then the object is processed accordingly prior to determining if it has been seen or not.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-87" type="radio" name="radio-set-1729796988-87" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-87" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-88" type="radio" name="radio-set-1729796988-87" class="tab-selector-2" /> <label for="tab-1729796988-88" 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> g.V().elementMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[<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">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">name</span>:vadas] ==>[<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">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">name</span>:josh] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">name</span>:peter] gremlin> g.V().dedup().by(label).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>lop</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().elementMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().dedup().by(label).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>Finally, if <code>dedup()</code> is provided an array of strings, then it will ensure that the de-duplication is not with respect to the current traverser object, but to the path history of the traverser.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-89" type="radio" name="radio-set-1729796988-89" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-89" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-90" type="radio" name="radio-set-1729796988-89" class="tab-selector-2" /> <label for="tab-1729796988-90" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>).in(<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">c</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>) ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">6</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">5</span>],<span class="key">c</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">6</span>]] ==>[<span class="key">a</span>:v[<span class="integer">6</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] ==>[<span class="key">a</span>:v[<span class="integer">6</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">6</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">6</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>).in(<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">c</span><span class="delimiter">'</span></span>).dedup(<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>).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>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">5</span>],<span class="key">c</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">4</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] ==>[<span class="key">a</span>:v[<span class="integer">6</span>],<span class="key">b</span>:v[<span class="integer">3</span>],<span class="key">c</span>:v[<span class="integer">1</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). dedup(<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>).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">(2)</b> 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>) ==>[<span class="key">a</span>:marko,<span class="key">b</span>:vadas,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh,<span class="key">c</span>:ripple] ==>[<span class="key">a</span>:vadas,<span class="key">b</span>:marko,<span class="key">c</span>:lop] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:marko,<span class="key">c</span>:lop]</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>).out(<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">b</span><span class="delimiter">'</span></span>).in(<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">c</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>) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>).in(<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">c</span><span class="delimiter">'</span></span>).dedup(<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>).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>) <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>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). dedup(<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>).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">(2)</b> 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="colist arabic"> <ol> <li> <p>If the current <code>a</code> and <code>b</code> combination has been seen previously, then filter the traverser.</p> </li> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dedup(org.apache.tinkerpop.gremlin.process.traversal.Scope,java.lang.String...)"><code>dedup(Scope,String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dedup(java.lang.String...)"><code>dedup(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#dedup-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="difference-step">Difference Step</h3> <div class="paragraph"> <p>The <code>difference()</code>-step (<strong>map</strong>) calculates the difference between the incoming list traverser and the provided list argument. More specifically, this provides the set operation A-B where A is the traverser and B is the argument. This step only expects list data (array or Iterable) and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-91" type="radio" name="radio-set-1729796988-91" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-91" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-92" type="radio" name="radio-set-1729796988-91" class="tab-selector-2" /> <label for="tab-1729796988-92" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().difference([<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">ripple</span><span class="delimiter">"</span></span>]) ==>[peter,vadas,josh,marko] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().difference(__.V().limit(<span class="integer">2</span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold()) ==>[ripple,peter,josh,lop]</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">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().difference([<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">ripple</span><span class="delimiter">"</span></span>]) g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().difference(__.V().limit(<span class="integer">2</span>).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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#difference(java.lang.Object)"><code>difference(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#difference-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="disjunct-step">Disjunct Step</h3> <div class="paragraph"> <p>The <code>disjunct()</code>-step (<strong>map</strong>) calculates the disjunct set between the incoming list traverser and the provided list argument. This step only expects list data (array or Iterable) and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-93" type="radio" name="radio-set-1729796988-93" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-93" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-94" type="radio" name="radio-set-1729796988-93" class="tab-selector-2" /> <label for="tab-1729796988-94" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().disjunct([<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">peter</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">sam</span><span class="delimiter">"</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[ripple,vadas,josh,sam,marko] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().disjunct(__.V().limit(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold()) ==>[ripple,peter,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().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().disjunct([<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">peter</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">sam</span><span class="delimiter">"</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().disjunct(__.V().limit(<span class="integer">3</span>).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="colist arabic"> <ol> <li> <p>Find the unique names between two group of names</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#disjunct(java.lang.Object)"><code>disjunct(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#disjunct-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="drop-step">Drop Step</h3> <div class="paragraph"> <p>The <code>drop()</code>-step (<strong>filter</strong>/<strong>sideEffect</strong>) is used to remove element and properties from the graph (i.e. remove). It is a filter step because the traversal yields no outgoing objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-95" type="radio" name="radio-set-1729796988-95" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-95" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-96" type="radio" name="radio-set-1729796988-95" class="tab-selector-2" /> <label for="tab-1729796988-96" 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> g.V().outE().drop() gremlin> g.E() gremlin> g.V().properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).drop() gremlin> g.V().elementMap() ==>[<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="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">35</span>] gremlin> g.V().drop() gremlin> g.V()</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().drop() g.E() g.V().properties(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).drop() g.V().elementMap() g.V().drop() g.V()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="ulist"> <ul> <li> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#drop()"><code>drop()</code></a></p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="e-step">E Step</h3> <div class="paragraph"> <p>The <code>E()</code>-step is meant to read edges from the graph and is usually used to start a <code>GraphTraversal</code>, but can also be used mid-traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-97" type="radio" name="radio-set-1729796988-97" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-97" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-98" type="radio" name="radio-set-1729796988-97" class="tab-selector-2" /> <label for="tab-1729796988-98" 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> g.E(<span class="integer">11</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] gremlin> g.E().hasLabel(<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">weight</span><span class="delimiter">'</span></span>, gt(<span class="float">0.75</span>)) ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>] gremlin> g.inject(<span class="integer">1</span>).coalesce(E().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">knows</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(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">josh</span><span class="delimiter">"</span></span>)).to(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">vadas</span><span class="delimiter">"</span></span>))) <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>] ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><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.E(<span class="integer">11</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.E().hasLabel(<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">weight</span><span class="delimiter">'</span></span>, gt(<span class="float">0.75</span>)) g.inject(<span class="integer">1</span>).coalesce(E().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">knows</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(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">josh</span><span class="delimiter">"</span></span>)).to(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">vadas</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>Find the edge by its unique identifier (i.e. <code>T.id</code>) - not all graphs will use a numeric value for their identifier.</p> </li> <li> <p>Get edges with label <code>knows</code>, if there is none then add new one between <code>josh</code> and <code>vadas</code>.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#E(java.lang.Object...)"><code>E(Object…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="element-step">Element Step</h3> <div class="paragraph"> <p>The <code>element()</code> step is a no-argument step that traverses from a <code>Property</code> to the <code>Element</code> that owns it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-99" type="radio" name="radio-set-1729796988-99" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-99" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-100" type="radio" name="radio-set-1729796988-99" class="tab-selector-2" /> <label for="tab-1729796988-100" 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> g.V().properties().element() <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">7</span>] ==>v[<span class="integer">7</span>] ==>v[<span class="integer">7</span>] ==>v[<span class="integer">7</span>] ==>v[<span class="integer">8</span>] ==>v[<span class="integer">8</span>] ==>v[<span class="integer">8</span>] ==>v[<span class="integer">8</span>] ==>v[<span class="integer">8</span>] ==>v[<span class="integer">9</span>] ==>v[<span class="integer">9</span>] ==>v[<span class="integer">9</span>] ==>v[<span class="integer">9</span>] ==>v[<span class="integer">10</span>] ==>v[<span class="integer">11</span>] gremlin> g.E().properties().element() <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">13</span>][<span class="integer">1</span>-develops-><span class="integer">10</span>] ==>e[<span class="integer">14</span>][<span class="integer">1</span>-develops-><span class="integer">11</span>] ==>e[<span class="integer">15</span>][<span class="integer">1</span>-uses-><span class="integer">10</span>] ==>e[<span class="integer">16</span>][<span class="integer">1</span>-uses-><span class="integer">11</span>] ==>e[<span class="integer">17</span>][<span class="integer">7</span>-develops-><span class="integer">10</span>] ==>e[<span class="integer">18</span>][<span class="integer">7</span>-develops-><span class="integer">11</span>] ==>e[<span class="integer">19</span>][<span class="integer">7</span>-uses-><span class="integer">10</span>] ==>e[<span class="integer">20</span>][<span class="integer">7</span>-uses-><span class="integer">11</span>] ==>e[<span class="integer">21</span>][<span class="integer">8</span>-develops-><span class="integer">10</span>] ==>e[<span class="integer">22</span>][<span class="integer">8</span>-uses-><span class="integer">10</span>] ==>e[<span class="integer">23</span>][<span class="integer">8</span>-uses-><span class="integer">11</span>] ==>e[<span class="integer">24</span>][<span class="integer">9</span>-uses-><span class="integer">10</span>] ==>e[<span class="integer">25</span>][<span class="integer">9</span>-uses-><span class="integer">11</span>] gremlin> g.V().properties().properties().element() <span class="comment">//</span>// <b class="conum">(3)</b> ==>vp[location->san diego] ==>vp[location->san diego] ==>vp[location->santa cruz] ==>vp[location->santa cruz] ==>vp[location->brussels] ==>vp[location->brussels] ==>vp[location->santa fe] ==>vp[location->centreville] ==>vp[location->centreville] ==>vp[location->dulles] ==>vp[location->dulles] ==>vp[location->purcellville] ==>vp[location->bremen] ==>vp[location->bremen] ==>vp[location->baltimore] ==>vp[location->baltimore] ==>vp[location->oakland] ==>vp[location->oakland] ==>vp[location->seattle] ==>vp[location->spremberg] ==>vp[location->spremberg] ==>vp[location->kaiserslautern] ==>vp[location->kaiserslautern] ==>vp[location->aachen]</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().properties().element() <span class="comment">//</span>// <b class="conum">(1)</b> g.E().properties().element() <span class="comment">//</span>// <b class="conum">(2)</b> g.V().properties().properties().element() <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Traverse from <code>VertexProperty</code> to <code>Vertex</code></p> </li> <li> <p>Traverse from <code>Property</code> (edge property) to <code>Edge</code></p> </li> <li> <p>Traverse from <code>Property</code> (meta property) to <code>VertexProperty</code></p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#element()"><code>element()</code></a></p> </div> </div> <div class="sect2"> <h3 id="elementmap-step">ElementMap Step</h3> <div class="paragraph"> <p>The <code>elementMap()</code>-step yields a <code>Map</code> representation of the structure of an element.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-101" type="radio" name="radio-set-1729796988-101" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-101" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-102" type="radio" name="radio-set-1729796988-101" class="tab-selector-2" /> <label for="tab-1729796988-102" 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> g.V().elementMap() ==>[<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">29</span>] ==>[<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">27</span>] ==>[<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] ==>[<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">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<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">35</span>] gremlin> g.V().elementMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==>[<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="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">35</span>] gremlin> g.V().elementMap(<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">blah</span><span class="delimiter">'</span></span>) ==>[<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="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">age</span>:<span class="integer">35</span>] gremlin> g.E().elementMap() ==>[<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>] ==>[<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>] ==>[<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>] ==>[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:created,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">1.0</span>] ==>[<span class="key">id</span>:<span class="integer">11</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">4</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.4</span>] ==>[<span class="key">id</span>:<span class="integer">12</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">6</span>,<span class="key">label</span>:person],<span class="key">weight</span>:<span class="float">0.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().elementMap() g.V().elementMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) g.V().elementMap(<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">blah</span><span class="delimiter">'</span></span>) g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is important to note that the map of a vertex assumes that cardinality for each key is <code>single</code> and if it is <code>list</code> then only the first item encountered will be returned. As <code>single</code> is the more common cardinality for properties this assumption should serve the greatest number of use cases.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-103" type="radio" name="radio-set-1729796988-103" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-103" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-104" type="radio" name="radio-set-1729796988-103" class="tab-selector-2" /> <label for="tab-1729796988-104" 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> g.V().elementMap() ==>[<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">location</span>:santa fe] ==>[<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] ==>[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:person,<span class="key">name</span>:matthias,<span class="key">location</span>:seattle] ==>[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:person,<span class="key">name</span>:daniel,<span class="key">location</span>:aachen] ==>[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:software,<span class="key">name</span>:gremlin] ==>[<span class="key">id</span>:<span class="integer">11</span>,<span class="key">label</span>:software,<span class="key">name</span>:tinkergraph] gremlin> 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>) ==>vp[location->san diego] ==>vp[location->santa cruz] ==>vp[location->brussels] ==>vp[location->santa fe] gremlin> 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).elementMap() ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">key</span>:location,<span class="key">value</span>:san diego,<span class="key">startTime</span>:<span class="integer">1997</span>,<span class="key">endTime</span>:<span class="integer">2001</span>] ==>[<span class="key">id</span>:<span class="integer">7</span>,<span class="key">key</span>:location,<span class="key">value</span>:santa cruz,<span class="key">startTime</span>:<span class="integer">2001</span>,<span class="key">endTime</span>:<span class="integer">2004</span>] ==>[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">key</span>:location,<span class="key">value</span>:brussels,<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2005</span>] ==>[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">key</span>:location,<span class="key">value</span>:santa fe,<span class="key">startTime</span>:<span class="integer">2005</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().elementMap() 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).elementMap()</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>elementMap()</code>-step does not return the vertex labels for incident vertices when using <code>GraphComputer</code> as the <code>id</code> is the only available data to the star graph. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#elementMap(java.lang.String...)"><code>elementMap(String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="emit-step">Emit Step</h3> <div class="paragraph"> <p>The <code>emit</code>-step is not an actual step, but is instead a step modulator for <code><a href="#repeat-step">repeat()</a></code> (find more documentation on the <code>emit()</code> there).</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#emit()"><code>emit()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#emit(java.util.function.Predicate)"><code>emit(Predicate)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#emit(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>emit(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="explain-step">Explain Step</h3> <div class="paragraph"> <p>The <code>explain()</code>-step (<strong>terminal</strong>) will return a <code>TraversalExplanation</code>. A traversal explanation details how the traversal (prior to <code>explain()</code>) will be compiled given the registered <a href="#traversalstrategy">traversal strategies</a>. A <code>TraversalExplanation</code> has a <code>toString()</code> representation with 3-columns. The first column is the traversal strategy being applied. The second column is the traversal strategy category: [D]ecoration, [O]ptimization, [P]rovider optimization, [F]inalization, and [V]erification. Finally, the third column is the state of the traversal post strategy application. The final traversal is the resultant execution plan.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-105" type="radio" name="radio-set-1729796988-105" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-105" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-106" type="radio" name="radio-set-1729796988-105" class="tab-selector-2" /> <label for="tab-1729796988-106" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).outE().identity().inV().count().is(gt(<span class="integer">5</span>)).explain() ==>Traversal Explanation ================================================================================================================================================================================== Original Traversal [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] ConnectiveStrategy [D] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] IdentityRemovalStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] MatchPredicateStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] FilterRankingStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] InlineFilterStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] IncidentToAdjacentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,vertex), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] RepeatUnrollStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,vertex), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] PathRetractionStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,vertex), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] EarlyLimitStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,vertex), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] AdjacentToIncidentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] ByModulatorOptimizationStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] CountStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] LazyBarrierStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] TinkerGraphCountStrategy [P] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] ProfileStrategy [F] [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] StandardVerificationStrategy [V] [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(gt(<span class="integer">5</span>))] Final Traversal [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">6</span>), CountGlobalStep, IsStep(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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).outE().identity().inV().count().is(gt(<span class="integer">5</span>)).explain()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>For traversal profiling information, please see <a href="#profile-step"><code>profile()</code></a>-step.</p> </div> </div> <div class="sect2"> <h3 id="fail-step">Fail Step</h3> <div class="paragraph"> <p>The <code>fail()</code>-step provides a way to force a traversal to immediately fail with an exception. This feature is often helpful during debugging purposes and for validating certain conditions prior to continuing with traversal execution.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> g.V().has('person','name','peter').fold(). ......1> coalesce(unfold(), ......2> fail('peter should exist')). ......3> property('k',100) ==>v[6] gremlin> g.V().has('person','name','stephen').fold(). ......1> coalesce(unfold(), ......2> fail('stephen should exist')). ......3> property('k',100) fail() Step Triggered =========================================================================================================================== Message > stephen should exist Traverser> [] Bulk > 1 Traversal> fail() Parent > CoalesceStep [V().has("person","name","stephen").fold().coalesce(__.unfold(),__.fail()).property("k",(int) 100)] Metadata > {} ===========================================================================================================================</code></pre> </div> </div> <div class="paragraph"> <p>The code example above exemplifies the latter use case where there is essentially an assertion that there is a vertex with a particular "name" value prior to updating the property "k" and explicitly failing when that vertex is not found.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fail()"><code>fail()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fail(java.lang.String)"><code>fail(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="filter-step">Filter Step</h3> <div class="paragraph"> <p>The <code>filter()</code> step maps the traverser from the current object to either <code>true</code> or <code>false</code> where the latter will not pass the traverser to the next step in the process. Please see the <a href="#general-steps">General Steps</a> section for more information.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#filter(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>map(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="flatmap-step">FlatMap Step</h3> <div class="paragraph"> <p>The <code>flatMap()</code> step maps the traverser from the current object to an <code>Iterator</code> of objects for the next step in the process. Please see the <a href="#general-steps">General Steps</a> section for more information.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#flatMap(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>map(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="format-step">Format Step</h3> <div class="paragraph"> <p>This step is designed to simplify some string operations. In general, it is similar to the string formatting function available in many programming languages. Variable values can be picked up from Element properties, maps and scope variables.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-107" type="radio" name="radio-set-1729796988-107" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-107" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-108" type="radio" name="radio-set-1729796988-107" class="tab-selector-2" /> <label for="tab-1729796988-108" 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> g.V().format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} is %{age} years old</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>marko is <span class="integer">29</span> years old ==>vadas is <span class="integer">27</span> years old ==>josh is <span class="integer">32</span> years old ==>peter is <span class="integer">35</span> years old gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).as(<span class="string"><span class="delimiter">"</span><span class="content">p1</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).in(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).format(<span class="string"><span class="delimiter">"</span><span class="content">%{p1} knows %{name}</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>vadas knows marko ==>josh knows marko gremlin> g.V().format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} has %{_} connections</span><span class="delimiter">"</span></span>).by(bothE().count()) <span class="comment">//</span>// <b class="conum">(3)</b> ==>marko has <span class="integer">3</span> connections ==>vadas has <span class="integer">1</span> connections ==>lop has <span class="integer">3</span> connections ==>josh has <span class="integer">3</span> connections ==>ripple has <span class="integer">1</span> connections ==>peter has <span class="integer">1</span> connections gremlin> 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">count</span><span class="delimiter">"</span></span>).by(values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)).by(bothE().count()).format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} has %{count} connections</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==>marko has <span class="integer">3</span> connections ==>vadas has <span class="integer">1</span> connections ==>lop has <span class="integer">3</span> connections ==>josh has <span class="integer">3</span> connections ==>ripple has <span class="integer">1</span> connections ==>peter has <span class="integer">1</span> connections</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().format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} is %{age} years old</span><span class="delimiter">"</span></span>) <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>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).as(<span class="string"><span class="delimiter">"</span><span class="content">p1</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).in(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).format(<span class="string"><span class="delimiter">"</span><span class="content">%{p1} knows %{name}</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} has %{_} connections</span><span class="delimiter">"</span></span>).by(bothE().count()) <span class="comment">//</span>// <b class="conum">(3)</b> 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">count</span><span class="delimiter">"</span></span>).by(values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)).by(bothE().count()).format(<span class="string"><span class="delimiter">"</span><span class="content">%{name} has %{count} connections</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>A <code>format()</code> will use property values from incoming Element to produce String result.</p> </li> <li> <p>A <code>format()</code> will use scope variable <code>p1</code> and property <code>name</code> to resolve variable values.</p> </li> <li> <p>A <code>format()</code> will use property <code>name</code> and traversal product for positional argument to resolve variable values.</p> </li> <li> <p>A <code>format()</code> will use map produced by <code>project</code> step to resolve variable values.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#format(java.lang.String)"><code>format(String)</code></a>,</p> </div> </div> <div class="sect2"> <h3 id="fold-step">Fold Step</h3> <div class="paragraph"> <p>There are situations when the traversal stream needs a "barrier" to aggregate all the objects and emit a computation that is a function of the aggregate. The <code>fold()</code>-step (<strong>map</strong>) is one particular instance of this. Please see <a href="#unfold-step"><code>unfold()</code></a>-step for the inverse functionality.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-109" type="radio" name="radio-set-1729796988-109" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-109" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-110" type="radio" name="radio-set-1729796988-109" class="tab-selector-2" /> <label for="tab-1729796988-110" 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> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>josh gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[vadas,josh] gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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().getClass() <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="type">class</span> <span class="class">java</span>.util.ArrayList gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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(<span class="integer">0</span>) {a,b -> a + b.length()} <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="integer">9</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold(<span class="integer">0</span>) {a,b -> a + b} <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="integer">123</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold(<span class="integer">0</span>, sum) <span class="comment">//</span>// <b class="conum">(5)</b> ==><span class="integer">123</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum() <span class="comment">//</span>// <b class="conum">(6)</b> ==><span class="integer">123</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>:<span class="integer">1</span>],[<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>:<span class="integer">2</span>]).fold(<span class="type">[]</span>, addAll) <span class="comment">//</span>// <b class="conum">(7)</b> ==>[[<span class="key">a</span>:<span class="integer">1</span>],[<span class="key">b</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(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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().getClass() <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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(<span class="integer">0</span>) {a,b -> a + b.length()} <span class="comment">//</span>// <b class="conum">(3)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold(<span class="integer">0</span>) {a,b -> a + b} <span class="comment">//</span>// <b class="conum">(4)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold(<span class="integer">0</span>, sum) <span class="comment">//</span>// <b class="conum">(5)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum() <span class="comment">//</span>// <b class="conum">(6)</b> g.inject([<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>:<span class="integer">1</span>],[<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>:<span class="integer">2</span>]).fold(<span class="type">[]</span>, addAll) <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A parameterless <code>fold()</code> will aggregate all the objects into a list and then emit the list.</p> </li> <li> <p>A verification of the type of list returned.</p> </li> <li> <p><code>fold()</code> can be provided two arguments —  a seed value and a reduce bi-function ("vadas" is 5 characters + "josh" with 4 characters).</p> </li> <li> <p>What is the total age of the people in the graph?</p> </li> <li> <p>The same as before, but using a built-in bi-function.</p> </li> <li> <p>The same as before, but using the <a href="#sum-step"><code>sum()</code>-step</a>.</p> </li> <li> <p>A mechanism for merging <code>Map</code> instances. If a key occurs in more than a single <code>Map</code>, the later occurrence will replace the earlier.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fold()"><code>fold()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#fold(E2,java.util.function.BiFunction)"><code>fold(Object,BiFunction)</code></a></p> </div> </div> <div class="sect2"> <h3 id="from-step">From Step</h3> <div class="paragraph"> <p>The <code>from()</code>-step is not an actual step, but instead is a "step-modulator" similar to <a href="#as-step"><code>as()</code></a> and <a href="#by-step"><code>by()</code></a>. If a step is able to accept traversals or strings then <code>from()</code> is the means by which they are added. The general pattern is <code>step().from()</code>. See <a href="#to-step"><code>to()</code></a>-step.</p> </div> <div class="paragraph"> <p>The list of steps that support <code>from()</code>-modulation are: <a href="#simplepath-step"><code>simplePath()</code></a>, <a href="#cyclicpath-step"><code>cyclicPath()</code></a>, <a href="#path-step"><code>path()</code></a>, and <a href="#addedge-step"><code>addE()</code></a>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Javascript</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>from</code> is a reserved word in Javascript, and therefore must be referred to in Gremlin with <code>from_()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>from</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>from_()</code>.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#from(java.lang.String)"><code>from(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#from(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>from(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#from(org.apache.tinkerpop.gremlin.structure.Vertex)"><code>from(Vertex)</code></a></p> </div> </div> <div class="sect2"> <h3 id="group-step">Group Step</h3> <div class="paragraph"> <p>As traversers propagate across a graph as defined by a traversal, sideEffect computations are sometimes required. That is, the actual path taken or the current location of a traverser is not the ultimate output of the computation, but some other representation of the traversal. The <code>group()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>) is one such sideEffect that organizes the objects according to some function of the object. Then, if required, that organization (a list) is reduced. An example is provided below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-111" type="radio" name="radio-set-1729796988-111" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-111" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-112" type="radio" name="radio-set-1729796988-111" class="tab-selector-2" /> <label for="tab-1729796988-112" 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> g.V().group().by(label) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">software</span>:[v[<span class="integer">3</span>],v[<span class="integer">5</span>]],<span class="key">person</span>:[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> g.V().group().by(label).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">software</span>:[lop,ripple],<span class="key">person</span>:[marko,vadas,josh,peter]] gremlin> g.V().group().by(label).by(count()) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="key">software</span>:<span class="integer">2</span>,<span class="key">person</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().group().by(label) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().group().by(label).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().group().by(label).by(count()) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Group the vertices by their label.</p> </li> <li> <p>For each vertex in the group, get their name.</p> </li> <li> <p>For each grouping, what is its size?</p> </li> </ol> </div> <div class="paragraph"> <p>The two projection parameters available to <code>group()</code> via <code>by()</code> are:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Key-projection: What feature of the object to group on (a function that yields the map key)?</p> </li> <li> <p>Value-projection: What feature of the group to store in the key-list?</p> </li> </ol> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-113" type="radio" name="radio-set-1729796988-113" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-113" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-114" type="radio" name="radio-set-1729796988-113" class="tab-selector-2" /> <label for="tab-1729796988-114" 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> g.V().group().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">32</span>:[josh],<span class="integer">35</span>:[peter],<span class="integer">27</span>:[vadas],<span class="integer">29</span>:[marko]] gremlin> g.V().group().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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">(2)</b> ==>[<span class="key">ripple</span>:<span class="type">[]</span>,<span class="key">peter</span>:[<span class="integer">35</span>],<span class="key">vadas</span>:[<span class="integer">27</span>],<span class="key">josh</span>:[<span class="integer">32</span>],<span class="key">lop</span>:<span class="type">[]</span>,<span class="key">marko</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().group().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().group().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those keys are filtered.</p> </li> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#group()"><code>group()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#group(java.lang.String)"><code>group(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="groupcount-step">GroupCount Step</h3> <div class="paragraph"> <p>When it is important to know how many times a particular object has been at a particular part of a traversal, <code>groupCount()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>) is used.</p> </div> <div class="literalblock"> <div class="content"> <pre>"What is the distribution of ages in the graph?"</pre> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-115" type="radio" name="radio-set-1729796988-115" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-115" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-116" type="radio" name="radio-set-1729796988-115" class="tab-selector-2" /> <label for="tab-1729796988-116" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount() ==>[<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>] gremlin> 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>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<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>] gremlin> g.V().groupCount().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">(2)</b> ==>[<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>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount() 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>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">age</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>You can also supply a pre-group projection, where the provided <a href="#by-step"><code>by()</code></a>-modulation determines what to group the incoming object by.</p> </li> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>There is one person that is 32, one person that is 35, one person that is 27, and one person that is 29.</p> </div> <div class="literalblock"> <div class="content"> <pre>"Iteratively walk the graph and count the number of times you see the second letter of each name."</pre> </div> </div> <div class="imageblock"> <div class="content"> <img src="../images/groupcount-step.png" alt="groupcount step" width="420"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-117" type="radio" name="radio-set-1729796988-117" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-117" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-118" type="radio" name="radio-set-1729796988-117" class="tab-selector-2" /> <label for="tab-1729796988-118" 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> g.V().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(label)).times(<span class="integer">10</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>) ==>[<span class="key">software</span>:<span class="integer">19598</span>,<span class="key">person</span>:<span class="integer">39196</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().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).by(label)).times(<span class="integer">10</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above is interesting in that it demonstrates the use of referencing the internal <code>Map<Object,Long></code> of <code>groupCount()</code> with a string variable. Given that <code>groupCount()</code> is a sideEffect-step, it simply passes the object it received to its output. Internal to <code>groupCount()</code>, the object’s count is incremented.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#groupCount()"><code>groupCount()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#groupCount(java.lang.String)"><code>groupCount(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="has-step">Has Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/has-step.png" alt="has step" width="670"> </div> </div> <div class="paragraph"> <p>It is possible to filter vertices, edges, and vertex properties based on their properties using <code>has()</code>-step (<strong>filter</strong>). There are numerous variations on <code>has()</code> including:</p> </div> <div class="ulist"> <ul> <li> <p><code>has(key,value)</code>: Remove the traverser if its element does not have the provided key/value property.</p> </li> <li> <p><code>has(label, key, value)</code>: Remove the traverser if its element does not have the specified label and provided key/value property.</p> </li> <li> <p><code>has(key,predicate)</code>: Remove the traverser if its element does not have a key value that satisfies the bi-predicate. For more information on predicates, please read <a href="#a-note-on-predicates">A Note on Predicates</a>.</p> </li> <li> <p><code>hasLabel(labels…​)</code>: Remove the traverser if its element does not have any of the labels.</p> </li> <li> <p><code>hasId(ids…​)</code>: Remove the traverser if its element does not have any of the ids.</p> </li> <li> <p><code>hasKey(keys…​)</code>: Remove the <code>Property</code> traverser if it does not match one of the provided keys.</p> </li> <li> <p><code>hasValue(values…​)</code>: Remove the <code>Property</code> traverser if it does not match one of the provided values.</p> </li> <li> <p><code>has(key)</code>: Remove the traverser if its element does not have a value for the key.</p> </li> <li> <p><code>hasNot(key)</code>: Remove the traverser if its element has a value for the key.</p> </li> <li> <p><code>has(key, traversal)</code>: Remove the traverser if its object does not yield a result through the traversal off the property value.</p> </li> </ul> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-119" type="radio" name="radio-set-1729796988-119" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-119" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-120" type="radio" name="radio-set-1729796988-119" class="tab-selector-2" /> <label for="tab-1729796988-120" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) ==>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> g.V().hasLabel(<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>) ==>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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).out().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).out().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)). outE().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) ==>e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>] ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,inside(<span class="integer">20</span>,<span class="integer">30</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> ==><span class="integer">29</span> ==><span class="integer">27</span> gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,outside(<span class="integer">20</span>,<span class="integer">30</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">(2)</b> ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).elementMap() <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<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">29</span>] ==>[<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">32</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,without(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).elementMap() <span class="comment">//</span>// <b class="conum">(4)</b> ==>[<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">27</span>] ==>[<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] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<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">35</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,not(within(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>))).elementMap() <span class="comment">//</span>// <b class="conum">(5)</b> ==>[<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">27</span>] ==>[<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] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<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">35</span>] gremlin> g.V().properties().hasKey(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).value() <span class="comment">//</span>// <b class="conum">(6)</b> ==><span class="integer">29</span> ==><span class="integer">27</span> ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().hasNot(<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>) <span class="comment">//</span>// <b class="conum">(7)</b> ==>lop ==>ripple gremlin> 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>, startingWith(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(8)</b> ==>v[<span class="integer">1</span>] gremlin> g.V().has(<span class="predefined-constant">null</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">(9)</b> gremlin> g.V().has(label, __.is(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(10)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) g.V().hasLabel(<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>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).out().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).out().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">vadas</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>)). outE().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,inside(<span class="integer">20</span>,<span class="integer">30</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">age</span><span class="delimiter">'</span></span>,outside(<span class="integer">20</span>,<span class="integer">30</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">(2)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,within(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).elementMap() <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>,without(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>)).elementMap() <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>,not(within(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>))).elementMap() <span class="comment">//</span>// <b class="conum">(5)</b> g.V().properties().hasKey(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).value() <span class="comment">//</span>// <b class="conum">(6)</b> g.V().hasNot(<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>) <span class="comment">//</span>// <b class="conum">(7)</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>, startingWith(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(8)</b> g.V().has(<span class="predefined-constant">null</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">(9)</b> g.V().has(label, __.is(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)) <span class="invisible">//</span><b class="conum">10</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find all vertices whose ages are between 20 (exclusive) and 30 (exclusive). In other words, the age must be greater than 20 and less than 30.</p> </li> <li> <p>Find all vertices whose ages are not between 20 (inclusive) and 30 (inclusive). In other words, the age must be less than 20 or greater than 30.</p> </li> <li> <p>Find all vertices whose names are exact matches to any names in the collection <code>[josh,marko]</code>, display all the key,value pairs for those vertices.</p> </li> <li> <p>Find all vertices whose names are not in the collection <code>[josh,marko]</code>, display all the key,value pairs for those vertices.</p> </li> <li> <p>Same as the prior example save using <code>not</code> on <code>within</code> to yield <code>without</code>.</p> </li> <li> <p>Find all age-properties and emit their value.</p> </li> <li> <p>Find all vertices that do not have an age-property and emit their name.</p> </li> <li> <p>Find all "person" vertices that have a name property that starts with the letter "m".</p> </li> <li> <p>Property key is always stored as <code>String</code> and therefore an equality check with <code>null</code> will produce no result.</p> </li> <li> <p>An example of <code>has()</code> where the argument is a <code>Traversal</code> and does not quite behave the way most expect.</p> </li> </ol> </div> <div class="paragraph"> <p>Item 10 in the above set of examples bears some discussion. The behavior is not such that the result of the <code>Traversal</code> is used as the comparing value for <code>has()</code>, but the current <code>Traverser</code>, which in this case is the vertex <code>label</code>, is given to the <code>Traversal</code> to behave as a filter itself. In other words, if the <code>Traversal</code> (i.e. <code>is('person')</code>) returns a value then the <code>has()</code> is effectively <code>true</code>. A common mistake is to try to use <code>select()</code> in this context where one would do <code>has('name', select('n'))</code> to try to inject the value of "n" into the step to get <code>has('name', <value-of-n>)</code>, but this would instead simply produce an always <code>true</code> filter for <code>has()</code>.</p> </div> <div class="paragraph"> <p>TinkerPop does not support a regular expression predicate, although specific graph databases that leverage TinkerPop may provide a partial match extension.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String)"><code>has(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String,java.lang.Object)"><code>has(String,Object)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.P)"><code>has(String,P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String,java.lang.String,java.lang.Object)"><code>has(String,String,Object)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String,java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.P)"><code>has(String,String,P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>has(String,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(org.apache.tinkerpop.gremlin.structure.T,java.lang.Object)"><code>has(T,Object)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(org.apache.tinkerpop.gremlin.structure.T,org.apache.tinkerpop.gremlin.process.traversal.P)"><code>has(T,P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(org.apache.tinkerpop.gremlin.structure.T,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>has(T,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasId(java.lang.Object,java.lang.Object...)"><code>hasId(Object,Object…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasId(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>hasId(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasKey(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>hasKey(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasKey(java.lang.String,java.lang.String...)"><code>hasKey(String,String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasLabel(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>hasLabel(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasLabel(java.lang.String,java.lang.String...)"><code>hasLabel(String,String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasNot(java.lang.String)"><code>hasNot(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasValue(java.lang.Object,java.lang.Object...)"><code>hasValue(Object,Object…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#hasValue(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>hasValue(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/P.html"><code>P</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/TextP.html"><code>TextP</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/structure/T.html"><code>T</code></a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/#has-traversal">Recipes - Anti-pattern</a></p> </div> </div> <div class="sect2"> <h3 id="id-step">Id Step</h3> <div class="paragraph"> <p>The <code>id()</code>-step (<strong>map</strong>) takes an <code>Element</code> and extracts its identifier from it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-121" type="radio" name="radio-set-1729796988-121" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-121" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-122" type="radio" name="radio-set-1729796988-121" class="tab-selector-2" /> <label for="tab-1729796988-122" 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> g.V().id() ==><span class="integer">1</span> ==><span class="integer">2</span> ==><span class="integer">3</span> ==><span class="integer">4</span> ==><span class="integer">5</span> ==><span class="integer">6</span> gremlin> g.V(<span class="integer">1</span>).out().id().is(<span class="integer">2</span>) ==><span class="integer">2</span> gremlin> g.V(<span class="integer">1</span>).outE().id() ==><span class="integer">9</span> ==><span class="integer">7</span> ==><span class="integer">8</span> gremlin> g.V(<span class="integer">1</span>).properties().id() ==><span class="integer">0</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().id() g.V(<span class="integer">1</span>).out().id().is(<span class="integer">2</span>) g.V(<span class="integer">1</span>).outE().id() g.V(<span class="integer">1</span>).properties().id()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#id()"><code>id()</code></a></p> </div> </div> <div class="sect2"> <h3 id="identity-step">Identity Step</h3> <div class="paragraph"> <p>The <code>identity()</code>-step (<strong>map</strong>) is an <a href="https://en.wikipedia.org/wiki/Identity_function">identity function</a> which maps the current object to itself.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-123" type="radio" name="radio-set-1729796988-123" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-123" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-124" type="radio" name="radio-set-1729796988-123" class="tab-selector-2" /> <label for="tab-1729796988-124" 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> g.V().identity() ==>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().identity()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#identity()"><code>identity()</code></a></p> </div> </div> <div class="sect2"> <h3 id="index-step">Index Step</h3> <div class="paragraph"> <p>The <code>index()</code>-step (<strong>map</strong>) indexes each element in the current collection. If the current traverser’s value is not a collection, then it’s treated as a single-item collection. There are two indexers available, which can be chosen using the <code>with()</code> modulator. The list indexer (default) creates a list for each collection item, with the first item being the original element and the second element being the index. The map indexer created a linked hash map in which the index represents the key and the original item is used as the value.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-125" type="radio" name="radio-set-1729796988-125" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-125" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-126" type="radio" name="radio-set-1729796988-125" class="tab-selector-2" /> <label for="tab-1729796988-126" 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> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>).index() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[[v[<span class="integer">3</span>],<span class="integer">0</span>]] ==>[[v[<span class="integer">5</span>],<span class="integer">0</span>]] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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(). order(Scope.local). index(). unfold(). order(). by(__.tail(Scope.local, <span class="integer">1</span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[lop,<span class="integer">0</span>] ==>[ripple,<span class="integer">1</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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(). order(Scope.local). index(). with(WithOptions.indexer, WithOptions.list). unfold(). order(). by(__.tail(Scope.local, <span class="integer">1</span>)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[lop,<span class="integer">0</span>] ==>[ripple,<span class="integer">1</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</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(). order(Scope.local). index(). with(WithOptions.indexer, WithOptions.map) <span class="comment">//</span>// <b class="conum">(4)</b> ==>[<span class="integer">0</span>:josh,<span class="integer">1</span>:marko,<span class="integer">2</span>:peter,<span class="integer">3</span>:vadas]</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">software</span><span class="delimiter">"</span></span>).index() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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(). order(Scope.local). index(). unfold(). order(). by(__.tail(Scope.local, <span class="integer">1</span>)) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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(). order(Scope.local). index(). with(WithOptions.indexer, WithOptions.list). unfold(). order(). by(__.tail(Scope.local, <span class="integer">1</span>)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</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(). order(Scope.local). index(). with(WithOptions.indexer, WithOptions.map) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Indexing non-collection items results in multiple indexed single-item collections.</p> </li> <li> <p>Index all software names in their alphabetical order.</p> </li> <li> <p>Same as statement 1, but with an explicitely specified list indexer.</p> </li> <li> <p>Index all person names in their alphabetical order and store the result in an ordered map.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#index()"><code>index()</code></a></p> </div> </div> <div class="sect2"> <h3 id="inject-step">Inject Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/inject-step.png" alt="inject step" width="800"> </div> </div> <div class="paragraph"> <p>The concept of "injectable steps" makes it possible to insert objects arbitrarily into a traversal stream. In general, <code>inject()</code>-step (<strong>sideEffect</strong>) exists and a few examples are provided below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-127" type="radio" name="radio-set-1729796988-127" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-127" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-128" type="radio" name="radio-set-1729796988-127" class="tab-selector-2" /> <label for="tab-1729796988-128" 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> g.V(<span class="integer">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>) ==>daniel ==>ripple ==>lop gremlin> g.V(<span class="integer">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).map {<span class="local-variable">it</span>.get().length()} ==><span class="integer">6</span> ==><span class="integer">6</span> ==><span class="integer">3</span> gremlin> g.V(<span class="integer">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).map {<span class="local-variable">it</span>.get().length()}.path() ==>[daniel,<span class="integer">6</span>] ==>[v[<span class="integer">4</span>],v[<span class="integer">5</span>],ripple,<span class="integer">6</span>] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>],lop,<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">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>) g.V(<span class="integer">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).map {<span class="local-variable">it</span>.get().length()} g.V(<span class="integer">4</span>).out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).inject(<span class="string"><span class="delimiter">'</span><span class="content">daniel</span><span class="delimiter">'</span></span>).map {<span class="local-variable">it</span>.get().length()}.path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the last example above, note that the path starting with <code>daniel</code> is only of length 2. This is because the <code>daniel</code> string was inserted half-way in the traversal. Finally, a typical use case is provided below — when the start of the traversal is not a graph object.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-129" type="radio" name="radio-set-1729796988-129" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-129" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-130" type="radio" name="radio-set-1729796988-129" class="tab-selector-2" /> <label for="tab-1729796988-130" 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> inject(<span class="integer">1</span>,<span class="integer">2</span>) ==><span class="integer">1</span> ==><span class="integer">2</span> gremlin> inject(<span class="integer">1</span>,<span class="integer">2</span>).map {<span class="local-variable">it</span>.get() + <span class="integer">1</span>} ==><span class="integer">2</span> ==><span class="integer">3</span> gremlin> inject(<span class="integer">1</span>,<span class="integer">2</span>).map {<span class="local-variable">it</span>.get() + <span class="integer">1</span>}.map {g.V(<span class="local-variable">it</span>.get()).next()}.values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>lop</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">inject(<span class="integer">1</span>,<span class="integer">2</span>) inject(<span class="integer">1</span>,<span class="integer">2</span>).map {<span class="local-variable">it</span>.get() + <span class="integer">1</span>} inject(<span class="integer">1</span>,<span class="integer">2</span>).map {<span class="local-variable">it</span>.get() + <span class="integer">1</span>}.map {g.V(<span class="local-variable">it</span>.get()).next()}.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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#inject(E...)"><code>inject(Object)</code></a></p> </div> </div> <div class="sect2"> <h3 id="intersect-step">Intersect Step</h3> <div class="paragraph"> <p>The <code>intersect()</code>-step (<strong>map</strong>) calculates the intersection between the incoming list traverser and the provided list argument. This step only expects list data (array or Iterable) and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-131" type="radio" name="radio-set-1729796988-131" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-131" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-132" type="radio" name="radio-set-1729796988-131" class="tab-selector-2" /> <label for="tab-1729796988-132" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().intersect([<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</span><span class="delimiter">"</span></span>]) ==>[josh,marko] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().intersect(__.V().limit(<span class="integer">2</span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold()) ==>[vadas,marko]</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">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().intersect([<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</span><span class="delimiter">"</span></span>]) g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().intersect(__.V().limit(<span class="integer">2</span>).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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#intersect(java.lang.Object)"><code>intersect(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#intersect-step"><code>Semantics</code></a></p> </div> <div class="paragraph"> <p><a id="_gremlin_i_o"></a></p> </div> </div> <div class="sect2"> <h3 id="io-step">IO Step</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-io.png" alt="gremlin io" width="250"></span> The task of importing and exporting the data of <code>Graph</code> instances is the job of the <code>io()</code>-step. By default, TinkerPop supports three formats for importing and exporting graph data in <a href="#graphml">GraphML</a>, <a href="#graphson">GraphSON</a>, and <a href="#gryo">Gryo</a>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Additional documentation for TinkerPop IO formats can be found in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/">IO Reference</a>. </td> </tr> </table> </div> <div class="paragraph"> <p>By itself the <code>io()</code>-step merely configures the kind of importing and exporting that is going to occur and it is the follow-on call to the <code>read()</code> or <code>write()</code> step that determines which of those actions will execute. Therefore, a typical usage of the <code>io()</code>-step would look like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.io(someInputFile).read().iterate() g.io(someOutputFile).write().iterate()</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The commands above are still traversals and therefore require iteration to be executed, hence the use of <code>iterate()</code> as a termination step. </td> </tr> </table> </div> <div class="paragraph"> <p>By default, the <code>io()</code>-step will try to detect the right file format using the file name extension. To gain greater control of the format use the <code>with()</code> step modulator to provide further information to <code>io()</code>. For example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.io(someInputFile). with(IO.reader, IO.graphson). read().iterate() g.io(someOutputFile). with(IO.writer,IO.graphml). write().iterate()</code></pre> </div> </div> <div class="paragraph"> <p>The <code>IO</code> class is a helper for the <code>io()</code>-step that provides expressions that can be used to help configure it and in this case it allows direct specification of the "reader" or "writer" to use. The "reader" actually refers to a <code>GraphReader</code> implementation and the "writer" refers to a <code>GraphWriter</code> implementation. The implementations of those interfaces provided by default are the standard TinkerPop implementations.</p> </div> <div class="paragraph"> <p>That default is an important point to consider for users. The default TinkerPop implementations are not designed with massive, complex, parallel bulk loading in mind. They are designed to do single-threaded, OLTP-style loading of data in the most generic way possible so as to accommodate the greatest number of graph databases out there. As such, from a reading perspective, they work best for small datasets (or perhaps medium datasets where memory is plentiful and time is not critical) that are loading to an empty graph - incremental loading is not supported. The story from the writing perspective is not that different in there are no parallel operations in play, however streaming the output to disk requires a single pass of the data without high memory requirements for larger datasets.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Default graph formats don’t contain information about property cardinality, so it is up to the graph provider to choose the appropriate one. You will see a warning message if the chosen cardinality is SINGLE while your graph input contains multiple values for that property. </td> </tr> </table> </div> <div class="paragraph"> <p>In general, TinkerPop recommends that users examine the native bulk import/export tools of the graph implementation that they choose. Those tools will often outperform the <code>io()</code>-step and perhaps be easier to use with a greater feature set. That said, graph providers do have the option to optimize <code>io()</code> to back it with their own import/export utilities and therefore the default behavior provided by TinkerPop described above might be overridden by the graph.</p> </div> <div class="paragraph"> <p>An excellent example of this lies in <a href="#hadoop-gremlin">HadoopGraph</a> with <a href="#sparkgraphcomputer">SparkGraphComputer</a> which replaces the default single-threaded implementation with a more advanced OLAP style bulk import/export functionality internally using <a href="#clonevertexprogram">CloneVertexProgram</a>. With this model, graphs of arbitrary size can be imported/exported assuming that there is a Hadoop <code>InputFormat</code> or <code>OutputFormat</code> to support it.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Remote Gremlin Console users or Gremlin Language Variant (GLV) users (e.g. gremlin-python) who utilize the <code>io()</code>-step should recall that their <code>read()</code> or <code>write()</code> operation will occur on the server and not locally and therefore the file specified for import/export must be something accessible by the server. </td> </tr> </table> </div> <div class="paragraph"> <p>GraphSON and Gryo formats are extensible allowing users and graph providers to extend supported serialization options. These extensions are exposed through <code>IoRegistry</code> implementations. To apply an <code>IoRegistry</code> use the <code>with()</code> option and the <code>IO.registry</code> key, where the value is either an actual <code>IoRegistry</code> instance or the fully qualified class name of one.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.io(someInputFile). with(IO.reader, IO.gryo). with(IO.registry, TinkerIoRegistryV3d0.instance()) read().iterate() g.io(someOutputFile). with(IO.writer,IO.graphson). with(IO.registry, <span class="string"><span class="delimiter">"</span><span class="content">org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0</span><span class="delimiter">"</span></span>) write().iterate()</code></pre> </div> </div> <div class="paragraph"> <p>GLVs will obviously always be forced to use the latter form as they can’t explicitly create an instance of an <code>IoRegistry</code> to pass to the server (nor are <code>IoRegistry</code> instances necessarily serializable).</p> </div> <div class="paragraph"> <p>The version of the formats (e.g. GraphSON 2.0 or 3.0) utilized by <code>io()</code> is determined entirely by the <code>IO.reader</code> and <code>IO.writer</code> configurations or their defaults. The defaults will always be the latest version for the current release of TinkerPop. It is also possible for graph providers to override these defaults, so consult the documentation of the underlying graph database in use for any details on that.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>io()</code> step will try to automatically detect the appropriate <code>GraphReader</code> or <code>GraphWriter</code> to use based on the file extension. If the file has a different extension than the ones expected, use <code>with()</code> as shown above to set the <code>reader</code> or <code>writer</code> explicitly. </td> </tr> </table> </div> <div class="paragraph"> <p>For more advanced configuration of <code>GraphReader</code> and <code>GraphWriter</code> operations (e.g. normalized output for GraphSON, disabling class registrations for Gryo, etc.) then construct the appropriate <code>GraphReader</code> and <code>GraphWriter</code> using the <code>build()</code> method on their implementations and use it directly. It can be passed directly to the <code>IO.reader</code> or <code>IO.writer</code> options. Obviously, these are JVM based operations and thus not available to GLVs as portable features.</p> </div> <div class="paragraph"> <p><a id="_graphml_reader_writer"></a></p> </div> <div class="sect3"> <h4 id="graphml">GraphML</h4> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-graphml.png" alt="gremlin graphml" width="350"></span> The <a href="http://graphml.graphdrawing.org/">GraphML</a> file format is a common XML-based representation of a graph. It is widely supported by graph-related tools and libraries making it a solid interchange format for TinkerPop. In other words, if the intent is to work with graph data in conjunction with applications outside of TinkerPop, GraphML may be the best choice to do that. Common use cases might be:</p> </div> <div class="ulist"> <ul> <li> <p>Generate a graph using <a href="https://networkx.github.io/">NetworkX</a>, export it with GraphML and import it to TinkerPop.</p> </li> <li> <p>Produce a subgraph and export it to GraphML to be consumed by and visualized in <a href="https://gephi.org/">Gephi</a>.</p> </li> <li> <p>Migrate the data of an entire graph to a different graph database not supported by TinkerPop.</p> </li> </ul> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> GraphML is a "lossy" format in that it only supports primitive values for properties and does not have support for <code>Graph</code> variables. It will use <code>toString</code> to serialize property values outside of those primitives. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> GraphML as a specification allows for <code><edge></code> and <code><node></code> elements to appear in any order. Most software that writes GraphML (including as TinkerPop’s <code>GraphMLWriter</code>) write <code><node></code> elements before <code><edge></code> elements. However it is important to note that <code>GraphMLReader</code> will read this data in order and order can matter. This is because TinkerPop does not allow the vertex label to be changed after the vertex has been created. Therefore, if an <code><edge></code> element comes before the <code><node></code>, the label on the vertex will be ignored. It is thus better to order <code><node></code> elements in the GraphML to appear before all <code><edge></code> elements if vertex labels are important to the graph. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// expects a file extension of .xml or .graphml to determine that</span> <span class="comment">// a GraphML reader/writer should be used.</span> g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.xml</span><span class="delimiter">"</span></span>).read().iterate(); g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.xml</span><span class="delimiter">"</span></span>).write().iterate();</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> If using GraphML generated from TinkerPop 2.x, read more about its incompatibilities in the <a href="https://tinkerpop.apache.org/docs/3.7.3/upgrade/#graphml-format">Upgrade Documentation</a>. </td> </tr> </table> </div> <div class="paragraph"> <p><a id="graphson-reader-writer"></a></p> </div> </div> <div class="sect3"> <h4 id="graphson">GraphSON</h4> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-graphson.png" alt="gremlin graphson" width="350"></span> GraphSON is a <a href="http://json.org/">JSON</a>-based format extended from earlier versions of TinkerPop. It is important to note that TinkerPop’s GraphSON is not backwards compatible with prior TinkerPop GraphSON versions. GraphSON has some support from graph-related application outside of TinkerPop, but it is generally best used in two cases:</p> </div> <div class="ulist"> <ul> <li> <p>A text format of the graph or its elements is desired (e.g. debugging, usage in source control, etc.)</p> </li> <li> <p>The graph or its elements need to be consumed by code that is not JVM-based (e.g. JavaScript, Python, .NET, etc.)</p> </li> </ul> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// expects a file extension of .json to interpret that</span> <span class="comment">// a GraphSON reader/writer should be used</span> g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.json</span><span class="delimiter">"</span></span>).read().iterate(); g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.json</span><span class="delimiter">"</span></span>).write().iterate();</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Additional documentation for GraphSON can be found in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/#graphson">IO Reference</a>. </td> </tr> </table> </div> <div class="paragraph"> <p><a id="gryo-reader-writer"></a></p> </div> </div> <div class="sect3"> <h4 id="gryo">Gryo</h4> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-kryo.png" alt="gremlin kryo" width="400"></span> <a href="https://github.com/EsotericSoftware/kryo">Kryo</a> is a popular serialization package for the JVM. Gremlin-Kryo is a binary <code>Graph</code> serialization format for use on the JVM by JVM languages. It is designed to be space efficient, non-lossy and is promoted as the standard format to use when working with graph data inside of the TinkerPop stack. A list of common use cases is presented below:</p> </div> <div class="ulist"> <ul> <li> <p>Migration from one Gremlin Structure implementation to another (e.g. <code>TinkerGraph</code> to <code>Neo4jGraph</code>)</p> </li> <li> <p>Serialization of individual graph elements to be sent over the network to another JVM.</p> </li> <li> <p>Backups of in-memory graphs or subgraphs.</p> </li> </ul> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> When migrating between Gremlin Structure implementations, Kryo may not lose data, but it is important to consider the features of each <code>Graph</code> and whether or not the data types supported in one will be supported in the other. Failure to do so, may result in errors. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// expects a file extension of .kryo to interpret that</span> <span class="comment">// a GraphSON reader/writer should be used</span> g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.kryo</span><span class="delimiter">"</span></span>).read().iterate() g.io(<span class="string"><span class="delimiter">"</span><span class="content">graph.kryo</span><span class="delimiter">"</span></span>).write().iterate()</code></pre> </div> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.html#io(java.lang.String)"><code>io(String)</code></a></p> </div> </div> </div> <div class="sect2"> <h3 id="is-step">Is Step</h3> <div class="paragraph"> <p>It is possible to filter scalar values using <code>is()</code>-step (<strong>filter</strong>).</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>is</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>is_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-133" type="radio" name="radio-set-1729796988-133" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-133" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-134" type="radio" name="radio-set-1729796988-133" class="tab-selector-2" /> <label for="tab-1729796988-134" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(<span class="integer">32</span>) ==><span class="integer">32</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lte(<span class="integer">30</span>)) ==><span class="integer">29</span> ==><span class="integer">27</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(inside(<span class="integer">30</span>, <span class="integer">40</span>)) ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(<span class="integer">1</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">(1)</b> ==>ripple gremlin> g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gte(<span class="integer">2</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">(2)</b> ==>lop gremlin> g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). mean().is(inside(<span class="float">30d</span>, <span class="float">35d</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">(3)</b> ==>lop ==>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.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(<span class="integer">32</span>) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(lte(<span class="integer">30</span>)) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).is(inside(<span class="integer">30</span>, <span class="integer">40</span>)) g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(<span class="integer">1</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">(1)</b> g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gte(<span class="integer">2</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">(2)</b> g.V().where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). mean().is(inside(<span class="float">30d</span>, <span class="float">35d</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">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find projects having exactly one contributor.</p> </li> <li> <p>Find projects having two or more contributors.</p> </li> <li> <p>Find projects whose contributors average age is between 30 and 35.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#is(java.lang.Object)"><code>is(Object)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#is(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>is(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/P.html"><code>P</code></a></p> </div> </div> <div class="sect2"> <h3 id="key-step">Key Step</h3> <div class="paragraph"> <p>The <code>key()</code>-step (<strong>map</strong>) takes a <code>Property</code> and extracts the key from it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-135" type="radio" name="radio-set-1729796988-135" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-135" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-136" type="radio" name="radio-set-1729796988-135" class="tab-selector-2" /> <label for="tab-1729796988-136" 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> g.V(<span class="integer">1</span>).properties().key() ==>name ==>location ==>location ==>location ==>location gremlin> g.V(<span class="integer">1</span>).properties().properties().key() ==>startTime ==>endTime ==>startTime ==>endTime ==>startTime ==>endTime ==>startTime</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>).properties().key() g.V(<span class="integer">1</span>).properties().properties().key()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#key()"><code>key()</code></a></p> </div> </div> <div class="sect2"> <h3 id="label-step">Label Step</h3> <div class="paragraph"> <p>The <code>label()</code>-step (<strong>map</strong>) takes an <code>Element</code> and extracts its label from it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-137" type="radio" name="radio-set-1729796988-137" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-137" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-138" type="radio" name="radio-set-1729796988-137" class="tab-selector-2" /> <label for="tab-1729796988-138" 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> g.V().label() ==>person ==>person ==>software ==>person ==>software ==>person gremlin> g.V(<span class="integer">1</span>).outE().label() ==>created ==>knows ==>knows gremlin> g.V(<span class="integer">1</span>).properties().label() ==>name ==>age</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().label() g.V(<span class="integer">1</span>).outE().label() g.V(<span class="integer">1</span>).properties().label()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#label()"><code>label()</code></a></p> </div> </div> <div class="sect2"> <h3 id="length-step">Length Step</h3> <div class="paragraph"> <p>The <code>length()</code>-step (<strong>map</strong>) returns the length incoming string or list of string traverser. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-139" type="radio" name="radio-set-1729796988-139" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-139" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-140" type="radio" name="radio-set-1729796988-139" class="tab-selector-2" /> <label for="tab-1729796988-140" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).length() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">5</span> ==><span class="integer">5</span> ==><span class="integer">3</span> ==><span class="integer">4</span> ==><span class="integer">6</span> ==><span class="integer">5</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().length(local) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="integer">5</span>,<span class="integer">5</span>,<span class="integer">3</span>,<span class="integer">4</span>,<span class="integer">6</span>,<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().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).length() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().length(local) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Return the string length of all vertex names.</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#length()"><code>length()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#length(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>length(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="limit-step">Limit Step</h3> <div class="paragraph"> <p>The <code>limit()</code>-step is analogous to <a href="#range-step"><code>range()</code>-step</a> save that the lower end range is set to 0.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-141" type="radio" name="radio-set-1729796988-141" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-141" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-142" type="radio" name="radio-set-1729796988-141" class="tab-selector-2" /> <label for="tab-1729796988-142" 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> g.V().limit(<span class="integer">2</span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] gremlin> g.V().range(<span class="integer">0</span>, <span class="integer">2</span>) ==>v[<span class="integer">1</span>] ==>v[<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().limit(<span class="integer">2</span>) g.V().range(<span class="integer">0</span>, <span class="integer">2</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>limit()</code>-step can also be applied with <code>Scope.local</code>, in which case it operates on the incoming collection. The examples below use the <a href="#the-crew-toy-graph">The Crew</a> toy data set.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-143" type="radio" name="radio-set-1729796988-143" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-143" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-144" type="radio" name="radio-set-1729796988-143" class="tab-selector-2" /> <label for="tab-1729796988-144" 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> g.V().valueMap().select(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).limit(local,<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[san diego,santa cruz] ==>[centreville,dulles] ==>[bremen,baltimore] ==>[spremberg,kaiserslautern] gremlin> g.V().valueMap().limit(local, <span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">name</span>:[marko]] ==>[<span class="key">name</span>:[stephen]] ==>[<span class="key">name</span>:[matthias]] ==>[<span class="key">name</span>:[daniel]] ==>[<span class="key">name</span>:[gremlin]] ==>[<span class="key">name</span>:[tinkergraph]]</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().valueMap().select(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).limit(local,<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().valueMap().limit(local, <span class="integer">1</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>List<String></code> for each vertex containing the first two locations.</p> </li> <li> <p><code>Map<String, Object></code> for each vertex, but containing only the first property value.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#limit(long)"><code>limit(long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#limit(org.apache.tinkerpop.gremlin.process.traversal.Scope,long)"><code>limit(Scope,long)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="local-step">Local Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/local-step.png" alt="local step" width="450"> </div> </div> <div class="paragraph"> <p>A <code>GraphTraversal</code> operates on a continuous stream of objects. In many situations, it is important to operate on a single element within that stream. To do such object-local traversal computations, <code>local()</code>-step exists (<strong>branch</strong>). Note that the examples below use the <a href="#the-crew-toy-graph">The Crew</a> toy data set.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-145" type="radio" name="radio-set-1729796988-145" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-145" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-146" type="radio" name="radio-set-1729796988-145" class="tab-selector-2" /> <label for="tab-1729796988-146" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>,asc).limit(<span class="integer">2</span>).value().as(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>). select(<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">location</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() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">person</span>:daniel,<span class="key">location</span>:spremberg] ==>[<span class="key">person</span>:stephen,<span class="key">location</span>:centreville] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). local(properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>,asc).limit(<span class="integer">2</span>)).value().as(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>). select(<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">location</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() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">person</span>:marko,<span class="key">location</span>:san diego] ==>[<span class="key">person</span>:marko,<span class="key">location</span>:santa cruz] ==>[<span class="key">person</span>:stephen,<span class="key">location</span>:centreville] ==>[<span class="key">person</span>:stephen,<span class="key">location</span>:dulles] ==>[<span class="key">person</span>:matthias,<span class="key">location</span>:bremen] ==>[<span class="key">person</span>:matthias,<span class="key">location</span>:baltimore] ==>[<span class="key">person</span>:daniel,<span class="key">location</span>:spremberg] ==>[<span class="key">person</span>:daniel,<span class="key">location</span>:kaiserslautern]</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">person</span><span class="delimiter">'</span></span>). properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>,asc).limit(<span class="integer">2</span>).value().as(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>). select(<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">location</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() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). local(properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>,asc).limit(<span class="integer">2</span>)).value().as(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>). select(<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">location</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() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Get the first two people and their respective location according to the most historic location start time.</p> </li> <li> <p>For every person, get their two most historic locations.</p> </li> </ol> </div> <div class="paragraph"> <p>The two traversals above look nearly identical save the inclusion of <code>local()</code> which wraps a section of the traversal in an object-local traversal. As such, the <code>order().by()</code> and the <code>limit()</code> refer to a particular object, not to the stream as a whole.</p> </div> <div class="paragraph"> <p>Local Step is quite similar in functionality to <a href="#general-steps">Flat Map Step</a> where it can often be confused. <code>local()</code> propagates the traverser through the internal traversal as is without splitting/cloning it. Thus, its a “global traversal” with local processing. Its use is subtle and primarily finds application in compilation optimizations (i.e. when writing <code>TraversalStrategy</code> implementations. As another example consider:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-147" type="radio" name="radio-set-1729796988-147" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-147" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-148" type="radio" name="radio-set-1729796988-147" class="tab-selector-2" /> <label for="tab-1729796988-148" 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> g.V().both().barrier().flatMap(groupCount().by(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)) ==>[<span class="key">lop</span>:<span class="integer">1</span>] ==>[<span class="key">lop</span>:<span class="integer">1</span>] ==>[<span class="key">lop</span>:<span class="integer">1</span>] ==>[<span class="key">vadas</span>:<span class="integer">1</span>] ==>[<span class="key">josh</span>:<span class="integer">1</span>] ==>[<span class="key">josh</span>:<span class="integer">1</span>] ==>[<span class="key">josh</span>:<span class="integer">1</span>] ==>[<span class="key">marko</span>:<span class="integer">1</span>] ==>[<span class="key">marko</span>:<span class="integer">1</span>] ==>[<span class="key">marko</span>:<span class="integer">1</span>] ==>[<span class="key">peter</span>:<span class="integer">1</span>] ==>[<span class="key">ripple</span>:<span class="integer">1</span>] gremlin> g.V().both().barrier().local(groupCount().by(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)) ==>[<span class="key">lop</span>:<span class="integer">3</span>] ==>[<span class="key">vadas</span>:<span class="integer">1</span>] ==>[<span class="key">josh</span>:<span class="integer">3</span>] ==>[<span class="key">marko</span>:<span class="integer">3</span>] ==>[<span class="key">peter</span>:<span class="integer">1</span>] ==>[<span class="key">ripple</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().both().barrier().flatMap(groupCount().by(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)) g.V().both().barrier().local(groupCount().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>Use of <code>local()</code> is often a mistake. This is especially true when its argument contains a reducing step. For example, let’s say the requirement was to count the number of properties per <code>Vertex</code> in:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-149" type="radio" name="radio-set-1729796988-149" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-149" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-150" type="radio" name="radio-set-1729796988-149" class="tab-selector-2" /> <label for="tab-1729796988-150" 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> g.V().both().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>).count()) <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">3</span> ==><span class="integer">2</span> ==><span class="integer">6</span> ==><span class="integer">6</span> ==><span class="integer">2</span> ==><span class="integer">1</span> gremlin> g.V().both().map(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>).count()) <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</span> ==><span class="integer">2</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().both().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>).count()) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().both().map(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>).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 output here seems impossible because no single vertex in the "modern" graph can have more than two properties given the "name" and "age" filters, but because the counting is happening object-local the counting is occurring unique to each object rather than each global traverser.</p> </li> <li> <p>Replacing <code>local()</code> with <code>map()</code> returns the result desired by the requirement.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> The anonymous traversal of <code>local()</code> processes the current object "locally." In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversal does not leave the confines of the vertex’s star graph. In other words, it can not traverse to an adjacent vertex’s properties or edges. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#local(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>local(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="loops-step">Loops Step</h3> <div class="paragraph"> <p>The <code>loops()</code>-step (<strong>map</strong>) extracts the number of times the <code>Traverser</code> has gone through the current loop.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-151" type="radio" name="radio-set-1729796988-151" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-151" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-152" type="radio" name="radio-set-1729796988-151" class="tab-selector-2" /> <label for="tab-1729796988-152" 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> g.V().emit(__.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>).or().loops().is(<span class="integer">2</span>)).repeat(__.out()).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>) ==>marko ==>ripple ==>lop</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(__.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>).or().loops().is(<span class="integer">2</span>)).repeat(__.out()).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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#loops()"><code>loops()</code></a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/#looping"><code>Looping Recipes</code></a></p> </div> </div> <div class="sect2"> <h3 id="lTrim-step">LTrim Step</h3> <div class="paragraph"> <p>The <code>lTrim()</code>-step (<strong>map</strong>) returns a string with leading whitespace removed. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-153" type="radio" name="radio-set-1729796988-153" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-153" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-154" type="radio" name="radio-set-1729796988-153" class="tab-selector-2" /> <label for="tab-1729796988-154" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).lTrim() ==>hello ==>world ==><span class="predefined-constant">null</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).lTrim(local) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[hello ,world ,<span class="predefined-constant">null</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.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).lTrim() g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).lTrim(local) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#lTrim()"><code>lTrim()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#lTrim(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>lTrim(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="map-step">Map Step</h3> <div class="paragraph"> <p>The <code>map()</code> step maps the traverser from the current object to the next step in the process. Please see the <a href="#general-steps">General Steps</a> section for more information.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#map(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>map(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="match-step">Match Step</h3> <div class="paragraph"> <p>The <code>match()</code>-step (<strong>map</strong>) provides a more <a href="http://en.wikipedia.org/wiki/Declarative_programming">declarative</a> form of graph querying based on the notion of <a href="http://en.wikipedia.org/wiki/Pattern_matching">pattern matching</a>. With <code>match()</code>, the user provides a collection of "traversal fragments," called patterns, that have variables defined that must hold true throughout the duration of the <code>match()</code>. When a traverser is in <code>match()</code>, a registered <code>MatchAlgorithm</code> analyzes the current state of the traverser (i.e. its history based on its <a href="#path-data-structure">path data</a>), the runtime statistics of the traversal patterns, and returns a traversal-pattern that the traverser should try next. The default <code>MatchAlgorithm</code> provided is called <code>CountMatchAlgorithm</code> and it dynamically revises the pattern execution plan by sorting the patterns according to their filtering capabilities (i.e. largest set reduction patterns execute first). For very large graphs, where the developer is uncertain of the statistics of the graph (e.g. how many <code>knows</code>-edges vs. <code>worksFor</code>-edges exist in the graph), it is advantageous to use <code>match()</code>, as an optimal plan will be determined automatically. Furthermore, some queries are much easier to express via <code>match()</code> than with single-path traversals.</p> </div> <div class="literalblock"> <div class="content"> <pre>"Who created a project named 'lop' that was also created by someone who is 29 years old? Return the two creators."</pre> </div> </div> <div class="imageblock"> <div class="content"> <img src="../images/match-step.png" alt="match step" width="500"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-155" type="radio" name="radio-set-1729796988-155" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-155" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-156" type="radio" name="radio-set-1729796988-155" class="tab-selector-2" /> <label for="tab-1729796988-156" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).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">lop</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in(<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">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>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</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">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>) ==>[<span class="key">a</span>:marko,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:josh,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:peter,<span class="key">c</span>:marko]</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">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).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">lop</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in(<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">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>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</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">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>Note that the above can also be more concisely written as below which demonstrates that standard inner-traversals can be arbitrarily defined.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-157" type="radio" name="radio-set-1729796988-157" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-157" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-158" type="radio" name="radio-set-1729796988-157" class="tab-selector-2" /> <label for="tab-1729796988-158" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<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>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</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">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>) ==>[<span class="key">a</span>:marko,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:josh,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:peter,<span class="key">c</span>:marko]</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">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<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>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">c</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">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>In order to improve readability, <code>as()</code>-steps can be given meaningful labels which better reflect your domain. The previous query can thus be written in a more expressive way as shown below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-159" type="radio" name="radio-set-1729796988-159" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-159" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-160" type="radio" name="radio-set-1729796988-159" class="tab-selector-2" /> <label for="tab-1729796988-160" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">creators</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">projects</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">projects</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">cocreators</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">creators</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cocreators</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="key">creators</span>:marko,<span class="key">cocreators</span>:marko] ==>[<span class="key">creators</span>:josh,<span class="key">cocreators</span>:marko] ==>[<span class="key">creators</span>:peter,<span class="key">cocreators</span>:marko]</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">'</span><span class="content">creators</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">projects</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">projects</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">cocreators</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">creators</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">cocreators</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find vertices that created something and match them as 'creators', then find out what they created which is named 'lop' and match these vertices as 'projects'.</p> </li> <li> <p>Using these 'projects' vertices, find out their creators aged 29 and remember these as 'cocreators'.</p> </li> <li> <p>Return the name of both 'creators' and 'cocreators'.</p> </li> </ol> </div> <div id="grateful-dead" class="imageblock"> <div class="content"> <img src="../images/grateful-dead-schema.png" alt="grateful dead schema" width="475"> </div> <div class="title">Figure 4. Grateful Dead</div> </div> <div class="paragraph"> <p><code>MatchStep</code> brings functionality similar to <a href="http://en.wikipedia.org/wiki/SPARQL">SPARQL</a> to Gremlin. Like SPARQL, MatchStep conjoins a set of patterns applied to a graph. For example, the following traversal finds exactly those songs which Jerry Garcia has both sung and written (using the Grateful Dead graph distributed in the <code>data/</code> directory):</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-161" type="radio" name="radio-set-1729796988-161" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-161" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-162" type="radio" name="radio-set-1729796988-161" class="tab-selector-2" /> <label for="tab-1729796988-162" 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> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</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">Garcia</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">writtenBy</span><span class="delimiter">'</span></span>).as(<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">a</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>CREAM PUFF WAR ==>CRYPTICAL ENVELOPMENT</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) 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().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</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">Garcia</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">writtenBy</span><span class="delimiter">'</span></span>).as(<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">a</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">b</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>Among the features which differentiate <code>match()</code> from SPARQL are:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-163" type="radio" name="radio-set-1729796988-163" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-163" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-164" type="radio" name="radio-set-1729796988-163" class="tab-selector-2" /> <label for="tab-1729796988-164" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<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>).repeat(out()).times(<span class="integer">2</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>vadas ==>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().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="integer">29</span>).as(<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>).repeat(out()).times(<span class="integer">2</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><strong>Patterns of arbitrary complexity</strong>: <code>match()</code> is not restricted to triple patterns or property paths.</p> </li> <li> <p><strong>Recursion support</strong>: <code>match()</code> supports the branch-based steps within a pattern, including <code>repeat()</code>.</p> </li> <li> <p><strong>Imperative/declarative hybrid</strong>: Before and after a <code>match()</code>, it is possible to leverage classic Gremlin traversals.</p> </li> </ol> </div> <div class="paragraph"> <p>To extend point #3, it is possible to support going from imperative, to declarative, to imperative, ad infinitum.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-165" type="radio" name="radio-set-1729796988-165" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-165" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-166" type="radio" name="radio-set-1729796988-165" class="tab-selector-2" /> <label for="tab-1729796988-166" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<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">y</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">y</span><span class="delimiter">'</span></span>).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">z</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">z</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>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().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).in(<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">y</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">y</span><span class="delimiter">'</span></span>).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">z</span><span class="delimiter">'</span></span>)). select(<span class="string"><span class="delimiter">'</span><span class="content">z</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="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>match()</code>-step is stateless. The variable bindings of the traversal patterns are stored in the path history of the traverser. As such, the variables used over all <code>match()</code>-steps within a traversal are globally unique. A benefit of this is that subsequent <code>where()</code>, <code>select()</code>, <code>match()</code>, etc. steps can leverage the same variables in their analysis. </td> </tr> </table> </div> <div class="paragraph"> <p>Like all other steps in Gremlin, <code>match()</code> is a function and thus, <code>match()</code> within <code>match()</code> is a natural consequence of Gremlin’s functional foundation (i.e. recursive matching).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-167" type="radio" name="radio-set-1729796988-167" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-167" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-168" type="radio" name="radio-set-1729796988-167" class="tab-selector-2" /> <label for="tab-1729796988-168" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">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>).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">ripple</span><span class="delimiter">'</span></span>)). select(<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>)). 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">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>) ==>[<span class="key">a</span>:marko,<span class="key">c</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.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">lop</span><span class="delimiter">'</span></span>), __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">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>).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">ripple</span><span class="delimiter">'</span></span>)). select(<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>)). 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">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>If a step-labeled traversal proceeds the <code>match()</code>-step and the traverser entering the <code>match()</code> is destined to bind to a particular variable, then the previous step should be labeled accordingly.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-169" type="radio" name="radio-set-1729796988-169" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-169" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-170" type="radio" name="radio-set-1729796988-169" class="tab-selector-2" /> <label for="tab-1729796988-170" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>), __.not(__.as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).in(<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">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>) ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh,<span class="key">c</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.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out(<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">c</span><span class="delimiter">'</span></span>), __.not(__.as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).in(<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">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>There are three types of <code>match()</code> traversal patterns.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>as('a')…​as('b')</code>: both the start and end of the traversal have a declared variable.</p> </li> <li> <p><code>as('a')…​</code>: only the start of the traversal has a declared variable.</p> </li> <li> <p><code>…​</code>: there are no declared variables.</p> </li> </ol> </div> <div class="paragraph"> <p>If a variable is at the start of a traversal pattern it <strong>must</strong> exist as a label in the path history of the traverser else the traverser can not go down that path. If a variable is at the end of a traversal pattern then if the variable exists in the path history of the traverser, the traverser’s current location <strong>must</strong> match (i.e. equal) its historic location at that same label. However, if the variable does not exist in the path history of the traverser, then the current location is labeled as the variable and thus, becomes a bound variable for subsequent traversal patterns. If a traversal pattern does not have an end label, then the traverser must simply "survive" the pattern (i.e. not be filtered) to continue to the next pattern. If a traversal pattern does not have a start label, then the traverser can go down that path at any point, but will only go down that pattern once as a traversal pattern is executed once and only once for the history of the traverser. Typically, traversal patterns that do not have a start and end label are used in conjunction with <code>and()</code>, <code>or()</code>, and <code>where()</code>. Once the traverser has "survived" all the patterns (or at least one for <code>or()</code>), <code>match()</code>-step analyzes the traverser’s path history and emits a <code>Map<String,Object></code> of the variable bindings to the next step in the traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-171" type="radio" name="radio-set-1729796988-171" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-171" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-172" type="radio" name="radio-set-1729796988-171" class="tab-selector-2" /> <label for="tab-1729796988-172" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> match( <span class="comment">//</span>// <b class="conum">(2)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().count().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(3)</b> __.not(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).in().as(<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> or( <span class="comment">//</span>// <b class="conum">(5)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).in().count().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).and().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).is(gt(<span class="integer">2</span>)))). <span class="comment">//</span>// <b class="conum">(6)</b> dedup(<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">c</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(7)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() <span class="comment">//</span>// <b class="conum">(8)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:lop,<span class="key">c</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">g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> match( <span class="comment">//</span>// <b class="conum">(2)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().count().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(3)</b> __.not(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).in().as(<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> or( <span class="comment">//</span>// <b class="conum">(5)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">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>).in().count().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).and().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>).is(gt(<span class="integer">2</span>)))). <span class="comment">//</span>// <b class="conum">(6)</b> dedup(<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">c</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(7)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() <span class="invisible">//</span><b class="conum">8</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A standard, step-labeled traversal can come prior to <code>match()</code>.</p> </li> <li> <p>If the traverser’s path prior to entering <code>match()</code> has requisite label values, then those historic values are bound.</p> </li> <li> <p>It is possible to use <a href="#a-note-on-barrier-steps">barrier steps</a> though they are computed locally to the pattern (as one would expect).</p> </li> <li> <p>It is possible to <code>not()</code> a pattern.</p> </li> <li> <p>It is possible to nest <code>and()</code>- and <code>or()</code>-steps for conjunction matching.</p> </li> <li> <p>Both infix and prefix conjunction notation is supported.</p> </li> <li> <p>It is possible to "distinct" the specified label combination.</p> </li> <li> <p>The bound values are of different types — vertex ("a"), vertex ("b"), long ("c").</p> </li> </ol> </div> <div class="sect3"> <h4 id="using-where-with-match">Using Where with Match</h4> <div class="paragraph"> <p>Match is typically used in conjunction with both <code>select()</code> (demonstrated previously) and <code>where()</code> (presented here). A <code>where()</code>-step allows the user to further constrain the result set provided by <code>match()</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-173" type="radio" name="radio-set-1729796988-173" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-173" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-174" type="radio" name="radio-set-1729796988-173" class="tab-selector-2" /> <label for="tab-1729796988-174" 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> g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).in(<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">c</span><span class="delimiter">'</span></span>)). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>, neq(<span class="string"><span class="delimiter">'</span><span class="content">c</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">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>) ==>[<span class="key">a</span>:marko,<span class="key">c</span>:josh] ==>[<span class="key">a</span>:marko,<span class="key">c</span>:peter] ==>[<span class="key">a</span>:josh,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:josh,<span class="key">c</span>:peter] ==>[<span class="key">a</span>:peter,<span class="key">c</span>:marko] ==>[<span class="key">a</span>:peter,<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().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).in(<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">c</span><span class="delimiter">'</span></span>)). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>, neq(<span class="string"><span class="delimiter">'</span><span class="content">c</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">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>where()</code>-step can take either a <code>P</code>-predicate (example above) or a <code>Traversal</code> (example below). Using <code>MatchPredicateStrategy</code>, <code>where()</code>-clauses are automatically folded into <code>match()</code> and thus, subject to the query optimizer within <code>match()</code>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-175" type="radio" name="radio-set-1729796988-175" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-175" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-176" type="radio" name="radio-set-1729796988-175" class="tab-selector-2" /> <label for="tab-1729796988-176" 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> traversal = g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(label,<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).in(<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">c</span><span class="delimiter">'</span></span>)). where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">c</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> 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">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>); <span class="predefined-constant">null</span> <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="predefined-constant">null</span> gremlin> traversal.toString() <span class="comment">//</span>// <b class="conum">(4)</b> ==>[GraphStep(vertex,<span class="type">[]</span>), MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([~label.eq(person)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), VertexStep(OUT,[created],vertex), MatchEndStep(b)], [MatchStartStep(b), VertexStep(IN,[created],vertex), MatchEndStep(c)]]), WhereTraversalStep([WhereStartStep(a), VertexStep(OUT,[knows],vertex), WhereEndStep(c)]), SelectStep(last,[a, c],[value(name)])] gremlin> traversal <span class="comment">// </span>// <b class="conum">(5)</b> <b class="conum">(6)</b> ==>[<span class="key">a</span>:marko,<span class="key">c</span>:josh] gremlin> traversal.toString() <span class="comment">//</span>// <b class="conum">(7)</b> ==>[TinkerGraphStep(vertex,[~label.eq(person)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), VertexStep(OUT,[created],vertex), MatchEndStep(b)], [MatchStartStep(b), VertexStep(IN,[created],vertex), MatchEndStep(c)], [MatchStartStep(a), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(OUT,[knows],vertex), WhereEndStep(c)]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectStep(last,[a, c],[value(name)])]</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">traversal = g.V().match( __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(label,<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(1)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">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>).in(<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">c</span><span class="delimiter">'</span></span>)). where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">c</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(2)</b> 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">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>); <span class="predefined-constant">null</span> <span class="comment">//</span>// <b class="conum">(3)</b> traversal.toString() <span class="comment">//</span>// <b class="conum">(4)</b> traversal <span class="comment">// </span>// <b class="conum">(5)</b> <b class="conum">(6)</b> <b class="conum">(5)</b> traversal.toString() <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Any <code>has()</code>-step traversal patterns that start with the match-key are pulled out of <code>match()</code> to enable the graph system to leverage the filter for index lookups.</p> </li> <li> <p>A <code>where()</code>-step with a traversal containing variable bindings declared in <code>match()</code>.</p> </li> <li> <p>A useful trick to ensure that the traversal is not iterated by Gremlin Console.</p> </li> <li> <p>The string representation of the traversal prior to its strategies being applied.</p> </li> <li> <p>The Gremlin Console will automatically iterate anything that is an iterator or is iterable.</p> </li> <li> <p>Both marko and josh are co-developers and marko knows josh.</p> </li> <li> <p>The string representation of the traversal after the strategies have been applied (and thus, <code>where()</code> is folded into <code>match()</code>)</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> A <code>where()</code>-step is a filter and thus, variables within a <code>where()</code> clause are not globally bound to the path of the traverser in <code>match()</code>. As such, <code>where()</code>-steps in <code>match()</code> are used for filtering, not binding. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#match(org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>match(Traversal…​)</code></a></p> </div> </div> </div> <div class="sect2"> <h3 id="math-step">Math Step</h3> <div class="paragraph"> <p>The <code>math()</code>-step (<strong>math</strong>) enables scientific calculator functionality within Gremlin. This step deviates from the common function composition and nesting formalisms to provide an easy to read string-based math processor. Variables within the equation map to scopes in Gremlin — e.g. path labels, side-effects, or incoming map keys. This step supports <code>by()</code>-modulation where the <code>by()</code>-modulators are applied in the order in which the variables are first referenced within the equation. Note that the reserved variable <code>_</code> refers to the current numeric traverser object incoming to the <code>math()</code>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-177" type="radio" name="radio-set-1729796988-177" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-177" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-178" type="radio" name="radio-set-1729796988-177" class="tab-selector-2" /> <label for="tab-1729796988-178" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>).math(<span class="string"><span class="delimiter">'</span><span class="content">a + b</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="float">56.0</span> ==><span class="float">61.0</span> gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). math(<span class="string"><span class="delimiter">'</span><span class="content">b + a</span><span class="delimiter">'</span></span>). by(both().count().math(<span class="string"><span class="delimiter">'</span><span class="content">_ + 100</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="float">132.0</span> ==><span class="float">133.0</span> ==><span class="float">135.0</span> ==><span class="float">138.0</span> gremlin> g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,<span class="integer">10</span>).V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).math(<span class="string"><span class="delimiter">'</span><span class="content">_ / x</span><span class="delimiter">'</span></span>) ==><span class="float">2.9</span> ==><span class="float">2.7</span> ==><span class="float">3.2</span> ==><span class="float">3.5</span> gremlin> g.withSack(<span class="integer">1</span>).V(<span class="integer">1</span>).repeat(sack(sum).by(constant(<span class="integer">1</span>))).times(<span class="integer">10</span>).emit().sack().math(<span class="string"><span class="delimiter">'</span><span class="content">sin _</span><span class="delimiter">'</span></span>) ==><span class="float">0.9092974268256817</span> ==><span class="float">0.1411200080598672</span> ==>-<span class="float">0.7568024953079282</span> ==>-<span class="float">0.9589242746631385</span> ==>-<span class="float">0.27941549819892586</span> ==><span class="float">0.6569865987187891</span> ==><span class="float">0.9893582466233818</span> ==><span class="float">0.4121184852417566</span> ==>-<span class="float">0.5440211108893698</span> ==>-<span class="float">0.9999902065507035</span> gremlin> g.V().math(<span class="string"><span class="delimiter">'</span><span class="content">_+1</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">(1)</b> ==><span class="float">30.0</span> ==><span class="float">28.0</span> ==><span class="float">33.0</span> ==><span class="float">36.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().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>).math(<span class="string"><span class="delimiter">'</span><span class="content">a + b</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<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">b</span><span class="delimiter">'</span></span>). math(<span class="string"><span class="delimiter">'</span><span class="content">b + a</span><span class="delimiter">'</span></span>). by(both().count().math(<span class="string"><span class="delimiter">'</span><span class="content">_ + 100</span><span class="delimiter">'</span></span>)). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,<span class="integer">10</span>).V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).math(<span class="string"><span class="delimiter">'</span><span class="content">_ / x</span><span class="delimiter">'</span></span>) g.withSack(<span class="integer">1</span>).V(<span class="integer">1</span>).repeat(sack(sum).by(constant(<span class="integer">1</span>))).times(<span class="integer">10</span>).emit().sack().math(<span class="string"><span class="delimiter">'</span><span class="content">sin _</span><span class="delimiter">'</span></span>) g.V().math(<span class="string"><span class="delimiter">'</span><span class="content">_+1</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="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>The operators supported by the calculator include: <code>*</code>, <code>+</code>, <code>/</code>, <code>^</code>, and <code>%</code>. Furthermore, the following built in functions are provided:</p> </div> <div class="ulist"> <ul> <li> <p><code>abs</code>: absolute value</p> </li> <li> <p><code>acos</code>: arc cosine</p> </li> <li> <p><code>asin</code>: arc sine</p> </li> <li> <p><code>atan</code>: arc tangent</p> </li> <li> <p><code>cbrt</code>: cubic root</p> </li> <li> <p><code>ceil</code>: nearest upper integer</p> </li> <li> <p><code>cos</code>: cosine</p> </li> <li> <p><code>cosh</code>: hyperbolic cosine</p> </li> <li> <p><code>exp</code>: euler’s number raised to the power (<code>e^x</code>)</p> </li> <li> <p><code>floor</code>: nearest lower integer</p> </li> <li> <p><code>log</code>: logarithmus naturalis (base e)</p> </li> <li> <p><code>log10</code>: logarithm (base 10)</p> </li> <li> <p><code>log2</code>: logarithm (base 2)</p> </li> <li> <p><code>sin</code>: sine</p> </li> <li> <p><code>sinh</code>: hyperbolic sine</p> </li> <li> <p><code>sqrt</code>: square root</p> </li> <li> <p><code>tan</code>: tangent</p> </li> <li> <p><code>tanh</code>: hyperbolic tangent</p> </li> <li> <p><code>signum</code>: signum function</p> </li> </ul> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#math(java.lang.String)"><code>math(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="max-step">Max Step</h3> <div class="paragraph"> <p>The <code>max()</code>-step (<strong>map</strong>) operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-179" type="radio" name="radio-set-1729796988-179" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-179" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-180" type="radio" name="radio-set-1729796988-179" class="tab-selector-2" /> <label for="tab-1729796988-180" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).max() ==><span class="integer">35</span> gremlin> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).max() ==><span class="integer">35</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).max() ==>vadas</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).max() g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).max() g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).max()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When called as <code>max(local)</code> it determines the maximum value of the current, local object (not the objects in the traversal stream). This works for <code>Collection</code> and <code>Comparable</code>-type objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-181" type="radio" name="radio-set-1729796988-181" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-181" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-182" type="radio" name="radio-set-1729796988-181" class="tab-selector-2" /> <label for="tab-1729796988-182" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().max(local) ==><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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().max(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When there are <code>null</code> values being evaluated the <code>null</code> objects are ignored, but if all values are recognized as <code>null</code> the return value is <code>null</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-183" type="radio" name="radio-set-1729796988-183" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-183" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-184" type="radio" name="radio-set-1729796988-183" class="tab-selector-2" /> <label for="tab-1729796988-184" 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> g.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).max() ==><span class="integer">10</span> gremlin> g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).max(local) ==><span class="predefined-constant">null</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.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).max() g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).max(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#max()"><code>max()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#max(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>max(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="mean-step">Mean Step</h3> <div class="paragraph"> <p>The <code>mean()</code>-step (<strong>map</strong>) operates on a stream of numbers and determines the average of those numbers.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-185" type="radio" name="radio-set-1729796988-185" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-185" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-186" type="radio" name="radio-set-1729796988-185" class="tab-selector-2" /> <label for="tab-1729796988-186" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).mean() ==><span class="float">30.75</span> gremlin> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).mean() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="float">30.645833333333332</span> gremlin> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).dedup().mean() ==><span class="float">30.75</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).mean() g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).mean() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).dedup().mean()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Realize that traversers are being bulked by <code>repeat()</code>. There may be more of a particular number than another, thus altering the average.</p> </li> </ol> </div> <div class="paragraph"> <p>When called as <code>mean(local)</code> it determines the mean of the current, local object (not the objects in the traversal stream). This works for <code>Collection</code> and <code>Number</code>-type objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-187" type="radio" name="radio-set-1729796988-187" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-187" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-188" type="radio" name="radio-set-1729796988-187" class="tab-selector-2" /> <label for="tab-1729796988-188" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().mean(local) ==><span class="float">30.75</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().mean(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>If <code>mean()</code> encounters <code>null</code> values, they will be ignored (i.e. their traversers not counted toward toward the divisor). If all traversers are <code>null</code> then the stream will return <code>null</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-189" type="radio" name="radio-set-1729796988-189" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-189" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-190" type="radio" name="radio-set-1729796988-189" class="tab-selector-2" /> <label for="tab-1729796988-190" 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> g.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).mean() ==><span class="float">9.5</span> gremlin> g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).mean(local) ==><span class="predefined-constant">null</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.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).mean() g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).mean(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mean()"><code>mean()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mean(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>mean(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="merge-step">Merge Step</h3> <div class="paragraph"> <p>The <code>merge()</code>-step (<strong>map</strong>) combines collections like lists and maps. It expects an incoming traverser to contain a collection objection and will combine that object with its specified argument which must be of a matching type. This is also known as the union operation. If the incoming traverser or its associated argument do not meet the expected type, the step will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>). This step differs from the <code>combine()</code>-step in that it doesn’t allow duplicates.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-191" type="radio" name="radio-set-1729796988-191" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-191" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-192" type="radio" name="radio-set-1729796988-191" class="tab-selector-2" /> <label for="tab-1729796988-192" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().merge([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</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="string"><span class="delimiter">"</span><span class="content">vadas</span><span class="delimiter">"</span></span>]) ==>[jen,ripple,peter,vadas,james,josh,lop,marko] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().merge(__.constant(<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>).fold()) ==>[ripple,peter,vadas,james,josh,lop,marko] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).elementMap().merge([<span class="key">year</span>:<span class="integer">2009</span>]) ==>[<span class="key">name</span>:lop,<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">lang</span>:java,<span class="key">year</span>:<span class="integer">2009</span>] ==>[<span class="key">name</span>:ripple,<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">lang</span>:java,<span class="key">year</span>:<span class="integer">2009</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">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().merge([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</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="string"><span class="delimiter">"</span><span class="content">vadas</span><span class="delimiter">"</span></span>]) g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().merge(__.constant(<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>).fold()) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).elementMap().merge([<span class="key">year</span>:<span class="integer">2009</span>])</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#merge(java.lang.Object)"><code>merge(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#merge-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="mergeedge-step">MergeEdge Step</h3> <div class="paragraph"> <p>The <code>mergeE()</code> step is used to add edges and their properties to a graph in a "create if not exist" fashion. The <code>mergeE()</code> step can also be used to find edges matching a given pattern. The input passed to <code>mergeE()</code> can be either a <code>Map</code>, or a child traversal that produces a <code>Map</code>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> There is a corresponding <code><a href="#mergevertex-step">mergeV()</a></code> step that can be used when creating vertices. </td> </tr> </table> </div> <div class="paragraph"> <p>Additionally, <code>option()</code> modulators may be combined with <code>mergeE()</code> to take action depending on whether a vertex was created, or already existed. There are various ways that <code>mergeE()</code> can be used. The simplest being to provide a single <code>Map</code> of keys and values, along with the source and target vertex IDs, as a parameter. A <code>T.id</code> and a <code>T.label</code> may also be provided but this is optional. The <code>mergeE()</code> step can be used directly from the <code>GraphTraversalSource</code> - <code>g</code>, or in the middle of a traversal. For a match with an existing vertex to occur, all values in the <code>Map</code> must exist on a vertex; otherwise, a new vertex will be created. The examples that follow show how <code>mergeE()</code> can be used to add relationships between dogs in the graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-193" type="radio" name="radio-set-1729796988-193" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-193" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-194" type="radio" name="radio-set-1729796988-193" class="tab-selector-2" /> <label for="tab-1729796988-194" 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> g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">1</span>] gremlin> g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">2</span>] gremlin> g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(Direction.from):<span class="integer">1</span>,(Direction.to):<span class="integer">2</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">2</span>][<span class="integer">1</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</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.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(Direction.from):<span class="integer">1</span>,(Direction.to):<span class="integer">2</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create two vertices with ID values of 1 and 2.</p> </li> <li> <p>Create a "Sibling" relationship between the vertices.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The example above is written with <code>gremlin-groovy</code> and evaluated in Gremlin Console as a Groovy script thus allowing <a href="https://groovy-lang.org/syntax.html#_maps">Groovy syntax</a> for initializing a <code>Map</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>For a <code>mergeE()</code> step to succeed, both the <code>from</code> and <code>to</code> vertices must already exist. It is not possible to create new vertices directly using <code>mergeE()</code>, but <code>mergeV()</code> and <code>mergeE()</code> steps can be combined, in a single query, to achieve that goal.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>mergeE()</code> step will not create vertices that do not exist. In those cases an error will be returned. </td> </tr> </table> </div> <div class="paragraph"> <p>If the <code>Direction</code> enum has been statically included, its explicit use can be omitted from the query.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-195" type="radio" name="radio-set-1729796988-195" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-195" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-196" type="radio" name="radio-set-1729796988-195" class="tab-selector-2" /> <label for="tab-1729796988-196" 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> g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">1</span>] gremlin> g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">2</span>] gremlin> g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]) ==>e[<span class="integer">2</span>][<span class="integer">1</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</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.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]) g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>One or more <code>option()</code> steps can be used to control the behavior when an edge is created or updated. Similar to <code>mergeV()</code>, the onCreate <code>Map</code> inherits from the main merge argument - any existence criteria in the main merge argument (<code>T.id</code>, <code>T.label</code>, <code>Direction.OUT</code>, <code>Direction.IN</code>) will be automatically carried over to the onCreate action, and these existence criteria cannot be overriden in the onCreate <code>Map</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-197" type="radio" name="radio-set-1729796988-197" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-197" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-198" type="radio" name="radio-set-1729796988-197" class="tab-selector-2" /> <label for="tab-1729796988-198" 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> g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">1</span>] gremlin> g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">2</span>] gremlin> g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]). mergeE(select(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>)). option(Merge.onCreate,[<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onMatch,[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]) ==>e[<span class="integer">2</span>][<span class="integer">1</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</span>] gremlin> g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]). mergeE(select(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>)). option(Merge.onCreate,[<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]). option(Merge.onMatch,[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">2</span>][<span class="integer">1</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</span>,<span class="key">updated</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</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.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]). mergeE(select(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>)). option(Merge.onCreate,[<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onMatch,[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]) g.E().elementMap() g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>]). mergeE(select(<span class="string"><span class="delimiter">'</span><span class="content">map</span><span class="delimiter">'</span></span>)). option(Merge.onCreate,[<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]). option(Merge.onMatch,[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(2)</b> g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The edge did not exist - set the created date.</p> </li> <li> <p>The edge did exist - set the updated date.</p> </li> </ol> </div> <div class="paragraph"> <p>More than one edge can be created by a single <code>mergeE()</code> operation. This is done by injecting a list of maps into the traversal and letting them stream into the <code>mergeE()</code> step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-199" type="radio" name="radio-set-1729796988-199" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-199" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-200" type="radio" name="radio-set-1729796988-199" class="tab-selector-2" /> <label for="tab-1729796988-200" 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> maps = [[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Siblings</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>], [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Siblings</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">3</span>]] ==>[<span class="key">label</span>:Siblings,<span class="key">OUT</span>:<span class="integer">1</span>,<span class="key">IN</span>:<span class="integer">2</span>] ==>[<span class="key">label</span>:Siblings,<span class="key">OUT</span>:<span class="integer">1</span>,<span class="key">IN</span>:<span class="integer">3</span>] gremlin> g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] gremlin> g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">2</span>] gremlin> g.mergeV([(T.id):<span class="integer">3</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Dax</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">3</span>] gremlin> g.inject(maps).unfold().mergeE() <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">3</span>][<span class="integer">1</span>-Siblings-><span class="integer">2</span>] ==>e[<span class="integer">4</span>][<span class="integer">1</span>-Siblings-><span class="integer">3</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:Siblings,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog]] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:Siblings,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog]]</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">maps = [[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Siblings</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">2</span>], [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Siblings</span><span class="delimiter">'</span></span>,(from):<span class="integer">1</span>,(to):<span class="integer">3</span>]] g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> g.mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) g.mergeV([(T.id):<span class="integer">3</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Dax</span><span class="delimiter">'</span></span>]) g.inject(maps).unfold().mergeE() <span class="comment">//</span>// <b class="conum">(2)</b> g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create three dogs.</p> </li> <li> <p>Stream the edge maps into <code>mergeE()</code> steps.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> There is a bit of an inconsistency present when <code>mergeE()</code> is used as a start step versus when it is used mid-traversal. As a start step, <code>mergeE()</code> will promote the currently created or matched <code>Edge</code> to the child traversal, allowing you to directly update it like <code>option(onMatch, property('k', 'v').constant([:]))</code>. However, when <code>mergeE()</code> is used mid-traversal, the <code>Edge</code> is not promoted to the child traversal and the incoming traverser is used instead. Such behavior is essentially blocked to prevent accidental misuse and will result in an exception at execution time that will have a message like, "The incoming traverser for MergeEdgeStep cannot be an Element". </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>mergeE</code> step can be combined with the <code>mergeV</code> step (or any other step producing a <code>Vertex</code>) using the <code>Merge.outV</code> and <code>Merge.inV</code> option modulators. These options can be used to "late-bind" the <code>OUT</code> and <code>IN</code> vertices in the main merge argument and in the <code>onCreate</code> argument:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-201" type="radio" name="radio-set-1729796988-201" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-201" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-202" type="radio" name="radio-set-1729796988-201" class="tab-selector-2" /> <label for="tab-1729796988-202" 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> g.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]).as(<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>). mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]).as(<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>). mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):Merge.outV,(to):Merge.inV]). option(Merge.outV, select(<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>)). option(Merge.inV, select(<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>)) ==>e[<span class="integer">2</span>][<span class="integer">1</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</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.mergeV([(T.id):<span class="integer">1</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]).as(<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>). mergeV([(T.id):<span class="integer">2</span>,(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]).as(<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>). mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):Merge.outV,(to):Merge.inV]). option(Merge.outV, select(<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>)). option(Merge.inV, select(<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>)) g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>Merge.outV</code> and <code>Merge.inV</code> tokens can be used as placeholders for values for <code>Direction.OUT</code> and <code>Direction.IN</code> respectively in the <code>mergeE</code> arguments. These options can produce <code>Vertices</code>, as in the example above, or they can specify <code>Maps</code>, which will be used to search for <code>Vertices</code> in the graph. This is useful when the exact <code>T.id</code> of the from/to vertices is not known in advance:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-203" type="radio" name="radio-set-1729796988-203" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-203" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-204" type="radio" name="radio-set-1729796988-203" class="tab-selector-2" /> <label for="tab-1729796988-204" 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> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">0</span>] gremlin> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) ==>v[<span class="integer">2</span>] gremlin> g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):Merge.outV,(to):Merge.inV]). option(Merge.outV, [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]). option(Merge.inV, [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) ==>e[<span class="integer">4</span>][<span class="integer">0</span>-Sibling-><span class="integer">2</span>] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:Sibling,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:Dog],<span class="key">created</span>:<span class="integer">2022</span>-<span class="octal">02</span>-<span class="octal">07</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.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]) g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) g.mergeE([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Sibling</span><span class="delimiter">'</span></span>,<span class="key">created</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-07</span><span class="delimiter">'</span></span>,(from):Merge.outV,(to):Merge.inV]). option(Merge.outV, [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>]). option(Merge.inV, [(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) g.E().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeE()"><code>mergeE()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeE(java.util.Map)"><code>mergeE(Map)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeE(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>mergeE(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Merge.html"><code>Merge</code></a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_mergee">Semantics</a></p> </div> </div> <div class="sect2"> <h3 id="mergevertex-step">MergeVertex Step</h3> <div class="paragraph"> <p>The <code>mergeV()</code> -step is used to add vertices and their properties to a graph in a "create if not exist" fashion. The <code>mergeV()</code> step can also be used to find vertices matching a given pattern. The input passed to <code>mergeV()</code> can be either a <code>Map</code>, or a child <code>Traversal</code> that produces a <code>Map</code>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> There is a corresponding <a href="#mergeedge-step"><code>mergeE()</code></a> step that can be used when creating edges. </td> </tr> </table> </div> <div class="paragraph"> <p>Additionally, <code>option()</code> modulators may be combined with <code>mergeV()</code> to take action depending on whether a vertex was created, or already existed. There are various ways <code>mergeV()</code> can be used. The simplest being to provide a single <code>Map</code> of keys and values as a parameter. A <code>T.id</code> and a <code>T.label</code> may also be provided but this is optional. The <code>mergeV()</code> step can be used directly from the <code>GraphTraversalSource</code> - <code>g</code>, or in the middle of a traversal. For a match with an existing vertex to occur, all values in the <code>Map</code> must exist on a vertex; otherwise, a new vertex will be created. The examples that follow show how <code>mergeV()</code> can be used to add some dogs to the graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-205" type="radio" name="radio-set-1729796988-205" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-205" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-206" type="radio" name="radio-set-1729796988-205" class="tab-selector-2" /> <label for="tab-1729796988-206" 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> g.mergeV([<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">0</span>] gremlin> 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">Brandy</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">0</span>] gremlin> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Scamp</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">12</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">2</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">name</span>:[Scamp],<span class="key">age</span>:[<span class="integer">12</span>]] gremlin> g.mergeV([(T.id):<span class="integer">300</span>, (T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">300</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Scamp],<span class="key">age</span>:[<span class="integer">12</span>]] ==>[<span class="key">id</span>:<span class="integer">300</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Toby],<span class="key">age</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.mergeV([<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</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">Brandy</span><span class="delimiter">'</span></span>) g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Scamp</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">12</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap() g.mergeV([(T.id):<span class="integer">300</span>, (T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create a vertex for Brandy as no other matching ones exist yet.</p> </li> <li> <p>Create a vertex for Scamp and also add a Dog label his age.</p> </li> <li> <p>Create a vertex for Toby with an <code>T.id</code> of 300.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The example above is written with <code>gremlin-groovy</code> and evaluated in Gremlin Console as a Groovy script thus allowing <a href="https://groovy-lang.org/syntax.html#_maps">Groovy syntax</a> for initializing a <code>Map</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>If a vertex already exists that matches the map passed to <code>mergeV()</code>, the existing vertex will be returned, otherwise a new one will be created. In this way, <code>mergeV()</code> provides "get or create" semantics.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-207" type="radio" name="radio-set-1729796988-207" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-207" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-208" type="radio" name="radio-set-1729796988-207" class="tab-selector-2" /> <label for="tab-1729796988-208" 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> g.mergeV([<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>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.mergeV([<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>]) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A vertex for Brandy already exists so return it. A new one is not created.</p> </li> </ol> </div> <div class="paragraph"> <p>It’s important to note that every key/value pair passed to <code>mergeV()</code> must already exist on one or more vertices for there to be a match. If a match is found, the vertex, or vertices, representing that match will be returned. If a vertex representing a dog called Brandy already exists, but it does not have an "age" property, the <code>mergeV()</code> below will not find a match and a new vertex will be created.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-209" type="radio" name="radio-set-1729796988-209" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-209" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-210" type="radio" name="radio-set-1729796988-209" class="tab-selector-2" /> <label for="tab-1729796988-210" 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> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">Dog</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">Brandy</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">0</span>] gremlin> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">13</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<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.addV(<span class="string"><span class="delimiter">'</span><span class="content">Dog</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">Brandy</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">13</span>]) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create a vertex for Brandy with no age property.</p> </li> <li> <p>A new vertex is created as there is no exact match to any existing vertices.</p> </li> </ol> </div> <div class="paragraph"> <p>A common scenario is to search for a vertex with a known <code>T.id</code> and if it exists return that vertex. If it does not exist, create it. As we have seen, one way to do this is to pass the <code>T.id</code> and all properties directly to <code>mergeV()</code>. Another is to use <code>Merge.onCreate</code>. Note that the <code>Map</code> specified for <code>Match.onCreate</code> does not need to include the <code>T.id</code> already present in the original search. The values provided to the <code>mergeV()</code> <code>Map</code> are inherited by the onCreate action and combined with the <code>Map</code> provided to <code>Merge.onCreate</code>. Overrides of the <code>T.id</code> or <code>T.label</code> in the onCreate <code>Map</code> are prohibited.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-211" type="radio" name="radio-set-1729796988-211" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-211" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-212" type="radio" name="radio-set-1729796988-211" class="tab-selector-2" /> <label for="tab-1729796988-212" 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> g.mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]) ==>v[<span class="integer">300</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.mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>])</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>To take specific action when the vertex already exists, <code>Merge.onMatch</code> can be used. The second parameter to the <code>option</code> step can be either a <code>Map</code> whose values are used to update the vertex or another Gremlin traversal that generates a <code>Map</code>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> If <code>mergeV()</code> is given an empty <code>Map</code>; such as <code>mergeV([:])</code>, it will match, and return, every vertex in the graph. This is the same behavior seen with <code>V([])</code>. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-213" type="radio" name="radio-set-1729796988-213" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-213" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-214" type="radio" name="radio-set-1729796988-213" class="tab-selector-2" /> <label for="tab-1729796988-214" 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> g.mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onMatch,[<span class="key">age</span>:<span class="integer">11</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">300</span>] gremlin> g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">new-data</span><span class="delimiter">'</span></span>,[<span class="key">age</span>:<span class="integer">11</span>]). mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]). option(Merge.onMatch,select(<span class="string"><span class="delimiter">'</span><span class="content">new-data</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">300</span>] gremlin> g.V(<span class="integer">300</span>).valueMap().with(WithOptions.tokens) ==>[<span class="key">id</span>:<span class="integer">300</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Toby],<span class="key">age</span>:[<span class="integer">11</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.mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onMatch,[<span class="key">age</span>:<span class="integer">11</span>]) <span class="comment">//</span>// <b class="conum">(2)</b> g.withSideEffect(<span class="string"><span class="delimiter">'</span><span class="content">new-data</span><span class="delimiter">'</span></span>,[<span class="key">age</span>:<span class="integer">11</span>]). mergeV([(T.id):<span class="integer">300</span>]). option(Merge.onCreate,[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span>, <span class="key">age</span>:<span class="integer">10</span>]). option(Merge.onMatch,select(<span class="string"><span class="delimiter">'</span><span class="content">new-data</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">300</span>).valueMap().with(WithOptions.tokens)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>If no match found create the vertex using these values.</p> </li> <li> <p>If a match is found, change the age property value.</p> </li> <li> <p>Change the age property by selecting from the <code>new-data</code> map.</p> </li> </ol> </div> <div class="paragraph"> <p>It is sometimes helpful to incorporate <code>fail()</code> step into scenarios where there is a need to stop the traversal for one event or the other:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> g.mergeV([(T.id): <span class="integer">1</span>]). ......<span class="integer">1</span>> option(onCreate, fail(<span class="string"><span class="delimiter">"</span><span class="content">vertex did not exist</span><span class="delimiter">"</span></span>)). ......<span class="integer">2</span>> option(onMatch, [<span class="key">modified</span>: <span class="integer">2022</span>]) fail() Step Triggered ====================================================================================================================================================================== Message > vertex did not exist Traverser> <span class="predefined-constant">false</span> Bulk > <span class="integer">1</span> Traversal> fail(<span class="string"><span class="delimiter">"</span><span class="content">vertex did not exist</span><span class="delimiter">"</span></span>) Parent > TinkerMergeVertexStep [mergeV([(T.id):((<span class="type">int</span>) <span class="integer">1</span>)]).option(Merge.onCreate,__.fail(<span class="string"><span class="delimiter">"</span><span class="content">vertex did not exist</span><span class="delimiter">"</span></span>)).option(Merge.onMatch,[(<span class="string"><span class="delimiter">"</span><span class="content">modified</span><span class="delimiter">"</span></span>):((<span class="type">int</span>) <span class="integer">2022</span>)])] Metadata > {} ======================================================================================================================================================================</code></pre> </div> </div> <div class="paragraph"> <p>When working with multi-properties, there are two ways to specify them for <code>mergeV()</code>. First, you can specify them individually using a <code>CardinalityValue</code> as the value in the <code>Map</code>. The <code>CardinalityValue</code> allows you to specify the value as well as the <code>Cardinality</code> for that value. Note that it is only possible to specify one value with this syntax even if you are using <code>set</code> or <code>list</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-215" type="radio" name="radio-set-1729796988-215" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-215" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-216" type="radio" name="radio-set-1729796988-215" class="tab-selector-2" /> <label for="tab-1729796988-216" 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> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(onCreate, [<span class="key">alias</span>: set(<span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>)]). <span class="comment">//</span>// <b class="conum">(2)</b> property(set,<span class="string"><span class="delimiter">'</span><span class="content">alias</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">Maxamillion</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">0</span>] gremlin> 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">Max</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) ==>[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Max],<span class="key">alias</span>:[Maximus,Maxamillion]]</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.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). <span class="comment">//</span>// <b class="conum">(1)</b> option(onCreate, [<span class="key">alias</span>: set(<span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>)]). <span class="comment">//</span>// <b class="conum">(2)</b> property(set,<span class="string"><span class="delimiter">'</span><span class="content">alias</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">Maxamillion</span><span class="delimiter">'</span></span>) <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">Max</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find or create a vertex for Max.</p> </li> <li> <p>If Max is not found then add an alias of <code>set</code> cardinality.</p> </li> <li> <p>Whether Max was found or created, add another alias with <code>set</code> cardinality.</p> </li> </ol> </div> <div class="paragraph"> <p>The second option is to specify <code>Cardinality</code> for the entire range of values as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-217" type="radio" name="radio-set-1729796988-217" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-217" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-218" type="radio" name="radio-set-1729796988-217" class="tab-selector-2" /> <label for="tab-1729796988-218" 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> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). option(onCreate, [<span class="key">alias</span>: <span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>, <span class="key">city</span>: <span class="string"><span class="delimiter">'</span><span class="content">Boston</span><span class="delimiter">'</span></span>], set) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">0</span>] gremlin> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). option(onCreate, [<span class="key">alias</span>: <span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>, <span class="key">city</span>: single(<span class="string"><span class="delimiter">'</span><span class="content">Boston</span><span class="delimiter">'</span></span>)], set) <span class="comment">//</span>// <b class="conum">(2)</b> ==>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.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). option(onCreate, [<span class="key">alias</span>: <span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>, <span class="key">city</span>: <span class="string"><span class="delimiter">'</span><span class="content">Boston</span><span class="delimiter">'</span></span>], set) <span class="comment">//</span>// <b class="conum">(1)</b> g.mergeV([(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Max</span><span class="delimiter">'</span></span>]). option(onCreate, [<span class="key">alias</span>: <span class="string"><span class="delimiter">'</span><span class="content">Maximus</span><span class="delimiter">'</span></span>, <span class="key">city</span>: single(<span class="string"><span class="delimiter">'</span><span class="content">Boston</span><span class="delimiter">'</span></span>)], set) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>If Max is created then set the alias and city with cardinality of <code>set</code>.</p> </li> <li> <p>If Max is created then set the alias with cardinality of <code>set</code> and city with cardinality <code>single</code>.</p> </li> </ol> </div> <div class="paragraph"> <p>More than one vertex can be created by a single <code>mergeV()</code> operation. This is done by injecting a <code>List</code> of <code>Map</code> objects into the traversal and letting them stream into the <code>mergeV()</code> step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-219" type="radio" name="radio-set-1729796988-219" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-219" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-220" type="radio" name="radio-set-1729796988-219" class="tab-selector-2" /> <label for="tab-1729796988-220" 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> maps = [[(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Scamp</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">King Charles Spaniel</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Shadow</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Rocket</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Dax</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Baxter</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Zoe</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Corgi</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>]] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Toby,<span class="key">breed</span>:Golden Retriever] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Brandy,<span class="key">breed</span>:Golden Retriever] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Scamp,<span class="key">breed</span>:King Charles Spaniel] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Shadow,<span class="key">breed</span>:Mixed] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Rocket,<span class="key">breed</span>:Golden Retriever] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Dax,<span class="key">breed</span>:Mixed] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Baxter,<span class="key">breed</span>:Mixed] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Zoe,<span class="key">breed</span>:Corgi] ==>[<span class="key">label</span>:Dog,<span class="key">name</span>:Pixel,<span class="key">breed</span>:Mixed] gremlin> g.inject(maps).unfold().mergeV() ==>v[<span class="integer">0</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">9</span>] ==>v[<span class="integer">12</span>] ==>v[<span class="integer">15</span>] ==>v[<span class="integer">18</span>] ==>v[<span class="integer">21</span>] ==>v[<span class="integer">24</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) ==>[<span class="key">id</span>:<span class="integer">0</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Toby],<span class="key">breed</span>:[Golden Retriever]] ==>[<span class="key">id</span>:<span class="integer">18</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Baxter],<span class="key">breed</span>:[Mixed]] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Brandy],<span class="key">breed</span>:[Golden Retriever]] ==>[<span class="key">id</span>:<span class="integer">21</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Zoe],<span class="key">breed</span>:[Corgi]] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Scamp],<span class="key">breed</span>:[King Charles Spaniel]] ==>[<span class="key">id</span>:<span class="integer">24</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Pixel],<span class="key">breed</span>:[Mixed]] ==>[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Shadow],<span class="key">breed</span>:[Mixed]] ==>[<span class="key">id</span>:<span class="integer">12</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Rocket],<span class="key">breed</span>:[Golden Retriever]] ==>[<span class="key">id</span>:<span class="integer">15</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Dax],<span class="key">breed</span>:[Mixed]]</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">maps = [[(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Toby</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Brandy</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Scamp</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">King Charles Spaniel</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Shadow</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Rocket</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Golden Retriever</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Dax</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Baxter</span><span class="delimiter">'</span></span>, <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Zoe</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Corgi</span><span class="delimiter">'</span></span>], [(T.label) : <span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span> , <span class="key">breed</span>: <span class="string"><span class="delimiter">'</span><span class="content">Mixed</span><span class="delimiter">'</span></span>]] g.inject(maps).unfold().mergeV() g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Another useful pattern that can be used with <code>mergeV()</code> involves putting multiple maps in a list and selecting different maps based on the action being taken. The examples below use a list containing three maps. The first containing just the ID to be searched for. The second map contains all the information to use when the vertex is created. The third map contains additional information that will be applied if an existing vertex is found.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-221" type="radio" name="radio-set-1729796988-221" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-221" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-222" type="radio" name="radio-set-1729796988-221" class="tab-selector-2" /> <label for="tab-1729796988-222" 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> g.inject([[(T.id):<span class="integer">400</span>],[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">1</span>],[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-1</span><span class="delimiter">'</span></span>]]). mergeV(limit(local,<span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onCreate,range(local,<span class="integer">1</span>,<span class="integer">2</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> option(Merge.onMatch,tail(local)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">400</span>] gremlin> g.V(<span class="integer">400</span>).valueMap().with(WithOptions.tokens) ==>[<span class="key">id</span>:<span class="integer">400</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Pixel],<span class="key">age</span>:[<span class="integer">1</span>]] gremlin> g.inject([[(T.id):<span class="integer">400</span>],[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">1</span>],[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-1</span><span class="delimiter">'</span></span>]]). mergeV(limit(local,<span class="integer">1</span>)). option(Merge.onCreate,range(local,<span class="integer">1</span>,<span class="integer">2</span>)). option(Merge.onMatch,tail(local)) <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">400</span>] gremlin> g.V(<span class="integer">400</span>).valueMap().with(WithOptions.tokens) <span class="comment">//</span>// <b class="conum">(5)</b> ==>[<span class="key">id</span>:<span class="integer">400</span>,<span class="key">label</span>:Dog,<span class="key">name</span>:[Pixel],<span class="key">updated</span>:[<span class="integer">2022</span>-<span class="octal">02</span>-<span class="integer">1</span>],<span class="key">age</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.inject([[(T.id):<span class="integer">400</span>],[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">1</span>],[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-1</span><span class="delimiter">'</span></span>]]). mergeV(limit(local,<span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(1)</b> option(Merge.onCreate,range(local,<span class="integer">1</span>,<span class="integer">2</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> option(Merge.onMatch,tail(local)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">400</span>).valueMap().with(WithOptions.tokens) g.inject([[(T.id):<span class="integer">400</span>],[(T.label):<span class="string"><span class="delimiter">'</span><span class="content">Dog</span><span class="delimiter">'</span></span>,<span class="key">name</span>:<span class="string"><span class="delimiter">'</span><span class="content">Pixel</span><span class="delimiter">'</span></span>,<span class="key">age</span>:<span class="integer">1</span>],[<span class="key">updated</span>:<span class="string"><span class="delimiter">'</span><span class="content">2022-02-1</span><span class="delimiter">'</span></span>]]). mergeV(limit(local,<span class="integer">1</span>)). option(Merge.onCreate,range(local,<span class="integer">1</span>,<span class="integer">2</span>)). option(Merge.onMatch,tail(local)) <span class="comment">//</span>// <b class="conum">(4)</b> g.V(<span class="integer">400</span>).valueMap().with(WithOptions.tokens) <span class="invisible">//</span><b class="conum">5</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Use the first map to search for a vertex with an ID of 400.</p> </li> <li> <p>If the vertex was not found, use the second map to create it.</p> </li> <li> <p>If the vertex was found, add an <code>updated</code> property.</p> </li> <li> <p>Pixel exists now, so we will take this option.</p> </li> <li> <p>The <code>updated</code> property has now been added.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> There is a bit of an inconsistency present when <code>mergeV()</code> is used as a start step versus when it is used mid-traversal. As a start step, <code>mergeV()</code> will promote the currently created or matched <code>Vertex</code> to the child traversal, allowing you to directly update it like <code>option(onMatch, property('k', 'v').constant([:]))</code>. However, when <code>mergeV()</code> is used mid-traversal, the <code>Vertex</code> is not promoted to the child traversal and the incoming traverser is used instead. Such behavior is essentially blocked to prevent accidental misuse and will result in an exception at execution time that will have a message like, "The incoming traverser for MergeVertexStep cannot be an Element". </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeV()"><code>mergeV()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeV(java.util.Map)"><code>mergeV(Map)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#mergeV(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>mergeV(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Merge.html"><code>Merge</code></a>, <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_mergee">Semantics</a></p> </div> </div> <div class="sect2"> <h3 id="min-step">Min Step</h3> <div class="paragraph"> <p>The <code>min()</code>-step (<strong>map</strong>) operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-223" type="radio" name="radio-set-1729796988-223" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-223" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-224" type="radio" name="radio-set-1729796988-223" class="tab-selector-2" /> <label for="tab-1729796988-224" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).min() ==><span class="integer">27</span> gremlin> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).min() ==><span class="integer">27</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).min() ==>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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).min() g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).min() g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).min()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When called as <code>min(local)</code> it determines the minimum value of the current, local object (not the objects in the traversal stream). This works for <code>Collection</code> and <code>Comparable</code>-type objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-225" type="radio" name="radio-set-1729796988-225" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-225" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-226" type="radio" name="radio-set-1729796988-225" class="tab-selector-2" /> <label for="tab-1729796988-226" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().min(local) ==><span class="integer">27</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().min(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When there are <code>null</code> values being evaluated the <code>null</code> objects are ignored, but if all values are recognized as <code>null</code> the return value is <code>null</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-227" type="radio" name="radio-set-1729796988-227" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-227" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-228" type="radio" name="radio-set-1729796988-227" class="tab-selector-2" /> <label for="tab-1729796988-228" 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> g.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).min() ==><span class="integer">9</span> gremlin> g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).min(local) ==><span class="predefined-constant">null</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.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).min() g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).min(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#min()"><code>min()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#min(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>min(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="none-step">None Step</h3> <div class="paragraph"> <p>The <code>none()</code>-step (<strong>filter</strong>) filters all objects from a traversal stream. It is especially useful for traversals that are executed remotely where returning results is not useful and the traversal is only meant to generate side-effects. Choosing not to return results saves in serialization and network costs as the objects are filtered on the remote end and not returned to the client side. Typically, this step does not need to be used directly and is quietly used by the <code>iterate()</code> terminal step which appends <code>none()</code> to the traversal before actually cycling through results.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> As of release 4.0.0, <code>none()</code> will be renamed to <code>discard()</code>. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Traversal.html#none()"><code>none()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Traversal.html#iterate()"><code>iterate()</code></a></p> </div> </div> <div class="sect2"> <h3 id="not-step">Not Step</h3> <div class="paragraph"> <p>The <code>not()</code>-step (<strong>filter</strong>) removes objects from the traversal stream when the traversal provided as an argument returns an object.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Groovy</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>not</code> is a reserved word in Groovy, and when therefore used as part of an anonymous traversal must be referred to in Gremlin with the double underscore <code>__.not()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>not</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>not_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-229" type="radio" name="radio-set-1729796988-229" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-229" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-230" type="radio" name="radio-set-1729796988-229" class="tab-selector-2" /> <label for="tab-1729796988-230" 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> g.V().not(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)).elementMap() ==>[<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] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</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">(1)</b> ==>marko ==>vadas ==>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().not(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)).elementMap() g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</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">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>josh created two projects and vadas none</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#not(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>not(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="option-step">Option Step</h3> <div class="paragraph"> <p>An option to a <a href="#general-steps"><code>branch()</code></a> or <a href="#choose-step"><code>choose()</code></a>.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#option(M,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>option(Object,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#option(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>option(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="optional-step">Optional Step</h3> <div class="paragraph"> <p>The <code>optional()</code>-step (<strong>branch/flatMap</strong>) returns the result of the specified traversal if it yields a result else it returns the calling element, i.e. the <code>identity()</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-231" type="radio" name="radio-set-1729796988-231" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-231" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-232" type="radio" name="radio-set-1729796988-231" class="tab-selector-2" /> <label for="tab-1729796988-232" 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> g.V(<span class="integer">2</span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">2</span>] gremlin> g.V(<span class="integer">2</span>).optional(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==>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">2</span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">2</span>).optional(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</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>vadas does not have an outgoing knows-edge so vadas is returned.</p> </li> <li> <p>vadas does have an incoming knows-edge so marko is returned.</p> </li> </ol> </div> <div class="paragraph"> <p><code>optional</code> is particularly useful for lifting entire graphs when used in conjunction with <code>path</code> or <code>tree</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-233" type="radio" name="radio-set-1729796988-233" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-233" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-234" type="radio" name="radio-set-1729796988-233" class="tab-selector-2" /> <label for="tab-1729796988-234" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>))).path() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">2</span>]] ==>[v[<span class="integer">4</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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).optional(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>))).path() <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Returns the paths of everybody followed by who they know followed by what they created.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#optional(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>optional(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="or-step">Or Step</h3> <div class="paragraph"> <p>The <code>or()</code>-step ensures that at least one of the provided traversals yield a result (<strong>filter</strong>). Please see <a href="#and-step"><code>and()</code></a> for and-semantics.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>or</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>or_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-235" type="radio" name="radio-set-1729796988-235" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-235" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-236" type="radio" name="radio-set-1729796988-235" class="tab-selector-2" /> <label for="tab-1729796988-236" 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> g.V().or( __.outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), __.inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</span>))). values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>lop ==>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().or( __.outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>), __.inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</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 <code>or()</code>-step can take an arbitrary number of traversals. At least one of the traversals must produce at least one output for the original traverser to pass to the next step.</p> </div> <div class="paragraph"> <p>An <a href="http://en.wikipedia.org/wiki/Infix_notation">infix notation</a> can be used as well.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-237" type="radio" name="radio-set-1729796988-237" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-237" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-238" type="radio" name="radio-set-1729796988-237" class="tab-selector-2" /> <label for="tab-1729796988-238" 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> g.V().where(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).or().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>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().where(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).or().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#or(org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>or(Traversal…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="order-step">Order Step</h3> <div class="paragraph"> <p>When the objects of the traversal stream need to be sorted, <code>order()</code>-step (<strong>map</strong>) can be leveraged.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-239" type="radio" name="radio-set-1729796988-239" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-239" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-240" type="radio" name="radio-set-1729796988-239" class="tab-selector-2" /> <label for="tab-1729796988-240" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order() ==>josh ==>lop ==>marko ==>peter ==>ripple ==>vadas gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().by(desc) ==>vadas ==>ripple ==>peter ==>marko ==>lop ==>josh gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, asc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>marko ==>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().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order() g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().by(desc) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, asc).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>One of the most traversed objects in a traversal is an <code>Element</code>. An element can have properties associated with it (i.e. key/value pairs). In many situations, it is desirable to sort an element traversal stream according to a comparison of their properties.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-241" type="radio" name="radio-set-1729796988-241" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-241" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-242" type="radio" name="radio-set-1729796988-241" class="tab-selector-2" /> <label for="tab-1729796988-242" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V().order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,asc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>josh ==>lop ==>marko ==>peter ==>ripple ==>vadas gremlin> g.V().order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,desc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>ripple ==>peter ==>marko ==>lop ==>josh gremlin> g.V().both().order().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">(1)</b> ==>v[<span class="integer">2</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">4</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().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,asc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().order().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,desc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().both().order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>The <code>order()</code>-step allows the user to provide an arbitrary number of comparators for primary, secondary, etc. sorting. In the example below, the primary ordering is based on the outgoing created-edge count. The secondary ordering is based on the age of the person.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-243" type="radio" name="radio-set-1729796988-243" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-243" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-244" type="radio" name="radio-set-1729796988-243" class="tab-selector-2" /> <label for="tab-1729796988-244" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count(), asc). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, asc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>marko ==>peter ==>josh gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count(), asc). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, desc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>peter ==>marko ==>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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count(), asc). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, asc).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count(), asc). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, desc).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>Randomizing the order of the traversers at a particular point in the traversal is possible with <code>Order.shuffle</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-245" type="radio" name="radio-set-1729796988-245" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-245" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-246" type="radio" name="radio-set-1729796988-245" class="tab-selector-2" /> <label for="tab-1729796988-246" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(shuffle) ==>v[<span class="integer">4</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">2</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(shuffle) ==>v[<span class="integer">6</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">1</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.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(shuffle) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).order().by(shuffle)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is possible to use <code>order(local)</code> to order the current local object and not the entire traversal stream. This works for <code>Collection</code>- and <code>Map</code>-type objects. For any other object, the object is returned unchanged.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-247" type="radio" name="radio-set-1729796988-247" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-247" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-248" type="radio" name="radio-set-1729796988-247" class="tab-selector-2" /> <label for="tab-1729796988-248" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().order(local).by(desc) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="integer">35</span>,<span class="integer">32</span>,<span class="integer">29</span>,<span class="integer">27</span>] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order(local).by(desc) <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="integer">29</span> ==><span class="integer">27</span> ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().groupCount().by(inE().count()).order(local).by(values, desc) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="integer">1</span>:<span class="integer">3</span>,<span class="integer">0</span>:<span class="integer">2</span>,<span class="integer">3</span>:<span class="integer">1</span>] gremlin> g.V().groupCount().by(inE().count()).order(local).by(keys, asc) <span class="comment">//</span>// <b class="conum">(4)</b> ==>[<span class="integer">0</span>:<span class="integer">2</span>,<span class="integer">1</span>:<span class="integer">3</span>,<span class="integer">3</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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().order(local).by(desc) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order(local).by(desc) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().groupCount().by(inE().count()).order(local).by(values, desc) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().groupCount().by(inE().count()).order(local).by(keys, asc) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The ages are gathered into a list and then that list is sorted in decreasing order.</p> </li> <li> <p>The ages are not gathered and thus <code>order(local)</code> is "ordering" single integers and thus, does nothing.</p> </li> <li> <p>The <code>groupCount()</code> map is ordered by its values in decreasing order.</p> </li> <li> <p>The <code>groupCount()</code> map is ordered by its keys in increasing order.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>values</code> and <code>keys</code> enums are from <code>Column</code> which is used to select "columns" from a <code>Map</code>, <code>Map.Entry</code>, or <code>Path</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>If a property key does not exist, then it will be treated as <code>null</code> which will sort it first for <code>Order.asc</code> and last for <code>Order.desc</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-249" type="radio" name="radio-set-1729796988-249" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-249" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-250" type="radio" name="radio-set-1729796988-249" class="tab-selector-2" /> <label for="tab-1729796988-250" 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> g.V().order().by(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).elementMap() ==>[<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">27</span>] ==>[<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">29</span>] ==>[<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">32</span>] ==>[<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">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().order().by(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).elementMap()</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"> Prior to version 3.3.4, ordering was defined by <code>Order.incr</code> for ascending order and <code>Order.decr</code> for descending order. Those tokens were deprecated and eventually removed in 3.5.0. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#order()"><code>order()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#order(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>order(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Order.html"><code>Order</code></a></p> </div> </div> <div class="sect2"> <h3 id="pagerank-step">PageRank Step</h3> <div class="paragraph"> <p>The <code>pageRank()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>) calculates <a href="http://en.wikipedia.org/wiki/PageRank">PageRank</a> using <a href="#pagerankvertexprogram"><code>PageRankVertexProgram</code></a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>pageRank()</code>-step is a <code>VertexComputing</code>-step and as such, can only be used against a graph that supports <code>GraphComputer</code> (OLAP). </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-251" type="radio" name="radio-set-1729796988-251" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-251" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-252" type="radio" name="radio-set-1729796988-251" class="tab-selector-2" /> <label for="tab-1729796988-252" 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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().pageRank().with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>) gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). pageRank(). with(PageRank.edges, __.outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>,desc). elementMap(<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">friendRank</span><span class="delimiter">'</span></span>) ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">friendRank</span>:<span class="float">0.8321166533236799</span>,<span class="key">name</span>:josh] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">friendRank</span>:<span class="float">0.8321166533236799</span>,<span class="key">name</span>:vadas] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">friendRank</span>:<span class="float">0.5839416733381598</span>,<span class="key">name</span>:peter] ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">friendRank</span>:<span class="float">0.5839416733381598</span>,<span class="key">name</span>:marko]</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">friendRank</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). pageRank(). with(PageRank.edges, __.outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>,desc). elementMap(<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">friendRank</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note the use of the <code>with()</code> modulating step which provides configuration options to the algorithm. It takes configuration keys from the <code>PageRank</code> and is automatically imported to the Gremlin Console.</p> </div> <div class="paragraph"> <p>The <a href="#explain-step"><code>explain()</code></a>-step can be used to understand how the traversal is compiled into multiple <code>GraphComputer</code> jobs.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-253" type="radio" name="radio-set-1729796988-253" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-253" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-254" type="radio" name="radio-set-1729796988-253" class="tab-selector-2" /> <label for="tab-1729796988-254" 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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). pageRank(). with(PageRank.edges, __.outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>,desc). elementMap(<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">friendRank</span><span class="delimiter">'</span></span>).explain() ==>Traversal Explanation ============================================================================================================================================================================================================================================= Original Traversal [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), OrderGlobalStep([[value(friendRank), desc]]), ElementMa pStep([name, friendRank])] VertexProgramStrategy [D] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] ConnectiveStrategy [D] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] IdentityRemovalStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] MatchPredicateStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] FilterRankingStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] IncidentToAdjacentStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] PathProcessorStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] InlineFilterStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] AdjacentToIncidentStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] MessagePassingReductionStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] RepeatUnrollStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] PathRetractionStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] EarlyLimitStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] ByModulatorOptimizationStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] CountStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] LazyBarrierStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] OrderLimitStrategy [O] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] TinkerGraphCountStrategy [P] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] TinkerGraphStepStrategy [P] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] ProfileStrategy [F] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] ComputerFinalizationStrategy [T] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] ComputerVerificationStrategy [V] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] StandardVerificationStrategy [V] [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep] Final Traversal [TraversalVertexProgramStep([GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,<span class="integer">20</span>,graphfilter[none]), Travers alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]</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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). pageRank(). with(PageRank.edges, __.outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>). order().by(<span class="string"><span class="delimiter">'</span><span class="content">friendRank</span><span class="delimiter">'</span></span>,desc). elementMap(<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">friendRank</span><span class="delimiter">'</span></span>).explain()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#pageRank()"><code>pageRank()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#pageRank(double)"><code>pageRank(double)</code></a></p> </div> </div> <div class="sect2"> <h3 id="path-step">Path Step</h3> <div class="paragraph"> <p>A traverser is transformed as it moves through a series of steps within a traversal. The history of the traverser is realized by examining its path with <code>path()</code>-step (<strong>map</strong>).</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/path-step.png" alt="path step" width="650"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-255" type="radio" name="radio-set-1729796988-255" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-255" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-256" type="radio" name="radio-set-1729796988-255" class="tab-selector-2" /> <label for="tab-1729796988-256" 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> g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>ripple ==>lop gremlin> g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).path() ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>],ripple] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>],lop] gremlin> g.V().both().path().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">(1)</b> ==>[<span class="integer">29</span>,<span class="integer">27</span>] ==>[<span class="integer">29</span>,<span class="integer">32</span>] ==>[<span class="integer">27</span>,<span class="integer">29</span>] ==>[<span class="integer">32</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().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).path() g.V().both().path().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>If edges are required in the path, then be sure to traverse those edges explicitly.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-257" type="radio" name="radio-set-1729796988-257" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-257" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-258" type="radio" name="radio-set-1729796988-257" class="tab-selector-2" /> <label for="tab-1729796988-258" 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> g.V().outE().inV().outE().inV().path() ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</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().outE().inV().outE().inV().path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is possible to post-process the elements of the path in a round-robin fashion via <code>by()</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-259" type="radio" name="radio-set-1729796988-259" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-259" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-260" type="radio" name="radio-set-1729796988-259" class="tab-selector-2" /> <label for="tab-1729796988-260" 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> g.V().out().out().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==>[marko,<span class="integer">32</span>,ripple] ==>[marko,<span class="integer">32</span>,lop]</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().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).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>Finally, because <code>by()</code>-based post-processing, nothing prevents triggering yet another traversal. In the traversal below, for each element of the path traversed thus far, if its a person (as determined by having an <code>age</code>-property), then get all of their creations, else if its a creation, get all the people that created it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-261" type="radio" name="radio-set-1729796988-261" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-261" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-262" type="radio" name="radio-set-1729796988-261" class="tab-selector-2" /> <label for="tab-1729796988-262" 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> g.V().out().out().path().by( choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>), __.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()) ==>[[lop],[ripple,lop],[josh]] ==>[[lop],[ripple,lop],[marko,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().out().out().path().by( choose(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>), out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>), __.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It’s possible to limit the path using the <a href="#to-step"><code>to()</code></a> or <a href="#from-step"><code>from()</code></a> step modulators.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-263" type="radio" name="radio-set-1729796988-263" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-263" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-264" type="radio" name="radio-set-1729796988-263" class="tab-selector-2" /> <label for="tab-1729796988-264" 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> 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">e</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</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>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[vadas,marko,josh] gremlin> 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">e</span><span class="delimiter">'</span></span>). in(<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">m</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)). path().to(<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>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[vadas,marko] gremlin> 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">e</span><span class="delimiter">'</span></span>). in(<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">m</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)). path().from(<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>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[marko,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">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">e</span><span class="delimiter">'</span></span>). in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</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>) <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">vadas</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). in(<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">m</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)). path().to(<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>) <span class="comment">//</span>// <b class="conum">(2)</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">vadas</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>). in(<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">m</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>)). path().from(<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>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Obtain the full path from vadas to josh.</p> </li> <li> <p>Save the middle node, marko, and use the <code>to()</code> modulator to show only the path from vadas to marko</p> </li> <li> <p>Use the <code>from()</code> mdoulator to show only the path from marko to josh</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Generating path information is expensive as the history of the traverser is stored into a Java list. With numerous traversers, there are numerous lists. Moreover, in an OLAP <a href="#graphcomputer"><code>GraphComputer</code></a> environment this becomes exceedingly prohibitive as there are traversers emanating from all vertices in the graph in parallel. In OLAP there are optimizations provided for traverser populations, but when paths are calculated (and each traverser is unique due to its history), then these optimizations are no longer possible. </td> </tr> </table> </div> <div class="sect3"> <h4 id="path-data-structure">Path Data Structure</h4> <div class="paragraph"> <p>The <code>Path</code> data structure is an ordered list of objects, where each object is associated to a <code>Set<String></code> of labels. An example is presented below to demonstrate both the <code>Path</code> API as well as how a traversal yields labeled paths.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/path-data-structure.png" alt="path data structure" width="350"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-265" type="radio" name="radio-set-1729796988-265" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-265" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-266" type="radio" name="radio-set-1729796988-265" class="tab-selector-2" /> <label for="tab-1729796988-266" 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> path = g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).out(<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">c</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">ripple</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). identity().as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).path().next() ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">5</span>] ==>ripple gremlin> path.size() ==><span class="integer">4</span> gremlin> path.objects() ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">5</span>] ==>ripple gremlin> path.labels() ==>[b,a] ==><span class="type">[]</span> ==>[c] ==>[d,e] gremlin> path.a ==>v[<span class="integer">1</span>] gremlin> path.b ==>v[<span class="integer">1</span>] gremlin> path.c ==>v[<span class="integer">5</span>] gremlin> path.d == path.e ==><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">path = g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).out(<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">c</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">ripple</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>). identity().as(<span class="string"><span class="delimiter">'</span><span class="content">e</span><span class="delimiter">'</span></span>).path().next() path.size() path.objects() path.labels() path.a path.b path.c path.d == path.e</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#path()"><code>path()</code></a></p> </div> </div> </div> <div class="sect2"> <h3 id="peerpressure-step">PeerPressure Step</h3> <div class="paragraph"> <p>The <code>peerPressure()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>) clusters vertices using <a href="#peerpressurevertexprogram"><code>PeerPressureVertexProgram</code></a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>peerPressure()</code>-step is a <code>VertexComputing</code>-step and as such, can only be used against a graph that supports <code>GraphComputer</code> (OLAP). </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-267" type="radio" name="radio-set-1729796988-267" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-267" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-268" type="radio" name="radio-set-1729796988-267" class="tab-selector-2" /> <label for="tab-1729796988-268" 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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().peerPressure().with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>) ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">1</span> ==><span class="integer">6</span> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). peerPressure(). with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>). group(). by(<span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[<span class="integer">1</span>:[vadas,marko,josh],<span class="integer">6</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 = traversal().withEmbedded(graph).withComputer() g.V().peerPressure().with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). peerPressure(). with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>). group(). by(<span class="string"><span class="delimiter">'</span><span class="content">cluster</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>Note the use of the <code>with()</code> modulating step which provides configuration options to the algorithm. It takes configuration keys from the <code>PeerPressure</code> class and is automatically imported to the Gremlin Console.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#peerPressure()"><code>peerPressure()</code></a></p> </div> </div> <div class="sect2"> <h3 id="product-step">Product Step</h3> <div class="paragraph"> <p>The <code>product()</code>-step (<strong>map</strong>) calculates the cartesian product between the incoming list traverser and the provided list argument. This step only expects list data (array or Iterable) and will throw an <code>IllegalArgumentException</code> if any other type is encountered (including <code>null</code>).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-269" type="radio" name="radio-set-1729796988-269" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-269" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-270" type="radio" name="radio-set-1729796988-269" class="tab-selector-2" /> <label for="tab-1729796988-270" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().product([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</span><span class="delimiter">"</span></span>]) ==>[[marko,james],[marko,jen],[vadas,james],[vadas,jen],[lop,james],[lop,jen],[josh,james],[josh,jen],[ripple,james],[ripple,jen],[peter,james],[peter,jen]] gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().product(__.V().has(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).limit(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).fold()) ==>[[marko,<span class="integer">29</span>],[vadas,<span class="integer">29</span>],[lop,<span class="integer">29</span>],[josh,<span class="integer">29</span>],[ripple,<span class="integer">29</span>],[peter,<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().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().product([<span class="string"><span class="delimiter">"</span><span class="content">james</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">jen</span><span class="delimiter">"</span></span>]) g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().product(__.V().has(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).limit(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).fold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#product(java.lang.Object)"><code>product(Object)</code></a> <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#product-step"><code>Semantics</code></a></p> </div> </div> <div class="sect2"> <h3 id="profile-step">Profile Step</h3> <div class="paragraph"> <p>The <code>profile()</code>-step (<strong>sideEffect</strong>) exists to allow developers to profile their traversals to determine statistical information like step runtime, counts, etc.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Profiling a Traversal will impede the Traversal’s performance. This overhead is mostly excluded from the profile results, but durations are not exact. Thus, durations are best considered in relation to each other. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-271" type="radio" name="radio-set-1729796988-271" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-271" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-272" type="radio" name="radio-set-1729796988-271" class="tab-selector-2" /> <label for="tab-1729796988-272" 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> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).repeat(both()).times(<span class="integer">3</span>).hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum().profile() ==>Traversal Metrics Step Count Traversers <span class="predefined-type">Time</span> (ms) % Dur ============================================================================================================= TinkerGraphStep(vertex,<span class="type">[]</span>) <span class="integer">6</span> <span class="integer">6</span> <span class="float">0.084</span> <span class="float">22.51</span> VertexStep(OUT,[created],vertex) <span class="integer">4</span> <span class="integer">4</span> <span class="float">0.044</span> <span class="float">11.97</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">4</span> <span class="integer">2</span> <span class="float">0.026</span> <span class="float">6.94</span> VertexStep(BOTH,vertex) <span class="integer">10</span> <span class="integer">4</span> <span class="float">0.012</span> <span class="float">3.44</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">10</span> <span class="integer">3</span> <span class="float">0.013</span> <span class="float">3.68</span> VertexStep(BOTH,vertex) <span class="integer">24</span> <span class="integer">7</span> <span class="float">0.016</span> <span class="float">4.40</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">24</span> <span class="integer">5</span> <span class="float">0.018</span> <span class="float">4.92</span> VertexStep(BOTH,vertex) <span class="integer">58</span> <span class="integer">11</span> <span class="float">0.022</span> <span class="float">5.88</span> NoOpBarrierStep(<span class="integer">2500</span>) <span class="integer">58</span> <span class="integer">6</span> <span class="float">0.030</span> <span class="float">8.03</span> HasStep([~label.eq(person)]) <span class="integer">48</span> <span class="integer">4</span> <span class="float">0.027</span> <span class="float">7.34</span> PropertiesStep([age],value) <span class="integer">48</span> <span class="integer">4</span> <span class="float">0.024</span> <span class="float">6.41</span> SumGlobalStep <span class="integer">1</span> <span class="integer">1</span> <span class="float">0.054</span> <span class="float">14.48</span> >TOTAL - - <span class="float">0.375</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(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).repeat(both()).times(<span class="integer">3</span>).hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum().profile()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>profile()</code>-step generates a <code>TraversalMetrics</code> sideEffect object that contains the following information:</p> </div> <div class="ulist"> <ul> <li> <p><code>Step</code>: A step within the traversal being profiled.</p> </li> <li> <p><code>Count</code>: The number of <em>represented</em> traversers that passed through the step.</p> </li> <li> <p><code>Traversers</code>: The number of traversers that passed through the step.</p> </li> <li> <p><code>Time (ms)</code>: The total time the step was actively executing its behavior.</p> </li> <li> <p><code>% Dur</code>: The percentage of total time spent in the step.</p> </li> </ul> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-exercise.png" alt="gremlin exercise" width="120"></span> It is important to understand the difference between "Count" and "Traversers". Traversers can be merged and as such, when two traversers are "the same" they may be aggregated into a single traverser. That new traverser has a <code>Traverser.bulk()</code> that is the sum of the two merged traverser bulks. On the other hand, the <code>Count</code> represents the sum of all <code>Traverser.bulk()</code> results and thus, expresses the number of "represented" (not enumerated) traversers. <code>Traversers</code> will always be less than or equal to <code>Count</code>.</p> </div> <div class="paragraph"> <p>For traversal compilation information, please see <a href="#explain-step"><code>explain()</code></a>-step.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#profile()"><code>profile()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#profile(java.lang.String)"><code>profile(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="project-step">Project Step</h3> <div class="paragraph"> <p>The <code>project()</code>-step (<strong>map</strong>) projects the current object into a <code>Map<String,Object></code> keyed by provided labels. It is similar to <a href="#select-step"><code>select()</code></a>-step, save that instead of retrieving and modulating historic traverser state, it modulates the current state of the traverser.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-273" type="radio" name="radio-set-1729796988-273" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-273" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-274" type="radio" name="radio-set-1729796988-273" class="tab-selector-2" /> <label for="tab-1729796988-274" 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> 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>). project(<span class="string"><span class="delimiter">'</span><span class="content">id</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">out</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">in</span><span class="delimiter">'</span></span>). by(id). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(outE().count()). by(inE().count()) ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">name</span>:marko,<span class="key">out</span>:<span class="integer">3</span>,<span class="keyword">in</span>:<span class="integer">0</span>] gremlin> 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>). 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">friendsNames</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(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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()) ==>[<span class="key">name</span>:marko,<span class="key">friendsNames</span>:[vadas,josh]] gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). project(<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>). 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">created</span><span class="delimiter">'</span></span>).count()). order().by(select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>),desc). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) ==>lop ==>lop ==>lop ==>ripple gremlin> g.V().project(<span class="string"><span class="delimiter">'</span><span class="content">n</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">a</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(<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> ==>[<span class="key">n</span>:marko,<span class="key">a</span>:<span class="integer">29</span>] ==>[<span class="key">n</span>:vadas,<span class="key">a</span>:<span class="integer">27</span>] ==>[<span class="key">n</span>:lop] ==>[<span class="key">n</span>:josh,<span class="key">a</span>:<span class="integer">32</span>] ==>[<span class="key">n</span>:ripple] ==>[<span class="key">n</span>:peter,<span class="key">a</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().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>). project(<span class="string"><span class="delimiter">'</span><span class="content">id</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">out</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">in</span><span class="delimiter">'</span></span>). by(id). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(outE().count()). by(inE().count()) 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>). 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">friendsNames</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(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</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()) g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). project(<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>). 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">created</span><span class="delimiter">'</span></span>).count()). order().by(select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>),desc). select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) g.V().project(<span class="string"><span class="delimiter">'</span><span class="content">n</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">a</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(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered and the key not present in the <code>Map</code>.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#project(java.lang.String,java.lang.String...)"><code>project(String,String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="program-step">Program Step</h3> <div class="paragraph"> <p>The <code>program()</code>-step (<strong>map</strong>/<strong>sideEffect</strong>) is the "lambda" step for <code>GraphComputer</code> jobs. The step takes a <a href="#vertexprogram"><code>VertexProgram</code></a> as an argument and will process the incoming graph accordingly. Thus, the user can create their own <code>VertexProgram</code> and have it execute within a traversal. The configuration provided to the vertex program includes:</p> </div> <div class="ulist"> <ul> <li> <p><code>gremlin.vertexProgramStep.rootTraversal</code> is a serialization of a <code>PureTraversal</code> form of the root traversal.</p> </li> <li> <p><code>gremlin.vertexProgramStep.stepId</code> is the step string id of the <code>program()</code>-step being executed.</p> </li> </ul> </div> <div class="paragraph"> <p>The user supplied <code>VertexProgram</code> can leverage that information accordingly within their vertex program. Example uses are provided below.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Developing a <code>VertexProgram</code> is for expert users. Moreover, developing one that can be used effectively within a traversal requires yet more expertise. This information is recommended to advanced users with a deep understanding of the mechanics of Gremlin OLAP (<a href="#graphcomputer"><code>GraphComputer</code></a>). </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">private</span> TraverserSet<<span class="predefined-type">Object</span>> haltedTraversers; <span class="directive">public</span> <span class="type">void</span> loadState(Graph graph, <span class="predefined-type">Configuration</span> configuration) { VertexProgram.super.loadState(graph, configuration); <span class="local-variable">this</span>.traversal = PureTraversal.loadState(configuration, VertexProgramStep.ROOT_TRAVERSAL, graph); <span class="local-variable">this</span>.programStep = <span class="keyword">new</span> TraversalMatrix<>(<span class="local-variable">this</span>.traversal.get()).getStepById(configuration.getString(ProgramVertexProgramStep.STEP_ID)); <span class="comment">// if the traversal sideEffects will be used in the computation, add them as memory compute keys</span> <span class="local-variable">this</span>.memoryComputeKeys.addAll(MemoryTraversalSideEffects.getMemoryComputeKeys(<span class="local-variable">this</span>.traversal.get())); <span class="comment">// if master-traversal traversers may be propagated, create a memory compute key</span> <span class="local-variable">this</span>.memoryComputeKeys.add(MemoryComputeKey.of(TraversalVertexProgram.HALTED_TRAVERSERS, Operator.addAll, <span class="predefined-constant">false</span>, <span class="predefined-constant">false</span>)); <span class="comment">// returns an empty traverser set if there are no halted traversers</span> <span class="local-variable">this</span>.haltedTraversers = TraversalVertexProgram.loadHaltedTraversers(configuration); } <span class="directive">public</span> <span class="type">void</span> storeState(<span class="predefined-type">Configuration</span> configuration) { VertexProgram.super.storeState(configuration); <span class="comment">// if halted traversers is null or empty, it does nothing</span> TraversalVertexProgram.storeHaltedTraversers(configuration, <span class="local-variable">this</span>.haltedTraversers); } <span class="directive">public</span> <span class="type">void</span> setup(Memory memory) { <span class="keyword">if</span>(!<span class="local-variable">this</span>.haltedTraversers.isEmpty()) { <span class="comment">// do what you like with the halted master traversal traversers</span> } <span class="comment">// once used, no need to keep that information around (master)</span> <span class="local-variable">this</span>.haltedTraversers = <span class="predefined-constant">null</span>; } <span class="directive">public</span> <span class="type">void</span> execute(Vertex vertex, Messenger messenger, Memory memory) { <span class="comment">// once used, no need to keep that information around (workers)</span> <span class="keyword">if</span>(<span class="predefined-constant">null</span> != <span class="local-variable">this</span>.haltedTraversers) <span class="local-variable">this</span>.haltedTraversers = <span class="predefined-constant">null</span>; <span class="keyword">if</span>(vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent()) { <span class="comment">// haltedTraversers in execute() represent worker-traversal traversers</span> <span class="comment">// for example, from a traversal of the form g.V().out().program(...)</span> TraverserSet<<span class="predefined-type">Object</span>> haltedTraversers = vertex.value(TraversalVertexProgram.HALTED_TRAVERSERS); <span class="comment">// create a new halted traverser set that can be used by the next OLAP job in the chain</span> <span class="comment">// these are worker-traversers that are distributed throughout the graph</span> TraverserSet<<span class="predefined-type">Object</span>> newHaltedTraversers = <span class="keyword">new</span> TraverserSet<>(); haltedTraversers.forEach(traverser -> { newHaltedTraversers.add(traverser.split(traverser.get().toString(), <span class="local-variable">this</span>.programStep)); }); vertex.property(VertexProperty.Cardinality.single, TraversalVertexProgram.HALTED_TRAVERSERS, newHaltedTraversers); <span class="comment">// it is possible to create master-traversers that are localized to the master traversal (this is how results are ultimately delivered back to the user)</span> memory.add(TraversalVertexProgram.HALTED_TRAVERSERS, <span class="keyword">new</span> TraverserSet<>(<span class="local-variable">this</span>.traversal().get().getTraverserGenerator().generate(<span class="string"><span class="delimiter">"</span><span class="content">an example</span><span class="delimiter">"</span></span>, <span class="local-variable">this</span>.programStep, <span class="integer">1l</span>))); } <span class="directive">public</span> <span class="type">boolean</span> terminate(Memory memory) { <span class="comment">// the master-traversal will have halted traversers</span> <span class="keyword">assert</span> memory.exists(TraversalVertexProgram.HALTED_TRAVERSERS); TraverserSet<<span class="predefined-type">String</span>> haltedTraversers = memory.get(TraversalVertexProgram.HALTED_TRAVERSERS); <span class="comment">// it will only have the traversers sent to the master traversal via memory</span> <span class="keyword">assert</span> haltedTraversers.stream().map(Traverser::get).filter(s -> s.equals(<span class="string"><span class="delimiter">"</span><span class="content">an example</span><span class="delimiter">"</span></span>)).findAny().isPresent(); <span class="comment">// it will not contain the worker traversers distributed throughout the vertices</span> <span class="keyword">assert</span> !haltedTraversers.stream().map(Traverser::get).filter(s -> !s.equals(<span class="string"><span class="delimiter">"</span><span class="content">an example</span><span class="delimiter">"</span></span>)).findAny().isPresent(); <span class="keyword">return</span> <span class="predefined-constant">true</span>; }</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The test case <code>ProgramTest</code> in <code>gremlin-test</code> has an example vertex program called <code>TestProgram</code> that demonstrates all the various ways in which traversal and traverser information is propagated within a vertex program and ultimately usable by other vertex programs (including <code>TraversalVertexProgram</code>) down the line in an OLAP compute chain. </td> </tr> </table> </div> <div class="paragraph"> <p>Finally, an example is provided using <code>PageRankVertexProgram</code> which doesn’t use <a href="#pagerank-step"><code>pageRank()</code></a>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-275" type="radio" name="radio-set-1729796988-275" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-275" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-276" type="radio" name="radio-set-1729796988-275" class="tab-selector-2" /> <label for="tab-1729796988-276" 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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). program(PageRankVertexProgram.build().property(<span class="string"><span class="delimiter">'</span><span class="content">rank</span><span class="delimiter">'</span></span>).create(graph)). order().by(<span class="string"><span class="delimiter">'</span><span class="content">rank</span><span class="delimiter">'</span></span>, asc). elementMap(<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">rank</span><span class="delimiter">'</span></span>) ==>[<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">rank</span>:<span class="float">0.14598540152719103</span>] ==>[<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">rank</span>:<span class="float">0.11375510357865537</span>] ==>[<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">rank</span>:<span class="float">0.14598540152719103</span>] ==>[<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">rank</span>:<span class="float">0.11375510357865537</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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). program(PageRankVertexProgram.build().property(<span class="string"><span class="delimiter">'</span><span class="content">rank</span><span class="delimiter">'</span></span>).create(graph)). order().by(<span class="string"><span class="delimiter">'</span><span class="content">rank</span><span class="delimiter">'</span></span>, asc). elementMap(<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">rank</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="properties-step">Properties Step</h3> <div class="paragraph"> <p>The <code>properties()</code>-step (<strong>map</strong>) extracts properties from an <code>Element</code> in the traversal stream.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-277" type="radio" name="radio-set-1729796988-277" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-277" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-278" type="radio" name="radio-set-1729796988-277" class="tab-selector-2" /> <label for="tab-1729796988-278" 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> g.V(<span class="integer">1</span>).properties() ==>vp[name->marko] ==>vp[location->san diego] ==>vp[location->santa cruz] ==>vp[location->brussels] ==>vp[location->santa fe] gremlin> g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">startTime</span>:<span class="integer">1997</span>,<span class="key">endTime</span>:<span class="integer">2001</span>] ==>[<span class="key">startTime</span>:<span class="integer">2001</span>,<span class="key">endTime</span>:<span class="integer">2004</span>] ==>[<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2005</span>] ==>[<span class="key">startTime</span>:<span class="integer">2005</span>] gremlin> g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">startTime</span>:<span class="integer">1997</span>,<span class="key">endTime</span>:<span class="integer">2001</span>] ==>[<span class="key">startTime</span>:<span class="integer">2001</span>,<span class="key">endTime</span>:<span class="integer">2004</span>] ==>[<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2005</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>).properties() g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap() g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>).valueMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#properties(java.lang.String...)"><code>properties(String…​)</code></a></p> </div> <div class="paragraph"> <p><a id="addproperty-step"></a></p> </div> </div> <div class="sect2"> <h3 id="property-step">Property Step</h3> <div class="paragraph"> <p>The <code>property()</code>-step is used to add properties to the elements of the graph (<strong>sideEffect</strong>). Unlike <code>addV()</code> and <code>addE()</code>, <code>property()</code> is a full sideEffect step in that it does not return the property it created, but the element that streamed into it. Moreover, if <code>property()</code> follows an <code>addV()</code> or <code>addE()</code>, then it is "folded" into the previous step to enable vertex and edge creation with all its properties in one creation operation.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-279" type="radio" name="radio-set-1729796988-279" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-279" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-280" type="radio" name="radio-set-1729796988-279" class="tab-selector-2" /> <label for="tab-1729796988-280" 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> g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">country</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">usa</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">country</span>:[usa],<span class="key">city</span>:[santa fe],<span class="key">name</span>:[marko],<span class="key">state</span>:[<span class="keyword">new</span> mexico],<span class="key">age</span>:[<span class="integer">29</span>]] gremlin> g.V(<span class="integer">1</span>).property([<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).property(list,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">35</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).property(list, [<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).valueMap() ==>[<span class="key">country</span>:[usa],<span class="key">city</span>:[santa fe,santa fe],<span class="key">name</span>:[marko],<span class="key">state</span>:[<span class="keyword">new</span> mexico,<span class="keyword">new</span> mexico],<span class="key">age</span>:[<span class="integer">29</span>,<span class="integer">35</span>]] gremlin> g.V(<span class="integer">1</span>).property(list, [<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>: single(<span class="integer">36</span>), <span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">wilmington</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">delaware</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).valueMap() ==>[<span class="key">country</span>:[usa],<span class="key">city</span>:[santa fe,santa fe,wilmington],<span class="key">name</span>:[marko],<span class="key">state</span>:[<span class="keyword">new</span> mexico,<span class="keyword">new</span> mexico,delaware],<span class="key">age</span>:[<span class="integer">36</span>]] gremlin> g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">friendWeight</span><span class="delimiter">'</span></span>,outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).sum(),<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">friendWeight</span><span class="delimiter">'</span></span>).valueMap() <span class="comment">//</span>// <b class="conum">(6)</b> ==>[<span class="key">acl</span>:<span class="directive">private</span>] gremlin> g.addV().property(T.label,<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) <span class="comment">//</span>// <b class="conum">(7)</b> ==>[<span class="key">id</span>:<span class="integer">13</span>,<span class="key">label</span>:person] gremlin> g.addV().property(<span class="predefined-constant">null</span>) <span class="comment">//</span>// <b class="conum">(8)</b> ==>v[<span class="integer">14</span>] gremlin> g.addV().property(set, <span class="predefined-constant">null</span>) ==>v[<span class="integer">15</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>).property(<span class="string"><span class="delimiter">'</span><span class="content">country</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">usa</span><span class="delimiter">'</span></span>) g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>).property(<span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>).valueMap() g.V(<span class="integer">1</span>).property([<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).property(list,<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,<span class="integer">35</span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).property(list, [<span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">santa fe</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">new mexico</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">1</span>).valueMap() g.V(<span class="integer">1</span>).property(list, [<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>: single(<span class="integer">36</span>), <span class="string"><span class="delimiter">'</span><span class="content">city</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">wilmington</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">state</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">delaware</span><span class="delimiter">'</span></span>]) <span class="comment">//</span>// <b class="conum">(4)</b> g.V(<span class="integer">1</span>).valueMap() g.V(<span class="integer">1</span>).property(<span class="string"><span class="delimiter">'</span><span class="content">friendWeight</span><span class="delimiter">'</span></span>,outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).sum(),<span class="string"><span class="delimiter">'</span><span class="content">acl</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">private</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">friendWeight</span><span class="delimiter">'</span></span>).valueMap() <span class="comment">//</span>// <b class="conum">(6)</b> g.addV().property(T.label,<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) <span class="comment">//</span>// <b class="conum">(7)</b> g.addV().property(<span class="predefined-constant">null</span>) <span class="comment">//</span>// <b class="conum">(8)</b> g.addV().property(set, <span class="predefined-constant">null</span>)</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Properties can also take a <code>Map</code> as an argument.</p> </li> <li> <p>For vertices, a cardinality can be provided for <a href="#vertex-properties">vertex properties</a>.</p> </li> <li> <p>If a cardinality is specified for a <code>Map</code> then that cardinality will be used for all properties in the map.</p> </li> <li> <p>Assign the <code>Cardinality</code> individually to override the specified <code>list</code> or the default cardinality if not specified.</p> </li> <li> <p>It is possible to select the property value (as well as key) via a traversal.</p> </li> <li> <p>For vertices, the <code>property()</code>-step can add meta-properties.</p> </li> <li> <p>The label value can be specified as a property only at the time a vertex is added and if one is not specified in the addV()</p> </li> <li> <p>If you pass a <code>null</code> value for the Map this will be treated as a no-op and the input will be returned</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#property(java.lang.Object,java.lang.Object,java.lang.Object...)"><code>property(Object, Object, Object…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#property(org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality,java.lang.Object,java.lang.Object,java.lang.Object...)"><code>property(Cardinality, Object, Object, Object…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/structure/VertexProperty.Cardinality.html"><code>Cardinality</code></a></p> </div> </div> <div class="sect2"> <h3 id="propertymap-step">PropertyMap Step</h3> <div class="paragraph"> <p>The <code>propertiesMap()</code>-step yields a Map representation of the properties of an element.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-281" type="radio" name="radio-set-1729796988-281" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-281" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-282" type="radio" name="radio-set-1729796988-281" class="tab-selector-2" /> <label for="tab-1729796988-282" 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> g.V().propertyMap() ==>[<span class="key">name</span>:[vp[name->marko]],<span class="key">age</span>:[vp[age-><span class="integer">29</span>]]] ==>[<span class="key">name</span>:[vp[name->vadas]],<span class="key">age</span>:[vp[age-><span class="integer">27</span>]]] ==>[<span class="key">name</span>:[vp[name->lop]],<span class="key">lang</span>:[vp[lang->java]]] ==>[<span class="key">name</span>:[vp[name->josh]],<span class="key">age</span>:[vp[age-><span class="integer">32</span>]]] ==>[<span class="key">name</span>:[vp[name->ripple]],<span class="key">lang</span>:[vp[lang->java]]] ==>[<span class="key">name</span>:[vp[name->peter]],<span class="key">age</span>:[vp[age-><span class="integer">35</span>]]] gremlin> g.V().propertyMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==>[<span class="key">age</span>:[vp[age-><span class="integer">29</span>]]] ==>[<span class="key">age</span>:[vp[age-><span class="integer">27</span>]]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[vp[age-><span class="integer">32</span>]]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[vp[age-><span class="integer">35</span>]]] gremlin> g.V().propertyMap(<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">blah</span><span class="delimiter">'</span></span>) ==>[<span class="key">age</span>:[vp[age-><span class="integer">29</span>]]] ==>[<span class="key">age</span>:[vp[age-><span class="integer">27</span>]]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[vp[age-><span class="integer">32</span>]]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[vp[age-><span class="integer">35</span>]]] gremlin> g.E().propertyMap() ==>[<span class="key">weight</span>:p[weight-><span class="float">0.5</span>]] ==>[<span class="key">weight</span>:p[weight-><span class="float">1.0</span>]] ==>[<span class="key">weight</span>:p[weight-><span class="float">0.4</span>]] ==>[<span class="key">weight</span>:p[weight-><span class="float">1.0</span>]] ==>[<span class="key">weight</span>:p[weight-><span class="float">0.4</span>]] ==>[<span class="key">weight</span>:p[weight-><span class="float">0.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().propertyMap() g.V().propertyMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) g.V().propertyMap(<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">blah</span><span class="delimiter">'</span></span>) g.E().propertyMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#propertyMap(java.lang.String...)"><code>propertyMap(String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="range-step">Range Step</h3> <div class="paragraph"> <p>As traversers propagate through the traversal, it is possible to only allow a certain number of them to pass through with <code>range()</code>-step (<strong>filter</strong>). When the low-end of the range is not met, objects are continued to be iterated. When within the low (inclusive) and high (exclusive) range, traversers are emitted. When above the high range, the traversal breaks out of iteration. Finally, the use of <code>-1</code> on the high range will emit remaining traversers after the low range begins.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-283" type="radio" name="radio-set-1729796988-283" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-283" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-284" type="radio" name="radio-set-1729796988-283" class="tab-selector-2" /> <label for="tab-1729796988-284" 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> g.V().range(<span class="integer">0</span>,<span class="integer">3</span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">3</span>] gremlin> g.V().range(<span class="integer">1</span>,<span class="integer">3</span>) ==>v[<span class="integer">2</span>] ==>v[<span class="integer">3</span>] gremlin> g.V().range(<span class="integer">1</span>, -<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> g.V().repeat(both()).times(<span class="integer">1000000</span>).emit().range(<span class="integer">6</span>,<span class="integer">10</span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</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().range(<span class="integer">0</span>,<span class="integer">3</span>) g.V().range(<span class="integer">1</span>,<span class="integer">3</span>) g.V().range(<span class="integer">1</span>, -<span class="integer">1</span>) g.V().repeat(both()).times(<span class="integer">1000000</span>).emit().range(<span class="integer">6</span>,<span class="integer">10</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>range()</code>-step can also be applied with <code>Scope.local</code>, in which case it operates on the incoming collection. For example, it is possible to produce a <code>Map<String, String></code> for each traversed path, but containing only the second property value (the "b" step).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-285" type="radio" name="radio-set-1729796988-285" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-285" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-286" type="radio" name="radio-set-1729796988-285" class="tab-selector-2" /> <label for="tab-1729796988-286" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).range(local,<span class="integer">1</span>,<span class="integer">2</span>) ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:vadas] ==>[<span class="key">b</span>:josh] ==>[<span class="key">b</span>:ripple] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop] ==>[<span class="key">b</span>:lop]</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>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).in().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).range(local,<span class="integer">1</span>,<span class="integer">2</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The next example uses the <a href="#the-crew-toy-graph">The Crew</a> toy data set. It produces a <code>List<String></code> containing the second and third location for each vertex.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-287" type="radio" name="radio-set-1729796988-287" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-287" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-288" type="radio" name="radio-set-1729796988-287" class="tab-selector-2" /> <label for="tab-1729796988-288" 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> g.V().valueMap().select(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).range(local, <span class="integer">1</span>, <span class="integer">3</span>) ==>[santa cruz,brussels] ==>[dulles,purcellville] ==>[baltimore,oakland] ==>[kaiserslautern,aachen]</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().valueMap().select(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).range(local, <span class="integer">1</span>, <span class="integer">3</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#range(long,long)"><code>range(long,long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#range(org.apache.tinkerpop.gremlin.process.traversal.Scope,long,long)"><code>range(Scope,long,long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="read-step">Read Step</h3> <div class="paragraph"> <p>The <code>read()</code>-step is not really a "step" but a step modulator in that it modifies the functionality of the <code>io()</code>-step. More specifically, it tells the <code>io()</code>-step that it is expected to use its configuration to read data from some location. Please see the <a href="#io-step">documentation</a> for <code>io()</code>-step for more complete details on usage.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/full/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#read()"><code>read()</code></a></p> </div> </div> <div class="sect2"> <h3 id="repeat-step">Repeat Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-fade.png" alt="gremlin fade" width="350"> </div> </div> <div class="paragraph"> <p>The <code>repeat()</code>-step (<strong>branch</strong>) is used for looping over a traversal given some break predicate. Below are some examples of <code>repeat()</code>-step in action.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-289" type="radio" name="radio-set-1729796988-289" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-289" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-290" type="radio" name="radio-set-1729796988-289" class="tab-selector-2" /> <label for="tab-1729796988-290" 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> g.V(<span class="integer">1</span>).repeat(out()).times(<span class="integer">2</span>).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[marko,josh,ripple] ==>[marko,josh,lop] gremlin> g.V().until(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">ripple</span><span class="delimiter">'</span></span>)). repeat(out()).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[marko,josh,ripple] ==>[josh,ripple] ==>[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.V(<span class="integer">1</span>).repeat(out()).times(<span class="integer">2</span>).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().until(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">ripple</span><span class="delimiter">'</span></span>)). repeat(out()).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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>do-while semantics stating to do <code>out()</code> 2 times.</p> </li> <li> <p>while-do semantics stating to break if the traverser is at a vertex named "ripple".</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> There are two modulators for <code>repeat()</code>: <code>until()</code> and <code>emit()</code>. If <code>until()</code> comes after <code>repeat()</code> it is do/while looping. If <code>until()</code> comes before <code>repeat()</code> it is while/do looping. If <code>emit()</code> is placed after <code>repeat()</code>, it is evaluated on the traversers leaving the repeat-traversal. If <code>emit()</code> is placed before <code>repeat()</code>, it is evaluated on the traversers prior to entering the repeat-traversal. </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>repeat()</code>-step also supports an "emit predicate", where the predicate for an empty argument <code>emit()</code> is <code>true</code> (i.e. <code>emit() == emit{true}</code>). With <code>emit()</code>, the traverser is split in two — the traverser exits the code block as well as continues back within the code block (assuming <code>until()</code> holds true).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-291" type="radio" name="radio-set-1729796988-291" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-291" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-292" type="radio" name="radio-set-1729796988-291" class="tab-selector-2" /> <label for="tab-1729796988-292" 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> g.V(<span class="integer">1</span>).repeat(out()).times(<span class="integer">2</span>).emit().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[marko,lop] ==>[marko,vadas] ==>[marko,josh] ==>[marko,josh,ripple] ==>[marko,josh,lop] gremlin> g.V(<span class="integer">1</span>).emit().repeat(out()).times(<span class="integer">2</span>).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[marko] ==>[marko,lop] ==>[marko,vadas] ==>[marko,josh] ==>[marko,josh,ripple] ==>[marko,josh,lop]</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>).repeat(out()).times(<span class="integer">2</span>).emit().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).emit().repeat(out()).times(<span class="integer">2</span>).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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>The <code>emit()</code> comes after <code>repeat()</code> and thus, emission happens after the <code>repeat()</code> traversal is executed. Thus, no one vertex paths exist.</p> </li> <li> <p>The <code>emit()</code> comes before <code>repeat()</code> and thus, emission happens prior to the <code>repeat()</code> traversal being executed. Thus, one vertex paths exist.</p> </li> </ol> </div> <div class="paragraph"> <p>The <code>emit()</code>-modulator can take an arbitrary predicate.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-293" type="radio" name="radio-set-1729796988-293" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-293" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-294" type="radio" name="radio-set-1729796988-293" class="tab-selector-2" /> <label for="tab-1729796988-294" 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> g.V(<span class="integer">1</span>).repeat(out()).times(<span class="integer">2</span>).emit(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</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>) ==>[marko,lop] ==>[marko,josh,ripple] ==>[marko,josh,lop]</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>).repeat(out()).times(<span class="integer">2</span>).emit(has(<span class="string"><span class="delimiter">'</span><span class="content">lang</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>)</code></pre> </div> </div> </div> </div> </section> <div class="imageblock"> <div class="content"> <img src="../images/repeat-step.png" alt="repeat step" width="500"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-295" type="radio" name="radio-set-1729796988-295" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-295" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-296" type="radio" name="radio-set-1729796988-295" class="tab-selector-2" /> <label for="tab-1729796988-296" 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> g.V(<span class="integer">1</span>).repeat(out()).times(<span class="integer">2</span>).emit().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[marko,lop] ==>[marko,vadas] ==>[marko,josh] ==>[marko,josh,ripple] ==>[marko,josh,lop]</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>).repeat(out()).times(<span class="integer">2</span>).emit().path().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 first time through the <code>repeat()</code>, the vertices lop, vadas, and josh are seen. Given that <code>loops==1</code>, the traverser repeats. However, because the emit-predicate is declared true, those vertices are emitted. The next time through <code>repeat()</code>, the vertices traversed are ripple and lop (Josh’s created projects, as lop and vadas have no out edges). Given that <code>loops==2</code>, the until-predicate fails and ripple and lop are emitted. Therefore, the traverser has seen the vertices: lop, vadas, josh, ripple, and lop.</p> </div> <div class="paragraph"> <p><code>repeat()</code>-steps may be nested inside each other or inside the <code>emit()</code> or <code>until()</code> predicates and they can also be 'named' by passing a string as the first parameter to <code>repeat()</code>. The loop counter of a named repeat step can be accessed within the looped context with <code>loops(loopName)</code> where <code>loopName</code> is the name set whe creating the <code>repeat()</code>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-297" type="radio" name="radio-set-1729796988-297" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-297" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-298" type="radio" name="radio-set-1729796988-297" class="tab-selector-2" /> <label for="tab-1729796988-298" 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> g.V(<span class="integer">1</span>). repeat(out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>)). until(repeat(out(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>)).emit(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">lop</span><span class="delimiter">"</span></span>))) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">4</span>] gremlin> g.V(<span class="integer">6</span>). repeat(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>, both(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).simplePath()). emit(repeat(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>, both(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). until(loops(<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>).where(loops(<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">b</span><span class="delimiter">'</span></span>))). hasId(<span class="integer">2</span>)).dedup() <span class="comment">//</span>// <b class="conum">(2)</b> ==>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(<span class="integer">1</span>). repeat(out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>)). until(repeat(out(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>)).emit(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">lop</span><span class="delimiter">"</span></span>))) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">6</span>). repeat(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>, both(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).simplePath()). emit(repeat(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>, both(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). until(loops(<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>).where(loops(<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">b</span><span class="delimiter">'</span></span>))). hasId(<span class="integer">2</span>)).dedup() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Starting from vertex 1, keep going taking outgoing 'knows' edges until the vertex was created by 'lop'.</p> </li> <li> <p>Starting from vertex 6, keep taking created edges in either direction until the vertex is same distance from vertex 2 over knows edges as it is from vertex 6 over created edges.</p> </li> </ol> </div> <div class="paragraph"> <p>Finally, note that both <code>emit()</code> and <code>until()</code> can take a traversal and in such, situations, the predicate is determined by <code>traversal.hasNext()</code>. A few examples are provided below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-299" type="radio" name="radio-set-1729796988-299" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-299" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-300" type="radio" name="radio-set-1729796988-299" class="tab-selector-2" /> <label for="tab-1729796988-300" 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> g.V(<span class="integer">1</span>).repeat(out()).until(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</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>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[marko,lop] ==>[marko,josh,ripple] ==>[marko,josh,lop] gremlin> g.V(<span class="integer">1</span>).emit(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)).repeat(out()).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[marko] ==>[marko,vadas] ==>[marko,josh] gremlin> g.V(<span class="integer">1</span>).repeat(out()).until(outE().count().is(<span class="integer">0</span>)).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[marko,lop] ==>[marko,vadas] ==>[marko,josh,ripple] ==>[marko,josh,lop]</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>).repeat(out()).until(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">software</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>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).emit(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>)).repeat(out()).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">1</span>).repeat(out()).until(outE().count().is(<span class="integer">0</span>)).path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Starting from vertex 1, keep taking outgoing edges until a software vertex is reached.</p> </li> <li> <p>Starting from vertex 1, and in an infinite loop, emit the vertex if it is a person and then traverser the outgoing edges.</p> </li> <li> <p>Starting from vertex 1, keep taking outgoing edges until a vertex is reached that has no more outgoing edges.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> The anonymous traversal of <code>emit()</code> and <code>until()</code> (not <code>repeat()</code>) process their current objects "locally." In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversals do not leave the confines of the vertex’s star graph. In other words, they can not traverse to an adjacent vertex’s properties or edges. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#repeat(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>repeat(Traversal)</code></a></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/#looping"><code>Looping Recipes</code></a></p> </div> </div> <div class="sect2"> <h3 id="replace-step">Replace Step</h3> <div class="paragraph"> <p>The <code>replace()</code>-step (<strong>map</strong>) returns a string with the specified characters in the original string replaced with the new characters. Any null arguments will be a no-op and the original string is returned. Null values from the incoming traversers are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-301" type="radio" name="radio-set-1729796988-301" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-301" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-302" type="radio" name="radio-set-1729796988-301" class="tab-selector-2" /> <label for="tab-1729796988-302" 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> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">that</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">this</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">test</span><span class="delimiter">'</span></span>, <span class="predefined-constant">null</span>).replace(<span class="string"><span class="delimiter">'</span><span class="content">h</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">j</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>tjat ==>tjis ==>test ==><span class="predefined-constant">null</span> gremlin> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">hello world</span><span class="delimiter">'</span></span>).replace(<span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">'</span><span class="content">j</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>hello world gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).replace(<span class="string"><span class="delimiter">"</span><span class="content">p</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>log ==>riggle gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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().replace(local, <span class="string"><span class="delimiter">"</span><span class="content">p</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==>[log,riggle]</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.inject(<span class="string"><span class="delimiter">'</span><span class="content">that</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">this</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">test</span><span class="delimiter">'</span></span>, <span class="predefined-constant">null</span>).replace(<span class="string"><span class="delimiter">'</span><span class="content">h</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">j</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.inject(<span class="string"><span class="delimiter">'</span><span class="content">hello world</span><span class="delimiter">'</span></span>).replace(<span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">'</span><span class="content">j</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).replace(<span class="string"><span class="delimiter">"</span><span class="content">p</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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().replace(local, <span class="string"><span class="delimiter">"</span><span class="content">p</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">g</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>Replace "h" in the strings with "j".</p> </li> <li> <p>Null inputs are ignored and the original string is returned.</p> </li> <li> <p>Return software names with "p" replaced by "g".</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#replace(java.lang.String,java.lang.String)"><code>replace(String,String)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#replace(org.apache.tinkerpop.gremlin.process.traversal.Scope,java.lang.String,java.lang.String)"><code>replace(Scope,String,String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="reverse-step">Reverse Step</h3> <div class="paragraph"> <p>The <code>reverse()</code>-step (<strong>map</strong>) returns the reverse of the incoming list traverser. Single values (including <code>null</code>) are not processed and are added back to the Traversal Stream unchanged. If the incoming traverser is a String value then the reversed String will be returned.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-303" type="radio" name="radio-set-1729796988-303" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-303" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-304" type="radio" name="radio-set-1729796988-303" class="tab-selector-2" /> <label for="tab-1729796988-304" 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> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).reverse() <span class="comment">//</span>// <b class="conum">(1)</b> ==>okram ==>sadav ==>pol ==>hsoj ==>elppir ==>retep gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).order().fold().reverse() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[vadas,ripple,peter,marko,lop,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().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).reverse() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).order().fold().reverse() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Reverse the order of the characters in each name.</p> </li> <li> <p>Fold all the names into a list in ascending order and then reverse the list’s ordering (into descending).</p> </li> </ol> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#reverse()"><code>reverse()</code></a></p> </div> </div> <div class="sect2"> <h3 id="rTrim-step">RTrim Step</h3> <div class="paragraph"> <p>The <code>rTrim()</code>-step (<strong>map</strong>) returns a string with trailing whitespace removed. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-305" type="radio" name="radio-set-1729796988-305" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-305" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-306" type="radio" name="radio-set-1729796988-305" class="tab-selector-2" /> <label for="tab-1729796988-306" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).rTrim() ==> hello ==> world ==><span class="predefined-constant">null</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).rTrim(local) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[ hello, world,<span class="predefined-constant">null</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.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).rTrim() g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).rTrim(local) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#rTrim()"><code>rTrim()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#rTrim(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>rTrim(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="sack-step">Sack Step</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-sacks-running.png" alt="gremlin sacks running" width="175"></span> A traverser can contain a local data structure called a "sack". The <code>sack()</code>-step is used to read and write sacks (<strong>sideEffect</strong> or <strong>map</strong>). Each sack of each traverser is created when using <code>GraphTraversal.withSack(initialValueSupplier,splitOperator?,mergeOperator?)</code>.</p> </div> <div class="ulist"> <ul> <li> <p><strong>Initial value supplier</strong>: A <code>Supplier</code> providing the initial value of each traverser’s sack.</p> </li> <li> <p><strong>Split operator</strong>: a <code>UnaryOperator</code> that clones the traverser’s sack when the traverser splits. If no split operator is provided, then <code>UnaryOperator.identity()</code> is assumed.</p> </li> <li> <p><strong>Merge operator</strong>: A <code>BinaryOperator</code> that unites two traverser’s sack when they are merged. If no merge operator is provided, then traversers with sacks can not be merged.</p> </li> </ul> </div> <div class="paragraph"> <p>Two trivial examples are presented below to demonstrate the <strong>initial value supplier</strong>. In the first example below, a traverser is created at each vertex in the graph (<code>g.V()</code>), with a 1.0 sack (<code>withSack(1.0f)</code>), and then the sack value is accessed (<code>sack()</code>). In the second example, a random float supplier is used to generate sack values.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-307" type="radio" name="radio-set-1729796988-307" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-307" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-308" type="radio" name="radio-set-1729796988-307" class="tab-selector-2" /> <label for="tab-1729796988-308" 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> g.withSack(<span class="float">1.0f</span>).V().sack() ==><span class="float">1.0</span> ==><span class="float">1.0</span> ==><span class="float">1.0</span> ==><span class="float">1.0</span> ==><span class="float">1.0</span> ==><span class="float">1.0</span> gremlin> rand = <span class="keyword">new</span> <span class="predefined-type">Random</span>() ==>java.util.Random<span class="error">@</span><span class="float">5d</span><span class="float">6125d</span><span class="integer">8</span> gremlin> g.withSack {rand.nextFloat()}.V().sack() ==><span class="float">0.30364954</span> ==><span class="float">0.6187649</span> ==><span class="float">0.046979964</span> ==><span class="float">0.2031619</span> ==><span class="float">0.33781552</span> ==><span class="float">0.66723937</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">1.0f</span>).V().sack() rand = <span class="keyword">new</span> <span class="predefined-type">Random</span>() g.withSack {rand.nextFloat()}.V().sack()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>A more complicated initial value supplier example is presented below where the sack values are used in a running computation and then emitted at the end of the traversal. When an edge is traversed, the edge weight is multiplied by the sack value (<code>sack(mult).by('weight')</code>). Note that the <a href="#by-step"><code>by()</code></a>-modulator can be any arbitrary traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-309" type="radio" name="radio-set-1729796988-309" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-309" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-310" type="radio" name="radio-set-1729796988-309" class="tab-selector-2" /> <label for="tab-1729796988-310" 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> g.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>) ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>).sack() ==><span class="float">1.0</span> ==><span class="float">0.4</span> gremlin> g.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>).path(). by().by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) ==>[v[<span class="integer">1</span>],<span class="float">1.0</span>,v[<span class="integer">4</span>],<span class="float">1.0</span>,v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],<span class="float">1.0</span>,v[<span class="integer">4</span>],<span class="float">0.4</span>,v[<span class="integer">3</span>]] gremlin> g.V().sack(assign).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sack() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">29</span> ==><span class="integer">27</span> ==><span class="integer">32</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.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>) g.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>).sack() g.withSack(<span class="float">1.0f</span>).V().repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inV()).times(<span class="integer">2</span>).path(). by().by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) g.V().sack(assign).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sack() <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered during the assignment.</p> </li> </ol> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-sacks-standing.png" alt="gremlin sacks standing" width="100"></span> When complex objects are used (i.e. non-primitives), then a <strong>split operator</strong> should be defined to ensure that each traverser gets a clone of its parent’s sack. The first example does not use a split operator and as such, the same map is propagated to all traversers (a global data structure). The second example, demonstrates how <code>Map.clone()</code> ensures that each traverser’s sack contains a unique, local sack.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-311" type="radio" name="radio-set-1729796988-311" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-311" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-312" type="radio" name="radio-set-1729796988-311" class="tab-selector-2" /> <label for="tab-1729796988-312" 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> g.withSack {[:]}.V().out().out(). sack {m,v -> m[v.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)] = v.value(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>); m}.sack() <span class="comment">// BAD: single map</span> ==>[<span class="key">ripple</span>:java] ==>[<span class="key">ripple</span>:java,<span class="key">lop</span>:java] gremlin> g.withSack {[:]}{<span class="local-variable">it</span>.clone()}.V().out().out(). sack {m,v -> m[v.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)] = v.value(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>); m}.sack() <span class="comment">// GOOD: cloned map</span> ==>[<span class="key">ripple</span>:java] ==>[<span class="key">lop</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.withSack {[:]}.V().out().out(). sack {m,v -> m[v.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)] = v.value(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>); m}.sack() <span class="comment">// BAD: single map</span> g.withSack {[:]}{<span class="local-variable">it</span>.clone()}.V().out().out(). sack {m,v -> m[v.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)] = v.value(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>); m}.sack() <span class="comment">// GOOD: cloned map</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"> For primitives (i.e. integers, longs, floats, etc.), a split operator is not required as a primitives are encoded in the memory address of the sack, not as a reference to an object. </td> </tr> </table> </div> <div class="paragraph"> <p>If a <strong>merge operator</strong> is not provided, then traversers with sacks can not be bulked. However, in many situations, merging the sacks of two traversers at the same location is algorithmically sound and good to provide so as to gain the bulking optimization. In the examples below, the binary merge operator is <code>Operator.sum</code>. Thus, when two traverser merge, their respective sacks are added together.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-313" type="radio" name="radio-set-1729796988-313" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-313" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-314" type="radio" name="radio-set-1729796988-313" class="tab-selector-2" /> <label for="tab-1729796988-314" 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> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).sack() <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="float">1.0</span> ==><span class="float">1.0</span> gremlin> g.withSack(<span class="float">1.0d</span>, sum).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).sack() <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="float">2.0</span> ==><span class="float">2.0</span> gremlin> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier() <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(5)</b> ==><span class="float">0.5</span> ==><span class="float">0.5</span> gremlin> g.withSack(<span class="float">1.0d</span>,sum).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(6)</b> ==><span class="float">1.0</span> ==><span class="float">1.0</span> gremlin> g.withBulk(<span class="predefined-constant">false</span>).withSack(<span class="float">1.0f</span>,sum).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(7)</b> ==><span class="float">1.0</span> gremlin> g.withBulk(<span class="predefined-constant">false</span>).withSack(<span class="float">1.0f</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(8)</b> ==><span class="float">0.5</span> ==><span class="float">0.5</span> gremlin></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">1.0d</span>).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).sack() <span class="comment">//</span>// <b class="conum">(2)</b> g.withSack(<span class="float">1.0d</span>, sum).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).sack() <span class="comment">//</span>// <b class="conum">(3)</b> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier() <span class="comment">//</span>// <b class="conum">(4)</b> g.withSack(<span class="float">1.0d</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(5)</b> g.withSack(<span class="float">1.0d</span>,sum).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(6)</b> g.withBulk(<span class="predefined-constant">false</span>).withSack(<span class="float">1.0f</span>,sum).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(7)</b> g.withBulk(<span class="predefined-constant">false</span>).withSack(<span class="float">1.0f</span>).V(<span class="integer">1</span>).local(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier(normSack).inV()).in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).barrier().sack() <span class="comment">//</span>// <b class="conum">(8)</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>We find vertex 1 twice because he knows two other people</p> </li> <li> <p>Without a merge operation the sack values are 1.0.</p> </li> <li> <p>When specifying <code>sum</code> as the merge operation, the sack values are 2.0 because of bulking</p> </li> <li> <p>Like 1, but using barrier internally</p> </li> <li> <p>The <code>local(…​barrier(normSack)…​)</code> ensures that all traversers leaving vertex 1 have an evenly distributed amount of the initial 1.0 "energy" (50-50), i.e. the sack is 0.5 on each result</p> </li> <li> <p>Like 3, but using <code>sum</code> as merge operator leads to the expected 1.0</p> </li> <li> <p>There is now a single traverser with bulk of 2 and sack of 1.0 and thus, setting <code>withBulk(false)`</code> yields the expected 1.0</p> </li> <li> <p>Like 7, but without the <code>sum</code> operator</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sack()"><code>sack()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sack(java.util.function.BiFunction)"><code>sack(BiFunction)</code></a></p> </div> </div> <div class="sect2"> <h3 id="sample-step">Sample Step</h3> <div class="paragraph"> <p>The <code>sample()</code>-step is useful for sampling some number of traversers previous in the traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-315" type="radio" name="radio-set-1729796988-315" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-315" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-316" type="radio" name="radio-set-1729796988-315" class="tab-selector-2" /> <label for="tab-1729796988-316" 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> g.V().outE().sample(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) ==><span class="float">0.4</span> gremlin> g.V().outE().sample(<span class="integer">1</span>).by(<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>) ==><span class="float">0.2</span> gremlin> g.V().outE().sample(<span class="integer">2</span>).by(<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>) ==><span class="float">1.0</span> ==><span class="float">0.5</span> gremlin> g.V().both().sample(<span class="integer">2</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">(1)</b> ==>v[<span class="integer">2</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.V().outE().sample(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) g.V().outE().sample(<span class="integer">1</span>).by(<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>) g.V().outE().sample(<span class="integer">2</span>).by(<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>) g.V().both().sample(<span class="integer">2</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are not considered when sampling.</p> </li> </ol> </div> <div class="paragraph"> <p>One of the more interesting use cases for <code>sample()</code> is when it is used in conjunction with <a href="#local-step"><code>local()</code></a>. The combination of the two steps supports the execution of <a href="http://en.wikipedia.org/wiki/Random_walk">random walks</a>. In the example below, the traversal starts are vertex 1 and selects one edge to traverse based on a probability distribution generated by the weights of the edges. The output is always a single path as by selecting a single edge, the traverser never splits and continues down a single path in the graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-317" type="radio" name="radio-set-1729796988-317" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-317" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-318" type="radio" name="radio-set-1729796988-317" class="tab-selector-2" /> <label for="tab-1729796988-318" 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> g.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">5</span>) ==>v[<span class="integer">4</span>] gremlin> g.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">5</span>). path() ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] gremlin> g.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">10</span>). path() ==>[v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><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.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">5</span>) g.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">5</span>). path() g.V(<span class="integer">1</span>). repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())). times(<span class="integer">10</span>). path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>As a clarification, note that in the above example <code>local()</code> is not strictly required as it only does the random walk over a single vertex, but note what happens without it if multiple vertices are traversed:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-319" type="radio" name="radio-set-1729796988-319" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-319" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-320" type="radio" name="radio-set-1729796988-319" class="tab-selector-2" /> <label for="tab-1729796988-320" 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> g.V().repeat(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV()).times(<span class="integer">5</span>).path() ==>[v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<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().repeat(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV()).times(<span class="integer">5</span>).path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The use of <code>local()</code> ensures that the traversal over <code>bothE()</code> occurs once per vertex traverser that passes through, thus allowing one random walk per vertex.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-321" type="radio" name="radio-set-1729796988-321" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-321" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-322" type="radio" name="radio-set-1729796988-321" class="tab-selector-2" /> <label for="tab-1729796988-322" 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> g.V().repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())).times(<span class="integer">5</span>).path() ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<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().repeat(local(bothE().sample(<span class="integer">1</span>).by(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).otherV())).times(<span class="integer">5</span>).path()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>So, while not strictly required, it is likely better to be explicit with the use of <code>local()</code> so that the proper intent of the traversal is expressed.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sample(int)"><code>sample(int)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sample(org.apache.tinkerpop.gremlin.process.traversal.Scope,int)"><code>sample(Scope,int)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="select-step">Select Step</h3> <div class="paragraph"> <p><a href="http://en.wikipedia.org/wiki/Functional_programming">Functional languages</a> make use of function composition and lazy evaluation to create complex computations from primitive operations. This is exactly what <code>Traversal</code> does. One of the differentiating aspects of Gremlin’s data flow approach to graph processing is that the flow need not always go "forward," but in fact, can go back to a previously seen area of computation. Examples include <a href="#path-step"><code>path()</code></a> as well as the <code>select()</code>-step (<strong>map</strong>). There are two general ways to use <code>select()</code>-step.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Select labeled steps within a path (as defined by <code>as()</code> in a traversal).</p> </li> <li> <p>Select objects out of a <code>Map<String,Object></code> flow (i.e. a sub-map).</p> </li> </ol> </div> <div class="paragraph"> <p>The first use case is demonstrated via example below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-323" type="radio" name="radio-set-1729796988-323" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-323" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-324" type="radio" name="radio-set-1729796988-323" class="tab-selector-2" /> <label for="tab-1729796988-324" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>) <span class="comment">// no select</span> ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>) ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">4</span>],<span class="key">c</span>:v[<span class="integer">5</span>]] ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">4</span>],<span class="key">c</span>:v[<span class="integer">3</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">4</span>]] ==>[<span class="key">a</span>:v[<span class="integer">1</span>],<span class="key">b</span>:v[<span class="integer">4</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==>[<span class="key">a</span>:<span class="integer">29</span>,<span class="key">b</span>:<span class="integer">27</span>] ==>[<span class="key">a</span>:<span class="integer">29</span>,<span class="key">b</span>:<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().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>) <span class="comment">// no select</span> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</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="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).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="colist arabic"> <ol> <li> <p>If the selection is one step, no map is returned.</p> </li> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>When there is only one label selected, then a single object is returned. This is useful for stepping back in a computation and easily moving forward again on the object reverted to.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-325" type="radio" name="radio-set-1729796988-325" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-325" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-326" type="radio" name="radio-set-1729796988-325" class="tab-selector-2" /> <label for="tab-1729796988-326" 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> g.V().out().out() ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V().out().out().path() ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).out().out().select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).out().select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">4</span>] ==>v[<span class="integer">4</span>] gremlin> g.V().out().out().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">x</span><span class="delimiter">'</span></span>) <span class="comment">// pointless</span> ==>v[<span class="integer">5</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() g.V().out().out().path() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).out().out().select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>).out().select(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>) g.V().out().out().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">x</span><span class="delimiter">'</span></span>) <span class="comment">// pointless</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"> When executing a traversal with <code>select()</code> on a standard traversal engine (i.e. OLTP), <code>select()</code> will do its best to avoid calculating the path history and instead, will rely on a global data structure for storing the currently selected object. As such, if only a subset of the path walked is required, <code>select()</code> should be used over the more resource intensive <a href="#path-step"><code>path()</code></a>-step. </td> </tr> </table> </div> <div class="paragraph"> <p>When the set of keys or values (i.e. columns) of a path or map are needed, use <code>select(keys)</code> and <code>select(values)</code>, respectively. This is especially useful when one is only interested in the top N elements in a <code>groupCount()</code> ranking.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-327" type="radio" name="radio-set-1729796988-327" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-327" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-328" type="radio" name="radio-set-1729796988-327" class="tab-selector-2" /> <label for="tab-1729796988-328" 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> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>) ==>[PLAYING IN THE <span class="key">BAND</span>:<span class="integer">107</span>,JACK <span class="key">STRAW</span>:<span class="integer">99</span>,<span class="key">TRUCKING</span>:<span class="integer">94</span>,<span class="key">DRUMS</span>:<span class="integer">92</span>,ME AND MY <span class="key">UNCLE</span>:<span class="integer">86</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>).select(keys) ==>[PLAYING IN THE BAND,JACK STRAW,TRUCKING,DRUMS,ME AND MY UNCLE] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>).select(keys).unfold() ==>PLAYING IN THE BAND ==>JACK STRAW ==>TRUCKING ==>DRUMS ==>ME AND MY UNCLE</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) 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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>).select(keys) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">followedBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). order(local).by(values,desc).limit(local, <span class="integer">5</span>).select(keys).unfold()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Similarly, for extracting the values from a path or map.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-329" type="radio" name="radio-set-1729796988-329" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-329" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-330" type="radio" name="radio-set-1729796988-329" class="tab-selector-2" /> <label for="tab-1729796988-330" 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> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">All</span>:<span class="integer">9</span>,<span class="key">Weir_Garcia</span>:<span class="integer">1</span>,<span class="key">Lesh</span>:<span class="integer">19</span>,<span class="key">Weir_Kreutzmann</span>:<span class="integer">1</span>,<span class="key">Pigpen_Garcia</span>:<span class="integer">1</span>,<span class="key">Pigpen</span>:<span class="integer">36</span>,<span class="key">Unknown</span>:<span class="integer">6</span>,<span class="key">Weir_Bralove</span>:<span class="integer">1</span>,<span class="key">Joan_Baez</span>:<span class="integer">10</span>,<span class="key">Suzanne_Vega</span>:<span class="integer">2</span>,<span class="key">Welnick</span>:<span class="integer">10</span>,<span class="key">Lesh_Pigpen</span>:<span class="integer">1</span>,<span class="key">Elvin_Bishop</span>:<span class="integer">4</span>,<span class="key">Neil_Young</span>:<span class="integer">1</span>,<span class="key">Garcia_Weir_Lesh</span>:<span class="integer">1</span>,<span class="key">Hunter</span>:<span class="integer">3</span>,<span class="key">Hornsby</span>:<span class="integer">4</span>,<span class="key">Jon_Hendricks</span>:<span class="integer">2</span>,<span class="key">Weir_Hart</span>:<span class="integer">3</span>,<span class="key">Lesh_Mydland</span>:<span class="integer">1</span>,<span class="key">Mydland_Lesh</span>:<span class="integer">1</span>,<span class="key">instrumental</span>:<span class="integer">1</span>,<span class="key">Garcia</span>:<span class="integer">146</span>,<span class="key">Hart</span>:<span class="integer">2</span>,<span class="key">Welnick_Bralove</span>:<span class="integer">1</span>,<span class="key">Weir</span>:<span class="integer">99</span>,<span class="key">Garcia_Dawson</span>:<span class="integer">1</span>,<span class="key">Pigpen_Weir_Mydland</span>:<span class="integer">2</span>,<span class="key">Jorma_Kaukonen</span>:<span class="integer">4</span>,<span class="key">Joey_Covington</span>:<span class="integer">2</span>,<span class="key">Allman_Brothers</span>:<span class="integer">1</span>,<span class="key">Garcia_Lesh</span>:<span class="integer">3</span>,<span class="key">Boz_Scaggs</span>:<span class="integer">1</span>,Pigpen?:<span class="integer">1</span>,<span class="key">Keith_Godchaux</span>:<span class="integer">1</span>,<span class="key">Etta_James</span>:<span class="integer">1</span>,<span class="key">Weir_Wasserman</span>:<span class="integer">1</span>,<span class="key">Hall_and_Oates</span>:<span class="integer">2</span>,<span class="key">Grateful_Dead</span>:<span class="integer">17</span>,<span class="key">Spencer_Davis</span>:<span class="integer">2</span>,<span class="key">Pigpen_Mydland</span>:<span class="integer">3</span>,<span class="key">Beach_Boys</span>:<span class="integer">3</span>,<span class="key">Donna</span>:<span class="integer">4</span>,<span class="key">Bo_Diddley</span>:<span class="integer">7</span>,<span class="key">Bob_Dylan</span>:<span class="integer">22</span>,<span class="key">Hart_Kreutzmann</span>:<span class="integer">2</span>,<span class="key">Weir_Mydland</span>:<span class="integer">3</span>,<span class="key">Lesh_Hart_Kreutzmann</span>:<span class="integer">1</span>,<span class="key">Stephen_Stills</span>:<span class="integer">2</span>,<span class="key">Mydland</span>:<span class="integer">18</span>,<span class="key">Neville_Brothers</span>:<span class="integer">2</span>,<span class="key">Weir_Hart_Welnick</span>:<span class="integer">1</span>,<span class="key">Garcia_Lesh_Weir</span>:<span class="integer">1</span>,<span class="key">Garcia_Weir</span>:<span class="integer">3</span>,<span class="key">Neal_Cassady</span>:<span class="integer">1</span>,<span class="key">John_Fogerty</span>:<span class="integer">5</span>,<span class="key">Donna_Godchaux</span>:<span class="integer">2</span>,<span class="key">Pigpen_Weir</span>:<span class="integer">8</span>,<span class="key">Garcia_Kreutzmann</span>:<span class="integer">2</span>,<span class="key">None</span>:<span class="integer">6</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).select(values) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="integer">9</span>,<span class="integer">1</span>,<span class="integer">19</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">36</span>,<span class="integer">6</span>,<span class="integer">1</span>,<span class="integer">10</span>,<span class="integer">2</span>,<span class="integer">10</span>,<span class="integer">1</span>,<span class="integer">4</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">3</span>,<span class="integer">4</span>,<span class="integer">2</span>,<span class="integer">3</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">146</span>,<span class="integer">2</span>,<span class="integer">1</span>,<span class="integer">99</span>,<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">4</span>,<span class="integer">2</span>,<span class="integer">1</span>,<span class="integer">3</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">17</span>,<span class="integer">2</span>,<span class="integer">3</span>,<span class="integer">3</span>,<span class="integer">4</span>,<span class="integer">7</span>,<span class="integer">22</span>,<span class="integer">2</span>,<span class="integer">3</span>,<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">18</span>,<span class="integer">2</span>,<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">3</span>,<span class="integer">1</span>,<span class="integer">5</span>,<span class="integer">2</span>,<span class="integer">8</span>,<span class="integer">2</span>,<span class="integer">6</span>] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).select(values).unfold(). groupCount().order(local).by(values,desc).limit(local, <span class="integer">5</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="integer">1</span>:<span class="integer">22</span>,<span class="integer">2</span>:<span class="integer">12</span>,<span class="integer">3</span>:<span class="integer">7</span>,<span class="integer">4</span>:<span class="integer">4</span>,<span class="integer">6</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 = traversal().withEmbedded(graph) 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().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).select(values) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">song</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">sungBy</span><span class="delimiter">'</span></span>).groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).select(values).unfold(). groupCount().order(local).by(values,desc).limit(local, <span class="integer">5</span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Which artist sung how many songs?</p> </li> <li> <p>Get an anonymized set of song repertoire sizes.</p> </li> <li> <p>What are the 5 most common song repertoire sizes?</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Note that <code>by()</code>-modulation is not supported with <code>select(keys)</code> and <code>select(values)</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>There is also an option to supply a <code>Pop</code> operation to <code>select()</code> to manipulate <code>List</code> objects in the <code>Traverser</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-331" type="radio" name="radio-set-1729796988-331" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-331" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-332" type="radio" name="radio-set-1729796988-331" class="tab-selector-2" /> <label for="tab-1729796988-332" 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> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(first, <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(last, <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(all, <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[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(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(first, <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(last, <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).repeat(out().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>)).times(<span class="integer">2</span>).select(all, <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>In addition to the previously shown examples, where <code>select()</code> was used to select an element based on a static key, <code>select()</code> can also accept a traversal that emits a key.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Since the key used by <code>select(<traversal>)</code> cannot be determined at compile time, the <code>TraversalSelectStep</code> enables full path tracking. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-333" type="radio" name="radio-set-1729796988-333" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-333" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-334" type="radio" name="radio-set-1729796988-333" class="tab-selector-2" /> <label for="tab-1729796988-334" 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> g.withSideEffect(<span class="string"><span class="delimiter">"</span><span class="content">alias</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="string"><span class="delimiter">"</span><span class="content">okram</span><span class="delimiter">"</span></span>]).V(). <span class="comment">//</span>// <b class="conum">(1)</b> values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).sack(assign). <span class="comment">//</span>// <b class="conum">(2)</b> optional(select(<span class="string"><span class="delimiter">"</span><span class="content">alias</span><span class="delimiter">"</span></span>).select(sack())) <span class="comment">//</span>// <b class="conum">(3)</b> ==>okram ==>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.withSideEffect(<span class="string"><span class="delimiter">"</span><span class="content">alias</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="string"><span class="delimiter">"</span><span class="content">okram</span><span class="delimiter">"</span></span>]).V(). <span class="comment">//</span>// <b class="conum">(1)</b> values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).sack(assign). <span class="comment">//</span>// <b class="conum">(2)</b> optional(select(<span class="string"><span class="delimiter">"</span><span class="content">alias</span><span class="delimiter">"</span></span>).select(sack())) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Inject a name alias map and start the traversal from all vertices.</p> </li> <li> <p>Select all <code>name</code> values and store them as the current traverser’s sack value.</p> </li> <li> <p>Optionally select the alias for the current name from the injected map.</p> </li> </ol> </div> <div class="sect3"> <h4 id="using-where-with-select">Using Where with Select</h4> <div class="paragraph"> <p>Like <a href="#match-step"><code>match()</code></a>-step, it is possible to use <code>where()</code>, as where is a filter that processes <code>Map<String,Object></code> streams.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-335" type="radio" name="radio-set-1729796988-335" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-335" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-336" type="radio" name="radio-set-1729796988-335" class="tab-selector-2" /> <label for="tab-1729796988-336" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:peter] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:peter] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:peter] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:peter] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:josh,<span class="key">b</span>:peter] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:marko] ==>[<span class="key">a</span>:peter,<span class="key">b</span>:josh] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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="comment">//</span>// <b class="conum">(3)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[<span class="key">a</span>:marko,<span class="key">b</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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<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">b</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="comment">//</span>// <b class="conum">(3)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</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>).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="colist arabic"> <ol> <li> <p>A standard <code>select()</code> that generates a <code>Map<String,Object></code> of variables bindings in the path (i.e. <code>a</code> and <code>b</code>) for the sake of a running example.</p> </li> <li> <p>The <code>select().by('name')</code> projects each binding vertex to their name property value and <code>where()</code> operates to ensure respective <code>a</code> and <code>b</code> strings are not the same.</p> </li> <li> <p>The first <code>select()</code> projects a vertex binding set. A binding is filtered if <code>a</code> vertex equals <code>b</code> vertex. A binding is filtered if <code>a</code> doesn’t know <code>b</code>. The second and final <code>select()</code> projects the name of the vertices.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(org.apache.tinkerpop.gremlin.structure.Column)"><code>select(Column)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(org.apache.tinkerpop.gremlin.process.traversal.Pop,java.lang.String)"><code>select(Pop,String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(java.lang.String)"><code>select(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(java.lang.String,java.lang.String,java.lang.String...)"><code>select(String,String,String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>select(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#select(org.apache.tinkerpop.gremlin.process.traversal.Pop,org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>select(Pop,Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/structure/Column.html"><code>Column</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Pop.html"><code>Pop</code></a></p> </div> </div> </div> <div class="sect2"> <h3 id="shortestpath-step">ShortestPath step</h3> <div class="paragraph"> <p>The <code>shortestPath()</code>-step provides an easy way to find shortest non-cyclic paths in a graph. It is configurable using the <code>with()</code>-modulator with the options given below.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>shortestPath()</code>-step is a <code>VertexComputing</code>-step and as such, can only be used against a graph that supports <code>GraphComputer</code> (OLAP). </td> </tr> </table> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 11.5384%;"> <col style="width: 11.5384%;"> <col style="width: 57.6923%;"> <col style="width: 19.2309%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>target</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Traversal</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets a filter traversal for the end vertices (e.g. <code>__.has('name','marko')</code>).</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">all vertices (<code>__.identity()</code>)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>edges</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Traversal</code> or <code>Direction</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets a <code>Traversal</code> that emits the edges to traverse from the current vertex or the <code>Direction</code> to traverse during the shortest path discovery.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Direction.BOTH</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>distance</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Traversal</code> or <code>String</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the <code>Traversal</code> that calculates the distance for the current edge or the name of an edge property to use for the distance calculations.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>__.constant(1)</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>maxDistance</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Number</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the distance limit for all shortest paths.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">none</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeEdges</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Boolean</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Whether to include edges in the result or not.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td> </tr> </tbody> </table> <section class="tabs tabs-2"> <input id="tab-1729796988-337" type="radio" name="radio-set-1729796988-337" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-337" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-338" type="radio" name="radio-set-1729796988-337" class="tab-selector-2" /> <label for="tab-1729796988-338" 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> g = g.withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().shortestPath() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">4</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">2</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">5</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">3</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">1</span>]] ==>[v[<span class="integer">6</span>]] ==>[v[<span class="integer">6</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>],v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>]] gremlin> 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>).shortestPath() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>]] gremlin> g.V().shortestPath().with(ShortestPath.target, __.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>)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">6</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] gremlin> g.V().shortestPath(). with(ShortestPath.edges, Direction.IN). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(4)</b> ==>[v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">4</span>]] gremlin> 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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(5)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>]] gremlin> 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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(6)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>]] gremlin> 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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)). with(ShortestPath.includeEdges, <span class="predefined-constant">true</span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</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 = g.withComputer() g.V().shortestPath() <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>).shortestPath() <span class="comment">//</span>// <b class="conum">(2)</b> g.V().shortestPath().with(ShortestPath.target, __.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>)) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().shortestPath(). with(ShortestPath.edges, Direction.IN). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(4)</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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(5)</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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)). with(ShortestPath.distance, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(6)</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>). shortestPath(). with(ShortestPath.target, __.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">josh</span><span class="delimiter">'</span></span>)). with(ShortestPath.includeEdges, <span class="predefined-constant">true</span>) <span class="invisible">//</span><b class="conum">7</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find all shortest paths.</p> </li> <li> <p>Find all shortest paths from <code>marko</code>.</p> </li> <li> <p>Find all shortest paths to <code>peter</code>.</p> </li> <li> <p>Find all in-directed paths to <code>josh</code>.</p> </li> <li> <p>Find all shortest paths from <code>marko</code> to <code>josh</code>.</p> </li> <li> <p>Find all shortest paths from <code>marko</code> to <code>josh</code> using a custom distance property.</p> </li> <li> <p>Find all shortest paths from <code>marko</code> to <code>josh</code> and include edges in the result.</p> </li> </ol> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-339" type="radio" name="radio-set-1729796988-339" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-339" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-340" type="radio" name="radio-set-1729796988-339" class="tab-selector-2" /> <label for="tab-1729796988-340" 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> g.inject(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>). with(ShortestPath.maxDistance, <span class="integer">1</span>).toList().toArray()). map(unfold().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()) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[marko,<span class="float">0.4</span>,lop] ==>[marko] ==>[marko,<span class="float">0.4</span>,lop,<span class="float">0.4</span>,josh] ==>[marko,<span class="float">0.5</span>,vadas] ==>[marko,<span class="float">0.4</span>,lop,<span class="float">0.2</span>,peter] ==>[vadas,<span class="float">0.5</span>,marko,<span class="float">0.4</span>,lop] ==>[vadas,<span class="float">0.5</span>,marko] ==>[vadas] ==>[lop] ==>[lop,<span class="float">0.4</span>,marko] ==>[lop,<span class="float">0.4</span>,josh] ==>[lop,<span class="float">0.4</span>,marko,<span class="float">0.5</span>,vadas] ==>[lop,<span class="float">0.2</span>,peter] ==>[ripple,<span class="float">1.0</span>,josh] ==>[ripple] ==>[josh,<span class="float">0.4</span>,lop] ==>[josh,<span class="float">0.4</span>,lop,<span class="float">0.4</span>,marko] ==>[josh] ==>[josh,<span class="float">0.4</span>,lop,<span class="float">0.2</span>,peter] ==>[josh,<span class="float">1.0</span>,ripple] ==>[peter,<span class="float">0.2</span>,lop] ==>[peter,<span class="float">0.2</span>,lop,<span class="float">0.4</span>,marko] ==>[peter,<span class="float">0.2</span>,lop,<span class="float">0.4</span>,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.inject(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>). with(ShortestPath.maxDistance, <span class="integer">1</span>).toList().toArray()). map(unfold().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()) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find all shortest paths using a custom distance property and limit the distance to 1. Inject the result into a OLTP <code>GraphTraversal</code> in order to be able to select properties from all elements in all paths.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#shortestPath()"><code>shortestPath()</code></a></p> </div> </div> <div class="sect2"> <h3 id="sideeffect-step">SideEffect Step</h3> <div class="paragraph"> <p>The <code>sideEffect()</code> step performs some operation on the traverser and passes it to the next step in the process. Please see the <a href="#general-steps">General Steps</a> section for more information.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sideEffect(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>map(Traversal)</code></a></p> </div> </div> <div class="sect2"> <h3 id="simplepath-step">SimplePath Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/simplepath-step.png" alt="simplepath step" width="400"> </div> </div> <div class="paragraph"> <p>When it is important that a traverser not repeat its path through the graph, <code>simplePath()</code>-step should be used (<strong>filter</strong>). The <a href="#path-data-structure">path</a> information of the traverser is analyzed and if the path has repeated objects in it, the traverser is filtered. If cyclic behavior is desired, see <a href="#cyclicpath-step"><code>cyclicPath()</code></a>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-341" type="radio" name="radio-set-1729796988-341" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-341" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-342" type="radio" name="radio-set-1729796988-341" class="tab-selector-2" /> <label for="tab-1729796988-342" 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> g.V(<span class="integer">1</span>).both().both() ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">1</span>).both().both().simplePath() ==>v[<span class="integer">4</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">1</span>).both().both().simplePath().path() ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]] gremlin> g.V(<span class="integer">1</span>).both().both().simplePath().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">(1)</b> gremlin> g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). simplePath().by(label). path() gremlin> g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). simplePath(). by(label). 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>). path(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</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">g.V(<span class="integer">1</span>).both().both() g.V(<span class="integer">1</span>).both().both().simplePath() g.V(<span class="integer">1</span>).both().both().simplePath().path() g.V(<span class="integer">1</span>).both().both().simplePath().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">(1)</b> g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). simplePath().by(label). path() g.V().out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">c</span><span class="delimiter">'</span></span>). simplePath(). by(label). 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>). path(). 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="colist arabic"> <ol> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="paragraph"> <p>By using the <code>from()</code> and <code>to()</code> modulators traversers can ensure that only certain sections of the path are acyclic.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-343" type="radio" name="radio-set-1729796988-343" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-343" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-344" type="radio" name="radio-set-1729796988-343" class="tab-selector-2" /> <label for="tab-1729796988-344" 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> 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">link</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">link</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">link</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> g.V(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).times(<span class="integer">3</span>).path() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[A],v[B],v[C],v[D]] gremlin> g.V(<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).times(<span class="integer">3</span>).path() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[v[D],v[C],v[B],v[A]] gremlin> g.V(<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>). repeat(both().simplePath().from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).times(<span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). repeat(both().simplePath().from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).times(<span class="integer">3</span>).path() <span class="comment">//</span>// <b class="conum">(3)</b> ==>[v[A],v[B],v[C],v[D],v[C],v[B],v[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.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">link</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">link</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">link</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(<span class="string"><span class="delimiter">'</span><span class="content">A</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).times(<span class="integer">3</span>).path() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="string"><span class="delimiter">'</span><span class="content">D</span><span class="delimiter">'</span></span>).repeat(both().simplePath()).times(<span class="integer">3</span>).path() <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<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>). repeat(both().simplePath().from(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).times(<span class="integer">3</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). repeat(both().simplePath().from(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).times(<span class="integer">3</span>).path() <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Traverse all acyclic 3-hop paths starting from vertex <code>A</code></p> </li> <li> <p>Traverse all acyclic 3-hop paths starting from vertex <code>D</code></p> </li> <li> <p>Traverse all acyclic 3-hop paths starting from vertex <code>A</code> and from there again all 3-hop paths. The second path may cross the vertices from the first path.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#simplePath()"><code>simplePath()</code></a></p> </div> </div> <div class="sect2"> <h3 id="skip-step">Skip Step</h3> <div class="paragraph"> <p>The <code>skip()</code>-step is analogous to <a href="#range-step"><code>range()</code>-step</a> save that the higher end range is set to -1.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-345" type="radio" name="radio-set-1729796988-345" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-345" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-346" type="radio" name="radio-set-1729796988-345" class="tab-selector-2" /> <label for="tab-1729796988-346" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order() ==><span class="integer">27</span> ==><span class="integer">29</span> ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order().skip(<span class="integer">2</span>) ==><span class="integer">32</span> ==><span class="integer">35</span> gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order().range(<span class="integer">2</span>, -<span class="integer">1</span>) ==><span class="integer">32</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().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order() g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order().skip(<span class="integer">2</span>) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).order().range(<span class="integer">2</span>, -<span class="integer">1</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>skip()</code>-step can also be applied with <code>Scope.local</code>, in which case it operates on the incoming collection.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-347" type="radio" name="radio-set-1729796988-347" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-347" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-348" type="radio" name="radio-set-1729796988-347" class="tab-selector-2" /> <label for="tab-1729796988-348" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).filter(outE(<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">p</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> map(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">primary</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">other</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(limit(local, <span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> by(skip(local, <span class="integer">1</span>)) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="key">person</span>:marko,<span class="key">primary</span>:lop,<span class="key">other</span>:<span class="type">[]</span>] ==>[<span class="key">person</span>:josh,<span class="key">primary</span>:ripple,<span class="key">other</span>:[lop]] ==>[<span class="key">person</span>:peter,<span class="key">primary</span>:lop,<span class="key">other</span>:<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.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).filter(outE(<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">p</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> map(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">primary</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">other</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(limit(local, <span class="integer">1</span>)). <span class="comment">//</span>// <b class="conum">(2)</b> by(skip(local, <span class="integer">1</span>)) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>For each person who created something…​</p> </li> <li> <p>…​select the first project (random order) as <code>primary</code> and…​</p> </li> <li> <p>…​select all other projects as <code>other</code>.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#skip(long)"><code>skip(long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#skip(org.apache.tinkerpop.gremlin.process.traversal.Scope,long)"><code>skip(Scope,long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="split-step">Split Step</h3> <div class="paragraph"> <p>The <code>split()</code>-step (<strong>map</strong>) returns a list of strings created by splitting the incoming string traverser around the matches of the given separator. A null separator will split the string by whitespaces. Null values from the incoming traversers are not processed and remain as null when returned. If the incoming traverser is a non-String value then an IllegalArgumentException will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-349" type="radio" name="radio-set-1729796988-349" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-349" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-350" type="radio" name="radio-set-1729796988-349" class="tab-selector-2" /> <label for="tab-1729796988-350" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">that</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">this</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).split(<span class="string"><span class="delimiter">"</span><span class="content">h</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[t,at] ==>[t,is] ==>[test] ==><span class="predefined-constant">null</span> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).split(<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> ==>[m,rko] ==>[v,d,s] ==>[josh] ==>[peter] gremlin> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">helloworld</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).split(<span class="predefined-constant">null</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[helloworld] ==>[hello,world] ==>[hello,world] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</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().split(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">(4)</b> ==>[[m,rko],[v,d,s],[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.inject(<span class="string"><span class="delimiter">"</span><span class="content">that</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">this</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).split(<span class="string"><span class="delimiter">"</span><span class="content">h</span><span class="delimiter">"</span></span>) <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>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).split(<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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">helloworld</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).split(<span class="predefined-constant">null</span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</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().split(local, <span class="string"><span class="delimiter">"</span><span class="content">a</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>Split the strings by "h".</p> </li> <li> <p>Split person names by "a".</p> </li> <li> <p>Splitting by null will split by whitespaces.</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list of results.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#split(java.lang.String)"><code>split(String)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#split(org.apache.tinkerpop.gremlin.process.traversal.Scope,java.lang.String)"><code>split(Scope, String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="subgraph-step">Subgraph Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/subgraph-logo.png" alt="subgraph logo" width="380"> </div> </div> <div class="paragraph"> <p>Extracting a portion of a graph from a larger one for analysis, visualization or other purposes is a fairly common use case for graph analysts and developers. The <code>subgraph()</code>-step (<strong>sideEffect</strong>) provides a way to produce an <a href="http://mathworld.wolfram.com/Edge-InducedSubgraph.html">edge-induced subgraph</a> from virtually any traversal. The following example demonstrates how to produce the "knows" subgraph:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-351" type="radio" name="radio-set-1729796988-351" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-351" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-352" type="radio" name="radio-set-1729796988-351" class="tab-selector-2" /> <label for="tab-1729796988-352" 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> subGraph = g.E().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">3</span> <span class="key">edges</span>:<span class="integer">2</span>] gremlin> sg = traversal().withEmbedded(subGraph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">3</span> <span class="key">edges</span>:<span class="integer">2</span>], standard] gremlin> sg.E() <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>] ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><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">subGraph = g.E().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> sg = traversal().withEmbedded(subGraph) sg.E() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>As this function produces "edge-induced" subgraphs, <code>subgraph()</code> must be called at edge steps.</p> </li> <li> <p>The subgraph contains only "knows" edges.</p> </li> </ol> </div> <div class="paragraph"> <p>A more common subgraphing use case is to get all of the graph structure surrounding a single vertex:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-353" type="radio" name="radio-set-1729796988-353" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-353" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-354" type="radio" name="radio-set-1729796988-353" class="tab-selector-2" /> <label for="tab-1729796988-354" 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> subGraph = g.V(<span class="integer">3</span>).repeat(__.inE().subgraph(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).outV()).times(<span class="integer">3</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">4</span> <span class="key">edges</span>:<span class="integer">4</span>] gremlin> sg = traversal().withEmbedded(subGraph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">4</span> <span class="key">edges</span>:<span class="integer">4</span>], standard] gremlin> sg.E() ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>] ==>e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>] ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] ==>e[<span class="integer">12</span>][<span class="integer">6</span>-created-><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">subGraph = g.V(<span class="integer">3</span>).repeat(__.inE().subgraph(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).outV()).times(<span class="integer">3</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">subGraph</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> sg = traversal().withEmbedded(subGraph) sg.E()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Starting at vertex <code>3</code>, traverse 3 steps away on in-edges, outputting all of that into the subgraph.</p> </li> </ol> </div> <div class="paragraph"> <p>The above example is purposely brief so as to focus on <code>subgraph()</code> usage, however, it may not be the most optimal method for constructing the subgraph. For instance, if the graph had cycles, it would attempt to reconstruct parts of the subgraph which are already present. The duplicates would not be created, but it would involve some unnecessary processing. If the only interest of the traversal was to populate the subgraph, it would be better to include <code>simplePath()</code> to filter out those cycles, as in <code><em>.inE().subgraph('subGraph').outV().simplePath()</code>. From another perspective, it might also make some sense to use <code>dedup()</code> to avoid traversing the same vertices repeatedly where two vertices shared the multiple edges between them, as in <code></em>.inE().dedup().subgraph('subGraph').outV().dedup()</code>.</p> </div> <div class="paragraph"> <p>There can be multiple <code>subgraph()</code> calls within the same traversal. Each operating against either the same graph (i.e. same side-effect key) or different graphs (i.e. different side-effect keys).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-355" type="radio" name="radio-set-1729796988-355" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-355" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-356" type="radio" name="radio-set-1729796988-355" class="tab-selector-2" /> <label for="tab-1729796988-356" 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> t = g.V().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">knowsG</span><span class="delimiter">'</span></span>).inV().outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>). inV().inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>).iterate() gremlin> traversal().withEmbedded(t.sideEffects.get(<span class="string"><span class="delimiter">'</span><span class="content">knowsG</span><span class="delimiter">'</span></span>)).E() ==>e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>] ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>] gremlin> traversal().withEmbedded(t.sideEffects.get(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>)).E() ==>e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>] ==>e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>] ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] ==>e[<span class="integer">12</span>][<span class="integer">6</span>-created-><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">t = g.V().outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">knowsG</span><span class="delimiter">'</span></span>).inV().outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>). inV().inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).subgraph(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>).iterate() traversal().withEmbedded(t.sideEffects.get(<span class="string"><span class="delimiter">'</span><span class="content">knowsG</span><span class="delimiter">'</span></span>)).E() traversal().withEmbedded(t.sideEffects.get(<span class="string"><span class="delimiter">'</span><span class="content">createdG</span><span class="delimiter">'</span></span>)).E()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>TinkerGraph is the ideal (and default) <code>Graph</code> into which a subgraph is extracted as it’s fast, in-memory, and supports user-supplied identifiers which can be any Java object. It is this last feature that needs some focus as many TinkerPop-enabled graphs have complex identifier types and TinkerGraph’s ability to consume those makes it a perfect host for an incoming subgraph. However care needs to be taken when using the elements of the TinkerGraph subgraph. The original graph’s identifiers may be preserved, but the elements of the graph are now TinkerGraph objects like, <code>TinkerVertex</code> and <code>TinkerEdge</code>. As a result, they can not be used directly in Gremlin running against the original graph. For example, the following traversal would likely return an error:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Vertex v = sg.V().has('name','marko').next(); <span class="invisible">//</span><b class="conum">1</b> List<Vertex> vertices = g.V(v).out().toList(); <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Here "sg" is a reference to a TinkerGraph subgraph and "v" is a <code>TinkerVertex</code>.</p> </li> <li> <p>The <code>g.V(v)</code> has the potential to fail as "g" is the original <code>Graph</code> instance and not a TinkerGraph - it could reject the <code>TinkerVertex</code> instance as it will not recognize it.</p> </li> </ol> </div> <div class="paragraph"> <p>It is safer to wrap the <code>TinkerVertex</code> in a <code>ReferenceVertex</code> or simply reference the <code>id()</code> as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Vertex v = sg.V().has('name','marko').next(); List<Vertex> vertices = g.V(v.id()).out().toList(); // OR Vertex v = new ReferenceVertex(sg.V().has('name','marko').next()); List<Vertex> vertices = g.V(v).out().toList();</code></pre> </div> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#subgraph(java.lang.String)"><code>subgraph(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="substring-step">Substring Step</h3> <div class="paragraph"> <p>The <code>substring()</code>-step (<strong>map</strong>) returns a substring with a 0-based start index (inclusive) and optionally an end index (exclusive) specified. If the start index is negative then it will begin at the specified index counted from the end of the string, or 0 if exceeding the string length. Likewise, if the end index is negative then it will end at the specified index counted from the end of the string, or 0 if exceeding the string length.</p> </div> <div class="paragraph"> <p>End index is optional, if it is not specified or if it exceeds the length of the string then all remaining characters will be returned. End index ≤ start index will return the empty string. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-357" type="radio" name="radio-set-1729796988-357" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-357" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-358" type="radio" name="radio-set-1729796988-357" class="tab-selector-2" /> <label for="tab-1729796988-358" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).substring(<span class="integer">1</span>, <span class="integer">8</span>) ==>est ==>ello wo ==><span class="predefined-constant">null</span> gremlin> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).substring(-<span class="integer">4</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>orld gremlin> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).substring(<span class="integer">2</span>, <span class="integer">0</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==> gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).substring(<span class="integer">2</span>) ==>p ==>pple gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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().substring(local, <span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[p,pple]</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.inject(<span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).substring(<span class="integer">1</span>, <span class="integer">8</span>) g.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).substring(-<span class="integer">4</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span>).substring(<span class="integer">2</span>, <span class="integer">0</span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).substring(<span class="integer">2</span>) g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">software</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().substring(local, <span class="integer">2</span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Negative start index, the first character is read by counting from the end of the string</p> </li> <li> <p>Length of 0 specified will return the empty string</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#substring(int)"><code>substring(int)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#substring(org.apache.tinkerpop.gremlin.process.traversal.Scope,int)"><code>substring(Scope,int)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#substring(int,int)"><code>substring(int,int)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#substring(org.apache.tinkerpop.gremlin.process.traversal.Scope,int,int)"><code>substring(Scope,int,int)</code></a></p> </div> </div> <div class="sect2"> <h3 id="sum-step">Sum Step</h3> <div class="paragraph"> <p>The <code>sum()</code>-step (<strong>map</strong>) operates on a stream of numbers and sums the numbers together to yield a result. Note that the current traverser number is multiplied by the traverser bulk to determine how many such numbers are being represented.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-359" type="radio" name="radio-set-1729796988-359" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-359" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-360" type="radio" name="radio-set-1729796988-359" class="tab-selector-2" /> <label for="tab-1729796988-360" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum() ==><span class="integer">123</span> gremlin> g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum() ==><span class="integer">1471</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum() g.V().repeat(both()).times(<span class="integer">3</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).sum()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When called as <code>sum(local)</code> it determines the sum of the current, local object (not the objects in the traversal stream). This works for <code>Collection</code>-type objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-361" type="radio" name="radio-set-1729796988-361" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-361" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-362" type="radio" name="radio-set-1729796988-361" class="tab-selector-2" /> <label for="tab-1729796988-362" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().sum(local) ==><span class="integer">123</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">'</span><span class="content">age</span><span class="delimiter">'</span></span>).fold().sum(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>When there are <code>null</code> values being evaluated the <code>null</code> objects are ignored, but if all values are recognized as <code>null</code> the return value is <code>null</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-363" type="radio" name="radio-set-1729796988-363" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-363" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-364" type="radio" name="radio-set-1729796988-363" class="tab-selector-2" /> <label for="tab-1729796988-364" 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> g.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).sum() ==><span class="integer">19</span> gremlin> g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).sum(local) ==><span class="predefined-constant">null</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.inject(<span class="predefined-constant">null</span>,<span class="integer">10</span>, <span class="integer">9</span>, <span class="predefined-constant">null</span>).sum() g.inject([<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>,<span class="predefined-constant">null</span>]).sum(local)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sum()"><code>sum()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#sum(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>sum(Scope)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="tail-step">Tail Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/tail-step.png" alt="tail step" width="530"> </div> </div> <div class="paragraph"> <p>The <code>tail()</code>-step is analogous to <a href="#limit-step"><code>limit()</code></a>-step, except that it emits the last <code>n</code>-objects instead of the first <code>n</code>-objects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-365" type="radio" name="radio-set-1729796988-365" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-365" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-366" type="radio" name="radio-set-1729796988-365" class="tab-selector-2" /> <label for="tab-1729796988-366" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order() ==>josh ==>lop ==>marko ==>peter ==>ripple ==>vadas gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail() <span class="comment">//</span>// <b class="conum">(1)</b> ==>vadas gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>vadas gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail(<span class="integer">3</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>peter ==>ripple ==>vadas</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">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order() g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).order().tail(<span class="integer">3</span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Last name (alphabetically).</p> </li> <li> <p>Same as statement 1.</p> </li> <li> <p>Last three names.</p> </li> </ol> </div> <div class="paragraph"> <p>The <code>tail()</code>-step can also be applied with <code>Scope.local</code>, in which case it operates on the incoming collection.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-367" type="radio" name="radio-set-1729796988-367" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-367" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-368" type="radio" name="radio-set-1729796988-367" class="tab-selector-2" /> <label for="tab-1729796988-368" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(tail(local)).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">(1)</b> ==>ripple ==>lop gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()).tail(local) <span class="comment">//</span>// <b class="conum">(2)</b> ==>ripple ==>lop gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()).tail(local, <span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[ripple] ==>[lop] gremlin> g.V().elementMap().tail(local) <span class="comment">//</span>// <b class="conum">(4)</b> ==>[<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">lang</span>:java] ==>[<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">lang</span>:java] ==>[<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().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(tail(local)).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">(1)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()).tail(local) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out().as(<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>).by(unfold().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold()).tail(local, <span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V().elementMap().tail(local) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Only the most recent name from the "a" step (<code>List<Vertex></code> becomes <code>Vertex</code>).</p> </li> <li> <p>Same result as statement 1 (<code>List<String></code> becomes <code>String</code>).</p> </li> <li> <p><code>List<String></code> for each path containing the last two names from the 'a' step.</p> </li> <li> <p><code>Map<String, Object></code> for each vertex, but containing only the last property value.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tail()"><code>tail()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tail(long)"><code>tail(long)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tail(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>tail(Scope)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tail(org.apache.tinkerpop.gremlin.process.traversal.Scope,long)"><code>tail(Scope,long)</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/Scope.html"><code>Scope</code></a></p> </div> </div> <div class="sect2"> <h3 id="timelimit-step">TimeLimit Step</h3> <div class="paragraph"> <p>In many situations, a graph traversal is not about getting an exact answer as its about getting a relative ranking. A classic example is <a href="http://en.wikipedia.org/wiki/Recommender_system">recommendation</a>. What is desired is a relative ranking of vertices, not their absolute rank. Next, it may be desirable to have the traversal execute for no more than 2 milliseconds. In such situations, <code>timeLimit()</code>-step (<strong>filter</strong>) can be used.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/timelimit-step.png" alt="timelimit step" width="400"> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The method <code>clock(int runs, Closure code)</code> is a utility preloaded in the <a href="#gremlin-console">Gremlin Console</a> that can be used to time execution of a body of code. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-369" type="radio" name="radio-set-1729796988-369" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-369" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-370" type="radio" name="radio-set-1729796988-369" class="tab-selector-2" /> <label for="tab-1729796988-370" 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> g.V().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next() ==>v[<span class="integer">1</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">3</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">4</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">2</span>]=<span class="integer">1136688</span> ==>v[<span class="integer">5</span>]=<span class="integer">1136688</span> ==>v[<span class="integer">6</span>]=<span class="integer">1136688</span> gremlin> clock(<span class="integer">1</span>) {g.V().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next()} ==><span class="float">0.9833799999999999</span> gremlin> g.V().repeat(timeLimit(<span class="integer">2</span>).both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next() ==>v[<span class="integer">1</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">3</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">4</span>]=<span class="integer">2744208</span> ==>v[<span class="integer">2</span>]=<span class="integer">1136688</span> ==>v[<span class="integer">5</span>]=<span class="integer">1136688</span> ==>v[<span class="integer">6</span>]=<span class="integer">1136688</span> gremlin> clock(<span class="integer">1</span>) {g.V().repeat(timeLimit(<span class="integer">2</span>).both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next()} ==><span class="float">0.91479</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().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next() clock(<span class="integer">1</span>) {g.V().repeat(both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next()} g.V().repeat(timeLimit(<span class="integer">2</span>).both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next() clock(<span class="integer">1</span>) {g.V().repeat(timeLimit(<span class="integer">2</span>).both().groupCount(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>)).times(<span class="integer">16</span>).cap(<span class="string"><span class="delimiter">'</span><span class="content">m</span><span class="delimiter">'</span></span>).order(local).by(values,desc).next()}</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In essence, the relative order is respected, even through the number of traversers at each vertex is not. The primary benefit being that the calculation is guaranteed to complete at the specified time limit (in milliseconds). Finally, note that the internal clock of <code>timeLimit()</code>-step starts when the first traverser enters it. When the time limit is reached, any <code>next()</code> evaluation of the step will yield a <code>NoSuchElementException</code> and any <code>hasNext()</code> evaluation will yield <code>false</code>.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#timeLimit(long)"><code>timeLimit(long)</code></a></p> </div> </div> <div class="sect2"> <h3 id="to-step">To Step</h3> <div class="paragraph"> <p>The <code>to()</code>-step is not an actual step, but instead is a "step-modulator" similar to <a href="#as-step"><code>as()</code></a> and <a href="#by-step"><code>by()</code></a>. If a step is able to accept traversals or strings then <code>to()</code> is the means by which they are added. The general pattern is <code>step().to()</code>. See <a href="#from-step"><code>from()</code></a>-step.</p> </div> <div class="paragraph"> <p>The list of steps that support <code>to()</code>-modulation are: <a href="#simplepath-step"><code>simplePath()</code></a>, <a href="#cyclicpath-step"><code>cyclicPath()</code></a>, <a href="#path-step"><code>path()</code></a>, and <a href="#addedge-step"><code>addE()</code></a>.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#to(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>to(Direction,String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#to(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>to(String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#to(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>to(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#to(org.apache.tinkerpop.gremlin.structure.Vertex)"><code>to(Vertex)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toE(org.apache.tinkerpop.gremlin.structure.Direction,java.lang.String...)"><code>toE(Direction,String)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toV(org.apache.tinkerpop.gremlin.structure.Direction)"><code>toV(Direction)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/structure/Direction.html"><code>Direction</code></a></p> </div> </div> <div class="sect2"> <h3 id="toLower-step">ToLower Step</h3> <div class="paragraph"> <p>The <code>toLower()</code>-step (<strong>map</strong>) returns the lowercase representation of incoming string or list of string traverser. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-371" type="radio" name="radio-set-1729796988-371" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-371" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-372" type="radio" name="radio-set-1729796988-371" class="tab-selector-2" /> <label for="tab-1729796988-372" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">HELLO</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).toLower() ==>hello ==>world ==><span class="predefined-constant">null</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content">HELLO</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).toLower(Scope.local) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[hello,world,<span class="predefined-constant">null</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.inject(<span class="string"><span class="delimiter">"</span><span class="content">HELLO</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).toLower() g.inject([<span class="string"><span class="delimiter">"</span><span class="content">HELLO</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).toLower(Scope.local) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toLower()"><code>toLower()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toLower(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>toLower(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="toUpper-step">ToUpper Step</h3> <div class="paragraph"> <p>The <code>toUpper()</code>-step (<strong>map</strong>) returns the uppercase representation of incoming string or list of string traverser. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-373" type="radio" name="radio-set-1729796988-373" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-373" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-374" type="radio" name="radio-set-1729796988-373" class="tab-selector-2" /> <label for="tab-1729796988-374" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).toUpper() ==>HELLO ==>WORLD ==><span class="predefined-constant">null</span> gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).toUpper() <span class="comment">//</span>// <b class="conum">(1)</b> ==>MARKO ==>VADAS ==>LOP ==>JOSH ==>RIPPLE ==>PETER gremlin> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().toUpper(local) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[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.inject(<span class="string"><span class="delimiter">"</span><span class="content">hello</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">wORlD</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).toUpper() g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).toUpper() <span class="comment">//</span>// <b class="conum">(1)</b> g.V().values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).fold().toUpper(local) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Returns the upper case representation of all vertex names.</p> </li> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toUpper()"><code>toUpper()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#toUpper(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>toUpper(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="tree-step">Tree Step</h3> <div class="paragraph"> <p>From any one element (i.e. vertex or edge), the emanating paths from that element can be aggregated to form a <a href="http://en.wikipedia.org/wiki/Tree_(data_structure)">tree</a>. Gremlin provides <code>tree()</code>-step (<strong>sideEffect</strong>) for such this situation.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/tree-step.png" alt="tree step" width="450"> </div> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-375" type="radio" name="radio-set-1729796988-375" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-375" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-376" type="radio" name="radio-set-1729796988-375" class="tab-selector-2" /> <label for="tab-1729796988-376" 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> tree = g.V().out().out().tree().next() ==>v[<span class="integer">1</span>]={v[<span class="integer">4</span>]={v[<span class="integer">3</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">tree = g.V().out().out().tree().next()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is important to see how the paths of all the emanating traversers are united to form the tree.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/tree-step2.png" alt="tree step2" width="500"> </div> </div> <div class="paragraph"> <p>The resultant tree data structure can then be manipulated (see <code>Tree</code> JavaDoc).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-377" type="radio" name="radio-set-1729796988-377" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-377" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-378" type="radio" name="radio-set-1729796988-377" class="tab-selector-2" /> <label for="tab-1729796988-378" 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> tree = g.V().out().out().tree().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).next() ==>marko={josh={ripple={}, lop={}}} gremlin> tree[<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>] ==>josh={ripple={}, lop={}} gremlin> tree[<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>][<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>] ==>ripple={} ==>lop={} gremlin> tree.getObjectsAtDepth(<span class="integer">3</span>) ==>ripple ==>lop</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">tree = g.V().out().out().tree().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).next() tree[<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>] tree[<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>][<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>] tree.getObjectsAtDepth(<span class="integer">3</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note that when using <code>by()</code>-modulation, tree nodes are combined based on projection uniqueness, not on the uniqueness of the original objects being projected. For instance:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-379" type="radio" name="radio-set-1729796988-379" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-379" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-380" type="radio" name="radio-set-1729796988-379" class="tab-selector-2" /> <label for="tab-1729796988-380" 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> 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">josh</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).tree() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[v[<span class="integer">4</span>]:[v[<span class="integer">3</span>]:[<span class="key">lop</span>:<span class="type">[]</span>],v[<span class="integer">5</span>]:[<span class="key">ripple</span>:<span class="type">[]</span>]]] gremlin> 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">josh</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). tree().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label).by() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">josh</span>:[<span class="key">software</span>:[<span class="key">ripple</span>:<span class="type">[]</span>,<span class="key">lop</span>:<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.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">josh</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).tree() <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">josh</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). tree().by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by(label).by() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>When the <code>tree()</code> is created, vertex 3 and 5 are unique and thus, form unique branches in the tree structure.</p> </li> <li> <p>When the <code>tree()</code> is <code>by()</code>-modulated by <code>label</code>, then vertex 3 and 5 are both "software" and thus are merged to a single node in the tree.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tree()"><code>tree()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#tree(java.lang.String)"><code>tree(String)</code></a></p> </div> </div> <div class="sect2"> <h3 id="trim-step">Trim Step</h3> <div class="paragraph"> <p>The <code>trim()</code>-step (<strong>map</strong>) returns a string with leading and leading whitespace removed. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an <code>IllegalArgumentException</code> will be thrown.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-381" type="radio" name="radio-set-1729796988-381" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-381" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-382" type="radio" name="radio-set-1729796988-381" class="tab-selector-2" /> <label for="tab-1729796988-382" 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> g.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).trim() ==>hello ==>world ==><span class="predefined-constant">null</span> gremlin> g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).trim(Scope.local) <span class="comment">//</span>// <b class="conum">(1)</b> ==>[hello,world,<span class="predefined-constant">null</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.inject(<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>).trim() g.inject([<span class="string"><span class="delimiter">"</span><span class="content"> hello </span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> world </span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>]).trim(Scope.local) <span class="invisible">//</span><b class="conum">1</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Use <code>Scope.local</code> to operate on individual string elements inside incoming list, which will return a list.</p> </li> </ol> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#trim()"><code>trim()</code></a> <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#trim(org.apache.tinkerpop.gremlin.process.traversal.Scope)"><code>trim(Scope)</code></a></p> </div> </div> <div class="sect2"> <h3 id="unfold-step">Unfold Step</h3> <div class="paragraph"> <p>If the object reaching <code>unfold()</code> (<strong>flatMap</strong>) is an iterator, iterable, or map, then it is unrolled into a linear form. If not, then the object is simply emitted. Please see <a href="#fold-step"><code>fold()</code></a> step for the inverse behavior.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-383" type="radio" name="radio-set-1729796988-383" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-383" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-384" type="radio" name="radio-set-1729796988-383" class="tab-selector-2" /> <label for="tab-1729796988-384" 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> g.V(<span class="integer">1</span>).out().fold().inject(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>,[<span class="float">1.23</span>,<span class="float">2.34</span>]) ==>gremlin ==>[<span class="float">1.23</span>,<span class="float">2.34</span>] ==>[v[<span class="integer">3</span>],v[<span class="integer">2</span>],v[<span class="integer">4</span>]] gremlin> g.V(<span class="integer">1</span>).out().fold().inject(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>,[<span class="float">1.23</span>,<span class="float">2.34</span>]).unfold() ==>gremlin ==><span class="float">1.23</span> ==><span class="float">2.34</span> ==>v[<span class="integer">3</span>] ==>v[<span class="integer">2</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.V(<span class="integer">1</span>).out().fold().inject(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>,[<span class="float">1.23</span>,<span class="float">2.34</span>]) g.V(<span class="integer">1</span>).out().fold().inject(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>,[<span class="float">1.23</span>,<span class="float">2.34</span>]).unfold()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note that <code>unfold()</code> does not recursively unroll iterators. Instead, <code>repeat()</code> can be used to for recursive unrolling.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-385" type="radio" name="radio-set-1729796988-385" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-385" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-386" type="radio" name="radio-set-1729796988-385" class="tab-selector-2" /> <label for="tab-1729796988-386" 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> inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]) ==><span class="integer">1</span> ==>[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]] gremlin> inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]).unfold() ==><span class="integer">1</span> ==><span class="integer">2</span> ==><span class="integer">3</span> ==>[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]] gremlin> inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]).repeat(unfold()).until(count(local).is(<span class="integer">1</span>)).unfold() ==><span class="integer">1</span> ==><span class="integer">2</span> ==><span class="integer">3</span> ==><span class="integer">4</span> ==><span class="integer">5</span> ==><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">inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]) inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]).unfold() inject(<span class="integer">1</span>,[<span class="integer">2</span>,<span class="integer">3</span>,[<span class="integer">4</span>,<span class="integer">5</span>,[<span class="integer">6</span>]]]).repeat(unfold()).until(count(local).is(<span class="integer">1</span>)).unfold()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#unfold()"><code>unfold()</code></a></p> </div> </div> <div class="sect2"> <h3 id="union-step">Union Step</h3> <div class="imageblock"> <div class="content"> <img src="../images/union-step.png" alt="union step" width="650"> </div> </div> <div class="paragraph"> <p>The <code>union()</code>-step (<strong>branch</strong>) supports the merging of the results of an arbitrary number of traversals. When a traverser reaches a <code>union()</code>-step, it is copied to each of its internal steps. The traversers emitted from <code>union()</code> are the outputs of the respective internal traversals.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-387" type="radio" name="radio-set-1729796988-387" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-387" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-388" type="radio" name="radio-set-1729796988-387" class="tab-selector-2" /> <label for="tab-1729796988-388" 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> g.V(<span class="integer">4</span>).union( __.in().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>), out().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)) ==><span class="integer">29</span> ==>java ==>java gremlin> g.V(<span class="integer">4</span>).union( __.in().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>), out().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)).path() ==>[v[<span class="integer">4</span>],v[<span class="integer">1</span>],<span class="integer">29</span>] ==>[v[<span class="integer">4</span>],v[<span class="integer">5</span>],java] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>],java] gremlin> g.union(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>), 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">lop</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)) ==>v[<span class="integer">2</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">4</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(<span class="integer">4</span>).union( __.in().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>), out().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)) g.V(<span class="integer">4</span>).union( __.in().values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>), out().values(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>)).path() g.union(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>), 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">lop</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#union(org.apache.tinkerpop.gremlin.process.traversal.Traversal...)"><code>union(Traversal…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="until-step">Until Step</h3> <div class="paragraph"> <p>The <code>until</code>-step is not an actual step, but is instead a step modulator for <code><a href="#repeat-step">repeat()</a></code> (find more documentation on the <code>until()</code> there).</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#until(java.util.function.Predicate)"><code>until(Predicate)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#until(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>until(Traversal)</code></a></p> </div> <div class="paragraph"> <p><a id="graph-step"></a></p> </div> </div> <div class="sect2"> <h3 id="v-step">V Step</h3> <div class="paragraph"> <p>The <code>V()</code>-step is meant to read vertices from the graph and is usually used to start a <code>GraphTraversal</code>, but can also be used mid-traversal.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-389" type="radio" name="radio-set-1729796988-389" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-389" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-390" type="radio" name="radio-set-1729796988-389" class="tab-selector-2" /> <label for="tab-1729796988-390" 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> g.V(<span class="integer">1</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">1</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">0</span>][<span class="integer">1</span>-uses-><span class="integer">3</span>] ==>e[<span class="integer">13</span>][<span class="integer">1</span>-uses-><span class="integer">5</span>] ==>e[<span class="integer">14</span>][<span class="integer">2</span>-uses-><span class="integer">3</span>] ==>e[<span class="integer">15</span>][<span class="integer">2</span>-uses-><span class="integer">5</span>] ==>e[<span class="integer">16</span>][<span class="integer">4</span>-uses-><span class="integer">3</span>] ==>e[<span class="integer">17</span>][<span class="integer">4</span>-uses-><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(<span class="integer">1</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</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>Find the vertex by its unique identifier (i.e. <code>T.id</code>) - not all graphs will use a numeric value for their identifier.</p> </li> <li> <p>An example where <code>V()</code> is used both as a start step and in the middle of a traversal.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Whether a mid-traversal <code>V()</code> uses an index or not, depends on a) whether suitable index exists and b) if the particular graph system provider implemented this functionality. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-391" type="radio" name="radio-set-1729796988-391" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-391" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-392" type="radio" name="radio-set-1729796988-391" class="tab-selector-2" /> <label for="tab-1729796988-392" 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> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).toString() <span class="comment">//</span>// <b class="conum">(1)</b> ==>[GraphStep(vertex,<span class="type">[]</span>), HasStep([name.within([marko, vadas, josh])])<span class="error">@</span>[person], GraphStep(vertex,<span class="type">[]</span>), HasStep([name.within([lop, ripple])]), AddEdgeStep({~from=[[SelectOneStep(last,person,<span class="predefined-constant">null</span>)]], label=[uses]})] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate().toString() <span class="comment">//</span>// <b class="conum">(2)</b> ==>[TinkerGraphStep(vertex,[name.within([marko, vadas, josh])])<span class="error">@</span>[person], TinkerGraphStep(vertex,[name.within([lop, ripple])]), AddEdgeStep({~from=[[SelectOneStep(last,person,<span class="predefined-constant">null</span>)]], label=[uses]}), NoneStep]</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).toString() <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>, within(<span class="string"><span class="delimiter">'</span><span class="content">marko</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="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">person</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>, within(<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>)).addE(<span class="string"><span class="delimiter">'</span><span class="content">uses</span><span class="delimiter">'</span></span>).from(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate().toString() <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Normally the <code>V()</code>-step will iterate over all vertices. However, graph strategies can fold <code>HasContainer</code>'s into a <code>GraphStep</code> to allow index lookups.</p> </li> <li> <p>Whether the graph system provider supports mid-traversal <code>V()</code> index lookups or not can easily be determined by inspecting the <code>toString()</code> output of the iterated traversal. If <code>has</code> conditions were folded into the <code>V()</code>-step, an index - if one exists - will be used.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#V(java.lang.Object...)"><code>V(Object…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="value-step">Value Step</h3> <div class="paragraph"> <p>The <code>value()</code>-step (<strong>map</strong>) takes a <code>Property</code> and extracts the value from it.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-393" type="radio" name="radio-set-1729796988-393" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-393" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-394" type="radio" name="radio-set-1729796988-393" class="tab-selector-2" /> <label for="tab-1729796988-394" 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> g.V(<span class="integer">1</span>).properties().value() ==>marko ==>san diego ==>santa cruz ==>brussels ==>santa fe gremlin> g.V(<span class="integer">1</span>).properties().properties().value() ==><span class="integer">1997</span> ==><span class="integer">2001</span> ==><span class="integer">2001</span> ==><span class="integer">2004</span> ==><span class="integer">2004</span> ==><span class="integer">2005</span> ==><span class="integer">2005</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>).properties().value() g.V(<span class="integer">1</span>).properties().properties().value()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#value()"><code>value()</code></a></p> </div> </div> <div class="sect2"> <h3 id="valuemap-step">ValueMap Step</h3> <div class="paragraph"> <p>The <code>valueMap()</code>-step yields a <code>Map</code> representation of the properties of an element.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> This step is the precursor to the <a href="#elementmap-step">elementMap()-step</a>. Users should typically choose <code>elementMap()</code> unless they utilize multi-properties. <code>elementMap()</code> effectively mimics the functionality of <code>valueMap(true).by(unfold())</code> as a single step. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-395" type="radio" name="radio-set-1729796988-395" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-395" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-396" type="radio" name="radio-set-1729796988-395" class="tab-selector-2" /> <label for="tab-1729796988-396" 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> g.V().valueMap() ==>[<span class="key">name</span>:[marko],<span class="key">age</span>:[<span class="integer">29</span>]] ==>[<span class="key">name</span>:[vadas],<span class="key">age</span>:[<span class="integer">27</span>]] ==>[<span class="key">name</span>:[lop],<span class="key">lang</span>:[java]] ==>[<span class="key">name</span>:[josh],<span class="key">age</span>:[<span class="integer">32</span>]] ==>[<span class="key">name</span>:[ripple],<span class="key">lang</span>:[java]] ==>[<span class="key">name</span>:[peter],<span class="key">age</span>:[<span class="integer">35</span>]] gremlin> g.V().valueMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) ==>[<span class="key">age</span>:[<span class="integer">29</span>]] ==>[<span class="key">age</span>:[<span class="integer">27</span>]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[<span class="integer">32</span>]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[<span class="integer">35</span>]] gremlin> g.V().valueMap(<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">blah</span><span class="delimiter">'</span></span>) ==>[<span class="key">age</span>:[<span class="integer">29</span>]] ==>[<span class="key">age</span>:[<span class="integer">27</span>]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[<span class="integer">32</span>]] ==><span class="type">[]</span> ==>[<span class="key">age</span>:[<span class="integer">35</span>]] gremlin> g.E().valueMap() ==>[<span class="key">weight</span>:<span class="float">0.5</span>] ==>[<span class="key">weight</span>:<span class="float">1.0</span>] ==>[<span class="key">weight</span>:<span class="float">0.4</span>] ==>[<span class="key">weight</span>:<span class="float">1.0</span>] ==>[<span class="key">weight</span>:<span class="float">0.4</span>] ==>[<span class="key">weight</span>:<span class="float">0.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().valueMap() g.V().valueMap(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>) g.V().valueMap(<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">blah</span><span class="delimiter">'</span></span>) g.E().valueMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>It is important to note that the map of a vertex maintains a list of values for each key. The map of an edge or vertex-property represents a single property (not a list). The reason is that vertices in TinkerPop leverage <a href="#vertex-properties">vertex properties</a> which support multiple values per key. Using the <a href="#the-crew-toy-graph">"The Crew"</a> toy graph, the point is made explicit.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-397" type="radio" name="radio-set-1729796988-397" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-397" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-398" type="radio" name="radio-set-1729796988-397" class="tab-selector-2" /> <label for="tab-1729796988-398" 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> g.V().valueMap() ==>[<span class="key">name</span>:[marko],<span class="key">location</span>:[san diego,santa cruz,brussels,santa fe]] ==>[<span class="key">name</span>:[stephen],<span class="key">location</span>:[centreville,dulles,purcellville]] ==>[<span class="key">name</span>:[matthias],<span class="key">location</span>:[bremen,baltimore,oakland,seattle]] ==>[<span class="key">name</span>:[daniel],<span class="key">location</span>:[spremberg,kaiserslautern,aachen]] ==>[<span class="key">name</span>:[gremlin]] ==>[<span class="key">name</span>:[tinkergraph]] gremlin> 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>) ==>vp[location->san diego] ==>vp[location->santa cruz] ==>vp[location->brussels] ==>vp[location->santa fe] gremlin> 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap() ==>[<span class="key">startTime</span>:<span class="integer">1997</span>,<span class="key">endTime</span>:<span class="integer">2001</span>] ==>[<span class="key">startTime</span>:<span class="integer">2001</span>,<span class="key">endTime</span>:<span class="integer">2004</span>] ==>[<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2005</span>] ==>[<span class="key">startTime</span>:<span class="integer">2005</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().valueMap() 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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</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>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>To turn list of values into single items, the <code>by()</code> modulator can be used as shown below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-399" type="radio" name="radio-set-1729796988-399" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-399" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-400" type="radio" name="radio-set-1729796988-399" class="tab-selector-2" /> <label for="tab-1729796988-400" 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> g.V().valueMap().by(unfold()) ==>[<span class="key">name</span>:marko,<span class="key">location</span>:san diego] ==>[<span class="key">name</span>:stephen,<span class="key">location</span>:centreville] ==>[<span class="key">name</span>:matthias,<span class="key">location</span>:bremen] ==>[<span class="key">name</span>:daniel,<span class="key">location</span>:spremberg] ==>[<span class="key">name</span>:gremlin] ==>[<span class="key">name</span>:tinkergraph] gremlin> g.V().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">location</span><span class="delimiter">'</span></span>).by().by(unfold()) ==>[<span class="key">name</span>:[marko],<span class="key">location</span>:san diego] ==>[<span class="key">name</span>:[stephen],<span class="key">location</span>:centreville] ==>[<span class="key">name</span>:[matthias],<span class="key">location</span>:bremen] ==>[<span class="key">name</span>:[daniel],<span class="key">location</span>:spremberg] ==>[<span class="key">name</span>:[gremlin]] ==>[<span class="key">name</span>:[tinkergraph]]</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().valueMap().by(unfold()) g.V().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">location</span><span class="delimiter">'</span></span>).by().by(unfold())</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>If the <code>id</code>, <code>label</code>, <code>key</code>, and <code>value</code> of the <code>Element</code> is desired, then the <code>with()</code> modulator can be used to trigger its insertion into the returned map.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-401" type="radio" name="radio-set-1729796988-401" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-401" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-402" type="radio" name="radio-set-1729796988-401" class="tab-selector-2" /> <label for="tab-1729796988-402" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens) ==>[<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">location</span>:[san diego,santa cruz,brussels,santa fe]] ==>[<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>:[centreville,dulles,purcellville]] ==>[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:person,<span class="key">name</span>:[matthias],<span class="key">location</span>:[bremen,baltimore,oakland,seattle]] ==>[<span class="key">id</span>:<span class="integer">9</span>,<span class="key">label</span>:person,<span class="key">name</span>:[daniel],<span class="key">location</span>:[spremberg,kaiserslautern,aachen]] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).with(WithOptions.tokens, WithOptions.labels) ==>[<span class="key">label</span>:person,<span class="key">name</span>:[marko]] ==>[<span class="key">label</span>:person,<span class="key">name</span>:[stephen]] ==>[<span class="key">label</span>:person,<span class="key">name</span>:[matthias]] ==>[<span class="key">label</span>:person,<span class="key">name</span>:[daniel]] gremlin> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens, WithOptions.values) ==>[<span class="key">value</span>:san diego,<span class="key">startTime</span>:<span class="integer">1997</span>,<span class="key">endTime</span>:<span class="integer">2001</span>] ==>[<span class="key">value</span>:santa cruz,<span class="key">startTime</span>:<span class="integer">2001</span>,<span class="key">endTime</span>:<span class="integer">2004</span>] ==>[<span class="key">value</span>:brussels,<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2005</span>] ==>[<span class="key">value</span>:santa fe,<span class="key">startTime</span>:<span class="integer">2005</span>] ==>[<span class="key">value</span>:centreville,<span class="key">startTime</span>:<span class="integer">1990</span>,<span class="key">endTime</span>:<span class="integer">2000</span>] ==>[<span class="key">value</span>:dulles,<span class="key">startTime</span>:<span class="integer">2000</span>,<span class="key">endTime</span>:<span class="integer">2006</span>] ==>[<span class="key">value</span>:purcellville,<span class="key">startTime</span>:<span class="integer">2006</span>] ==>[<span class="key">value</span>:bremen,<span class="key">startTime</span>:<span class="integer">2004</span>,<span class="key">endTime</span>:<span class="integer">2007</span>] ==>[<span class="key">value</span>:baltimore,<span class="key">startTime</span>:<span class="integer">2007</span>,<span class="key">endTime</span>:<span class="integer">2011</span>] ==>[<span class="key">value</span>:oakland,<span class="key">startTime</span>:<span class="integer">2011</span>,<span class="key">endTime</span>:<span class="integer">2014</span>] ==>[<span class="key">value</span>:seattle,<span class="key">startTime</span>:<span class="integer">2014</span>] ==>[<span class="key">value</span>:spremberg,<span class="key">startTime</span>:<span class="integer">1982</span>,<span class="key">endTime</span>:<span class="integer">2005</span>] ==>[<span class="key">value</span>:kaiserslautern,<span class="key">startTime</span>:<span class="integer">2005</span>,<span class="key">endTime</span>:<span class="integer">2009</span>] ==>[<span class="key">value</span>:aachen,<span class="key">startTime</span>:<span class="integer">2009</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>).valueMap().with(WithOptions.tokens) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).valueMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).with(WithOptions.tokens, WithOptions.labels) g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).valueMap().with(WithOptions.tokens, WithOptions.values)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#valueMap(java.lang.String...)"><code>valueMap(String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="values-step">Values Step</h3> <div class="paragraph"> <p>The <code>values()</code>-step (<strong>map</strong>) extracts the values of properties from an <code>Element</code> in the traversal stream.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-403" type="radio" name="radio-set-1729796988-403" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-403" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-404" type="radio" name="radio-set-1729796988-403" class="tab-selector-2" /> <label for="tab-1729796988-404" 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> g.V(<span class="integer">1</span>).values() ==>marko ==>san diego ==>santa cruz ==>brussels ==>santa fe gremlin> g.V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>) ==>san diego ==>santa cruz ==>brussels ==>santa fe gremlin> g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).values() ==><span class="integer">1997</span> ==><span class="integer">2001</span> ==><span class="integer">2001</span> ==><span class="integer">2004</span> ==><span class="integer">2004</span> ==><span class="integer">2005</span> ==><span class="integer">2005</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>).values() g.V(<span class="integer">1</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>) g.V(<span class="integer">1</span>).properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).values()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#values(java.lang.String...)"><code>values(String…​)</code></a></p> </div> </div> <div class="sect2"> <h3 id="vertex-steps">Vertex Steps</h3> <div class="imageblock"> <div class="content"> <img src="../images/vertex-steps.png" alt="vertex steps" width="350"> </div> </div> <div class="paragraph"> <p>The vertex steps (<strong>flatMap</strong>) are fundamental to the Gremlin language. Via these steps, its possible to "move" on the graph — i.e. traverse.</p> </div> <div class="ulist"> <ul> <li> <p><code>out(string…​)</code>: Move to the outgoing adjacent vertices given the edge labels.</p> </li> <li> <p><code>in(string…​)</code>: Move to the incoming adjacent vertices given the edge labels.</p> </li> <li> <p><code>both(string…​)</code>: Move to both the incoming and outgoing adjacent vertices given the edge labels.</p> </li> <li> <p><code>outE(string…​)</code>: Move to the outgoing incident edges given the edge labels.</p> </li> <li> <p><code>inE(string…​)</code>: Move to the incoming incident edges given the edge labels.</p> </li> <li> <p><code>bothE(string…​)</code>: Move to both the incoming and outgoing incident edges given the edge labels.</p> </li> <li> <p><code>outV()</code>: Move to the outgoing vertex.</p> </li> <li> <p><code>inV()</code>: Move to the incoming vertex.</p> </li> <li> <p><code>bothV()</code>: Move to both vertices.</p> </li> <li> <p><code>otherV()</code> : Move to the vertex that was not the vertex that was moved from.</p> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Groovy</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>in</code> is a reserved word in Groovy, and when therefore used as part of an anonymous traversal must be referred to in Gremlin with the double underscore <code>__.in()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Javascript</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>in</code> is a reserved word in Javascript, and therefore must be referred to in Gremlin with <code>in_()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>in</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>in_()</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-405" type="radio" name="radio-set-1729796988-405" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-405" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-406" type="radio" name="radio-set-1729796988-405" class="tab-selector-2" /> <label for="tab-1729796988-406" 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> g.V(<span class="integer">4</span>) ==>v[<span class="integer">4</span>] gremlin> g.V(<span class="integer">4</span>).outE() <span class="comment">//</span>// <b class="conum">(1)</b> ==>e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>] ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] gremlin> g.V(<span class="integer">4</span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>] gremlin> g.V(<span class="integer">4</span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> gremlin> g.V(<span class="integer">4</span>).bothE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>) ==>e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>] ==>e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>] ==>e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>] gremlin> g.V(<span class="integer">4</span>).bothE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>).otherV() ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">4</span>).both(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">4</span>).outE().inV() <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">4</span>).out() <span class="comment">//</span>// <b class="conum">(5)</b> ==>v[<span class="integer">5</span>] ==>v[<span class="integer">3</span>] gremlin> g.V(<span class="integer">4</span>).inE().outV() ==>v[<span class="integer">1</span>] gremlin> g.V(<span class="integer">4</span>).inE().bothV() ==>v[<span class="integer">1</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.V(<span class="integer">4</span>) g.V(<span class="integer">4</span>).outE() <span class="comment">//</span>// <b class="conum">(1)</b> g.V(<span class="integer">4</span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V(<span class="integer">4</span>).inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(3)</b> g.V(<span class="integer">4</span>).bothE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>) g.V(<span class="integer">4</span>).bothE(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>).otherV() g.V(<span class="integer">4</span>).both(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">blah</span><span class="delimiter">'</span></span>) g.V(<span class="integer">4</span>).outE().inV() <span class="comment">//</span>// <b class="conum">(4)</b> g.V(<span class="integer">4</span>).out() <span class="comment">//</span>// <b class="conum">(5)</b> g.V(<span class="integer">4</span>).inE().outV() g.V(<span class="integer">4</span>).inE().bothV()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>All outgoing edges.</p> </li> <li> <p>All incoming knows-edges.</p> </li> <li> <p>All incoming created-edges.</p> </li> <li> <p>Moving forward touching edges and vertices.</p> </li> <li> <p>Moving forward only touching vertices.</p> </li> </ol> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#both(java.lang.String...)"><code>both(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#bothE(java.lang.String...)"><code>bothE(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#bothV()"><code>bothV()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#in(java.lang.String...)"><code>in(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#inE(java.lang.String...)"><code>inE(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#inV()"><code>inV()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#otherV()"><code>otherV()</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#out(java.lang.String...)"><code>out(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#outE(java.lang.String...)"><code>outE(String…​)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#outV()"><code>outV()</code></a></p> </div> </div> <div class="sect2"> <h3 id="where-step">Where Step</h3> <div class="paragraph"> <p>The <code>where()</code>-step filters the current object based on either the object itself (<code>Scope.local</code>) or the path history of the object (<code>Scope.global</code>) (<strong>filter</strong>). This step is typically used in conjunction with either <a href="#match-step"><code>match()</code></a>-step or <a href="#select-step"><code>select()</code></a>-step, but can be used in isolation.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-407" type="radio" name="radio-set-1729796988-407" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-407" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-408" type="radio" name="radio-set-1729796988-407" class="tab-selector-2" /> <label for="tab-1729796988-408" 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> g.V(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">4</span>] ==>v[<span class="integer">6</span>] gremlin> g.withSideEffect(<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">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>]).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).where(within(<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> ==>josh ==>peter gremlin> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</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">(3)</b> ==>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(<span class="integer">1</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(neq(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(1)</b> g.withSideEffect(<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">josh</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>]).V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).where(within(<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> g.V(<span class="integer">1</span>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">1</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">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Who are marko’s collaborators, where marko can not be his own collaborator? (predicate)</p> </li> <li> <p>Of the co-creators of marko, only keep those whose name is josh or peter. (using a sideEffect)</p> </li> <li> <p>Which of marko’s collaborators have worked on more than 1 project? (using a traversal)</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Please see <a href="#using-where-with-match"><code>match().where()</code></a> and <a href="#using-where-with-select"><code>select().where()</code></a> for how <code>where()</code> can be used in conjunction with <code>Map<String,Object></code> projecting steps — i.e. <code>Scope.local</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>A few more examples of filtering an arbitrary object based on a anonymous traversal is provided below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-409" type="radio" name="radio-set-1729796988-409" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-409" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-410" type="radio" name="radio-set-1729796988-409" class="tab-selector-2" /> <label for="tab-1729796988-410" 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> g.V().where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(1)</b> ==>marko ==>josh ==>peter gremlin> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(2)</b> ==>josh gremlin> g.V().where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gte(<span class="integer">2</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">(3)</b> ==>josh gremlin> g.V().where(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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> ==>marko gremlin> g.V().where(__.not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>))).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">(5)</b> ==>vadas gremlin> g.V().where(__.not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).and().in(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">(6)</b> ==>vadas gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). by(<span class="string"><span class="delimiter">'</span><span class="content">age</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>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:vadas] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).or(eq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</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>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(8)</b> ==>[<span class="key">a</span>:marko,<span class="key">b</span>:vadas] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:josh] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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">b</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">(9)</b> ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">4</span>] ==>v[<span class="integer">4</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().where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(1)</b> g.V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">(2)</b> g.V().where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is(gte(<span class="integer">2</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">(3)</b> g.V().where(out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).where(out(<span class="string"><span class="delimiter">'</span><span class="content">created</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> g.V().where(__.not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>))).where(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">(5)</b> g.V().where(__.not(out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).and().in(<span class="string"><span class="delimiter">'</span><span class="content">knows</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">(6)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). by(<span class="string"><span class="delimiter">'</span><span class="content">age</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>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).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">b</span><span class="delimiter">'</span></span>). where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,gt(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).or(eq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>))). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>). by(__.in(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</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>). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(8)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</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">b</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="invisible">//</span><b class="conum">9</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>What are the names of the people who have created a project?</p> </li> <li> <p>What are the names of the people that are known by someone one and have created a project?</p> </li> <li> <p>What are the names of the people how have created two or more projects?</p> </li> <li> <p>What are the names of the people who know someone that has created a project? (This only works in OLTP — see the <code>WARNING</code> below)</p> </li> <li> <p>What are the names of the people who have not created anything, but are known by someone?</p> </li> <li> <p>The concatenation of <code>where()</code>-steps is the same as a single <code>where()</code>-step with an and’d clause.</p> </li> <li> <p>Marko knows josh and vadas but is only older than vadas.</p> </li> <li> <p>Marko is younger than josh, but josh knows someone equal in age to marko (which is marko).</p> </li> <li> <p>The "age" property is not <a href="#by-step">productive</a> for all vertices and therefore those values are filtered.</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> The anonymous traversal of <code>where()</code> processes the current object "locally". In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversal does not leave the confines of the vertex’s star graph. In other words, it can not traverse to an adjacent vertex’s properties or edges. </td> </tr> </table> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#where(org.apache.tinkerpop.gremlin.process.traversal.P)"><code>where(P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#where(java.lang.String,org.apache.tinkerpop.gremlin.process.traversal.P)"><code>where(String,P)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#where(org.apache.tinkerpop.gremlin.process.traversal.Traversal)"><code>where(Traversal)</code></a>, <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/process/traversal/P.html"><code>P</code></a></p> </div> </div> <div class="sect2"> <h3 id="with-step">With Step</h3> <div class="paragraph"> <p>The <code>with()</code>-step is not an actual step, but is instead a "step modulator" which modifies the behavior of the step prior to it. The <code>with()</code>-step provides additional "configuration" information to steps that implement the <code>Configuring</code> interface. Steps that allow for this type of modulation will explicitly state so in their documentation.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Javascript</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>with</code> is a reserved word in Javascript, and therefore must be referred to in Gremlin with <code>with_()</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>with</code> is a reserved word in Python, and therefore must be referred to in Gremlin with <code>with_()</code>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="write-step">Write Step</h3> <div class="paragraph"> <p>The <code>write()</code>-step is not really a "step" but a step modulator in that it modifies the functionality of the <code>io()</code>-step. More specifically, it tells the <code>io()</code>-step that it is expected to use its configuration to write data to some location. Please see the <a href="#io-step">documentation</a> for <code>io()</code>-step for more complete details on usage.</p> </div> <div class="paragraph"> <p><strong>Additional References</strong></p> </div> <div class="paragraph"> <p><a href="https://tinkerpop.apache.org/javadocs/3.7.3/full/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#write()"><code>write()</code></a></p> </div> </div> </div> </div> <div class="sect1"> <h2 id="a-note-on-predicates">A Note on Predicates</h2> <div class="sectionbody"> <div class="paragraph"> <p>A <code>P</code> is a predicate of the form <code>Function<Object,Boolean></code>. That is, given some object, return true or false. As of the release of TinkerPop 3.4.0, Gremlin also supports simple text predicates, which only work on <code>String</code> values. The <code>TextP</code> text predicates extend the <code>P</code> predicates, but are specialized in that they are of the form <code>Function<String,Boolean></code>. The provided predicates are outlined in the table below and are used in various steps such as <a href="#has-step"><code>has()</code></a>-step, <a href="#where-step"><code>where()</code></a>-step, <a href="#is-step"><code>is()</code></a>-step, etc. Two new additional <code>TextP</code> predicate members were added in the TinkerPop 3.6.0 release that allow working with regular expressions. These are <code>TextP.regex</code> and <code>TextP.notRegex</code></p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 83.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Predicate</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.eq(object)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming object equal to the provided object?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.neq(object)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming object not equal to the provided object?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.lt(number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number less than the provided number?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.lte(number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number less than or equal to the provided number?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.gt(number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number greater than the provided number?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.gte(number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number greater than or equal to the provided number?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.inside(number,number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number greater than the first provided number and less than the second?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.outside(number,number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number less than the first provided number or greater than the second?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.between(number,number)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming number greater than or equal to the first provided number and less than the second?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.within(objects…​)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming object in the array of provided objects?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>P.without(objects…​)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Is the incoming object not in the array of the provided objects?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.startingWith(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> start with the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.endingWith(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> end with the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.containing(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> contain the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.notStartingWith(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> not start with the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.notEndingWith(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> not end with the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.notContaining(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> not contain the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.regex(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> match the regular expression in the provided <code>String</code>?</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>TextP.notRegex(string)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Does the incoming <code>String</code> fail to match the regular expression in the provided <code>String</code>?</p></td> </tr> </tbody> </table> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The TinkerPop reference implementation uses the Java <code>Pattern</code> and <code>Matcher</code> classes for it regular expression engine. Other implementations may decide to use a different regular expression engine. It’s a good idea to check the documentation for the implementation you are using to verify the allowed regular expression syntax. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-411" type="radio" name="radio-set-1729796988-411" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-411" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-412" type="radio" name="radio-set-1729796988-411" class="tab-selector-2" /> <label for="tab-1729796988-412" 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> eq(<span class="integer">2</span>) ==>eq(<span class="integer">2</span>) gremlin> not(neq(<span class="integer">2</span>)) <span class="comment">//</span>// <b class="conum">(1)</b> ==>eq(<span class="integer">2</span>) gremlin> not(within(<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>)) ==>without([a, b, c]) gremlin> not(within(<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>)).test(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="predefined-constant">true</span> gremlin> not(within(<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>)).test(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) ==><span class="predefined-constant">false</span> gremlin> within(<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">3</span>).and(not(eq(<span class="integer">2</span>))).test(<span class="integer">3</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="predefined-constant">true</span> gremlin> inside(<span class="integer">1</span>,<span class="integer">4</span>).or(eq(<span class="integer">5</span>)).test(<span class="integer">3</span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="predefined-constant">true</span> gremlin> inside(<span class="integer">1</span>,<span class="integer">4</span>).or(eq(<span class="integer">5</span>)).test(<span class="integer">5</span>) ==><span class="predefined-constant">true</span> gremlin> between(<span class="integer">1</span>,<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(5)</b> ==>and(gte(<span class="integer">1</span>), lt(<span class="integer">2</span>)) gremlin> not(between(<span class="integer">1</span>,<span class="integer">2</span>)) ==>or(lt(<span class="integer">1</span>), gte(<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">eq(<span class="integer">2</span>) not(neq(<span class="integer">2</span>)) <span class="comment">//</span>// <b class="conum">(1)</b> not(within(<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>)) not(within(<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>)).test(<span class="string"><span class="delimiter">'</span><span class="content">d</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> not(within(<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>)).test(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>) within(<span class="integer">1</span>,<span class="integer">2</span>,<span class="integer">3</span>).and(not(eq(<span class="integer">2</span>))).test(<span class="integer">3</span>) <span class="comment">//</span>// <b class="conum">(3)</b> inside(<span class="integer">1</span>,<span class="integer">4</span>).or(eq(<span class="integer">5</span>)).test(<span class="integer">3</span>) <span class="comment">//</span>// <b class="conum">(4)</b> inside(<span class="integer">1</span>,<span class="integer">4</span>).or(eq(<span class="integer">5</span>)).test(<span class="integer">5</span>) between(<span class="integer">1</span>,<span class="integer">2</span>) <span class="comment">//</span>// <b class="conum">(5)</b> not(between(<span class="integer">1</span>,<span class="integer">2</span>))</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The <code>not()</code> of a <code>P</code>-predicate is another <code>P</code>-predicate.</p> </li> <li> <p><code>P</code>-predicates are arguments to various steps which internally <code>test()</code> the incoming value.</p> </li> <li> <p><code>P</code>-predicates can be and’d together.</p> </li> <li> <p><code>P</code>-predicates can be or' together.</p> </li> <li> <p><code>and()</code> is a <code>P</code>-predicate and thus, a <code>P</code>-predicate can be composed of multiple <code>P</code>-predicates.</p> </li> </ol> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> To reduce the verbosity of predicate expressions, it is good to <code>import static org.apache.tinkerpop.gremlin.process.traversal.P.*</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>The following example demonstrates how the <code>regex()</code> predicate is used and it demonstrates an important point. When using <code>regex()</code>, the string is considered a match to the pattern if any substring matches the pattern. It is therefore important to use the appropriate boundary matchers (e.g. <code>$</code> for end of a line) to ensure a proper match.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-413" type="radio" name="radio-set-1729796988-413" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-413" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-414" type="radio" name="radio-set-1729796988-413" class="tab-selector-2" /> <label for="tab-1729796988-414" 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> 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>, regex(<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>peter gremlin> 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>, regex(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>peter gremlin> 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>, regex(<span class="string"><span class="delimiter">'</span><span class="content">r$</span><span class="delimiter">'</span></span>)).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></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().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>, regex(<span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>)).values(<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">person</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, regex(<span class="string"><span class="delimiter">'</span><span class="content">r</span><span class="delimiter">'</span></span>)).values(<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">person</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, regex(<span class="string"><span class="delimiter">'</span><span class="content">r$</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>Finally, note that <a href="#where-step"><code>where()</code></a>-step takes a <code>P<String></code>. The provided string value refers to a variable binding, not to the explicit string value.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-415" type="radio" name="radio-set-1729796988-415" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-415" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-416" type="radio" name="radio-set-1729796988-415" class="tab-selector-2" /> <label for="tab-1729796988-416" 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> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).count() ==><span class="integer">30</span> gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).count() ==><span class="integer">18</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().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).count() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).both().both().as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)).count()</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="a-note-on-barrier-steps">A Note on Barrier Steps</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/barrier.png" alt="barrier" width="165"></span> Gremlin is primarily a <a href="http://en.wikipedia.org/wiki/Lazy_evaluation">lazy</a>, stream processing language. This means that Gremlin fully processes (to the best of its abilities) any traversers currently in the traversal pipeline before getting more data from the start/head of the traversal. However, there are numerous situations in which a completely lazy computation is not possible (or impractical). When a computation is not lazy, a "barrier step" exists. There are three types of barriers:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>CollectingBarrierStep</code>: All of the traversers prior to the step are put into a collection and then processed in some way (e.g. ordered) prior to the collection being "drained" one-by-one to the next step. Examples include: <a href="#order-step"><code>order()</code></a>, <a href="#sample-step"><code>sample()</code></a>, <a href="#aggregate-step"><code>aggregate()</code></a>, <a href="#barrier-step"><code>barrier()</code></a>.</p> </li> <li> <p><code>ReducingBarrierStep</code>: All of the traversers prior to the step are processed by a reduce function and once all the previous traversers are processed, a single "reduced value" traverser is emitted to the next step. Note that the path history leading up to a reducing barrier step is destroyed given its many-to-one nature. Examples include: <a href="#fold-step"><code>fold()</code></a>, <a href="#count-step"><code>count()</code></a>, <a href="#sum-step"><code>sum()</code></a>, <a href="#max-step"><code>max()</code></a>, <a href="#min-step"><code>min()</code></a>.</p> </li> <li> <p><code>SupplyingBarrierStep</code>: All of the traversers prior to the step are iterated (no processing) and then some provided supplier yields a single traverser to continue to the next step. Examples include: <a href="#cap-step"><code>cap()</code></a>.</p> </li> </ol> </div> <div class="paragraph"> <p>In Gremlin OLAP (see <a href="#traversalvertexprogram"><code>TraversalVertexProgram</code></a>), a barrier is introduced at the end of every <a href="#vertex-steps">adjacent vertex step</a>. This means that the traversal does its best to compute as much as possible at the current, local vertex. What it can’t compute without referencing an adjacent vertex is aggregated into a barrier collection. When there are no more traversers at the local vertex, the barriered traversers are the messages that are propagated to remote vertices for further processing.</p> </div> </div> </div> <div class="sect1"> <h2 id="a-note-on-scopes">A Note on Scopes</h2> <div class="sectionbody"> <div class="paragraph"> <p>The <code>Scope</code> enum has two constants: <code>Scope.local</code> and <code>Scope.global</code>. Scope determines whether the particular step being scoped is with respects to the current object (<code>local</code>) at that step or to the entire stream of objects up to that step (<code>global</code>).</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Python</div> </td> <td class="content"> <div class="paragraph"> <p>The term <code>global</code> is a reserved word in Python, and therefore a <code>Scope</code> using that term must be referred as <code>global_</code>.</p> </div> </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-417" type="radio" name="radio-set-1729796988-417" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-417" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-418" type="radio" name="radio-set-1729796988-417" class="tab-selector-2" /> <label for="tab-1729796988-418" 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> 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>).count() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="integer">2</span> gremlin> 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>).fold().count() <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="integer">1</span> gremlin> 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>).fold().count(local) <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="integer">2</span> gremlin> 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>).fold().count(global) <span class="comment">//</span>// <b class="conum">(4)</b> ==><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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).count() <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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).fold().count() <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">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>).fold().count(local) <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">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>).fold().count(global) <span class="invisible">//</span><b class="conum">4</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Marko knows 2 people.</p> </li> <li> <p>A list of Marko’s friends is created and thus, one object is counted (the single list).</p> </li> <li> <p>A list of Marko’s friends is created and a <code>local</code>-count yields the number of objects in that list.</p> </li> <li> <p><code>count(global)</code> is the same as <code>count()</code> as the default behavior for most scoped steps is <code>global</code>.</p> </li> </ol> </div> <div class="paragraph"> <p>The steps that support scoping are:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#count-step"><code>count()</code></a>: count the local collection or global stream.</p> </li> <li> <p><a href="#dedup-step"><code>dedup()</code></a>: dedup the local collection of global stream.</p> </li> <li> <p><a href="#max-step"><code>max()</code></a>: get the max value in the local collection or global stream.</p> </li> <li> <p><a href="#mean-step"><code>mean()</code></a>: get the mean value in the local collection or global stream.</p> </li> <li> <p><a href="#min-step"><code>min()</code></a>: get the min value in the local collection or global stream.</p> </li> <li> <p><a href="#order-step"><code>order()</code></a>: order the objects in the local collection or global stream.</p> </li> <li> <p><a href="#range-step"><code>range()</code></a>: clip the local collection or global stream.</p> </li> <li> <p><a href="#limit-step"><code>limit()</code></a>: clip the local collection or global stream.</p> </li> <li> <p><a href="#sample-step"><code>sample()</code></a>: sample objects from the local collection or global stream.</p> </li> <li> <p><a href="#tail-step"><code>tail()</code></a>: get the tail of the objects in the local collection or global stream.</p> </li> </ul> </div> <div class="paragraph"> <p>A few more examples of the use of <code>Scope</code> are provided below:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-419" type="radio" name="radio-set-1729796988-419" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-419" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-420" type="radio" name="radio-set-1729796988-419" class="tab-selector-2" /> <label for="tab-1729796988-420" 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> g.V().both().group().by(label).select(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).dedup(local) ==>[v[<span class="integer">3</span>],v[<span class="integer">5</span>]] gremlin> g.V().groupCount().by(label).select(values).min(local) ==><span class="integer">2</span> gremlin> g.V().groupCount().by(label).order(local).by(values,desc) ==>[<span class="key">person</span>:<span class="integer">4</span>,<span class="key">software</span>:<span class="integer">2</span>] gremlin> g.V().fold().sample(local,<span class="integer">2</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().both().group().by(label).select(<span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>).dedup(local) g.V().groupCount().by(label).select(values).min(local) g.V().groupCount().by(label).order(local).by(values,desc) g.V().fold().sample(local,<span class="integer">2</span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Finally, note that <a href="#local-step"><code>local()</code></a>-step is a "hard-scoped step" that transforms any internal traversal into a locally-scoped operation. A contrived example is provided below:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-421" type="radio" name="radio-set-1729796988-421" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-421" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-422" type="radio" name="radio-set-1729796988-421" class="tab-selector-2" /> <label for="tab-1729796988-422" 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> g.V().fold().local(unfold().count()) ==><span class="integer">6</span> gremlin> g.V().fold().count(local) ==><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().local(unfold().count()) g.V().fold().count(local)</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect1"> <h2 id="a-note-on-lambdas">A Note On Lambdas</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/lambda.png" alt="lambda" width="150"></span> A <a href="http://en.wikipedia.org/wiki/Anonymous_function">lambda</a> is a function that can be referenced by software and thus, passed around like any other piece of data. In Gremlin, lambdas make it possible to generalize the behavior of a step such that custom steps can be created (on-the-fly) by the user. However, it is advised to avoid using lambdas if possible.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-423" type="radio" name="radio-set-1729796988-423" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-423" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-424" type="radio" name="radio-set-1729796988-423" class="tab-selector-2" /> <label for="tab-1729796988-424" 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> g.V().filter{<span class="local-variable">it</span>.get().value(<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>}. flatMap{<span class="local-variable">it</span>.get().vertices(OUT,<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)}. map {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)} <span class="comment">//</span>// <b class="conum">(1)</b> ==>lop gremlin> 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>).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">(2)</b> ==>lop</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().filter{<span class="local-variable">it</span>.get().value(<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>}. flatMap{<span class="local-variable">it</span>.get().vertices(OUT,<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)}. map {<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</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>).out(<span class="string"><span class="delimiter">'</span><span class="content">created</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">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>A lambda-rich Gremlin traversal which should and can be avoided. (<strong>bad</strong>)</p> </li> <li> <p>The same traversal (result), but without using lambdas. (<strong>good</strong>)</p> </li> </ol> </div> <div class="paragraph"> <p>Gremlin attempts to provide the user a comprehensive collection of steps in the hopes that the user will never need to leverage a lambda in practice. It is advised that users only leverage a lambda if and only if there is no corresponding lambda-less step that encompasses the desired functionality. The reason being, lambdas can not be optimized by Gremlin’s compiler strategies as they can not be programmatically inspected (see <a href="#traversalstrategy">traversal strategies</a>). It is also not currently possible to send a natively written lambda for remote execution to Gremlin-Server or a driver that supports remote execution.</p> </div> <div class="paragraph"> <p>In many situations where a lambda could be used, either a corresponding step exists or a traversal can be provided in its place. A <code>TraversalLambda</code> behaves like a typical lambda, but it can be optimized and it yields less objects than the corresponding pure-lambda form.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-425" type="radio" name="radio-set-1729796988-425" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-425" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-426" type="radio" name="radio-set-1729796988-425" class="tab-selector-2" /> <label for="tab-1729796988-426" 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> g.V().out().out().path().by {<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. by {<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. by {g.V(<span class="local-variable">it</span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()} <span class="comment">//</span>// <b class="conum">(1)</b> ==>[marko,josh,[josh]] ==>[marko,josh,[marko,josh,peter]] gremlin> g.V().out().out().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[marko,josh,[josh]] ==>[marko,josh,[marko,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().out().out().path().by {<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. by {<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)}. by {g.V(<span class="local-variable">it</span>).in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()} <span class="comment">//</span>// <b class="conum">(1)</b> g.V().out().out().path().by(<span class="string"><span class="delimiter">'</span><span class="content">name</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(__.in(<span class="string"><span class="delimiter">'</span><span class="content">created</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()) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>The length-3 paths have each of their objects transformed by a lambda. (<strong>bad</strong>)</p> </li> <li> <p>The length-3 paths have their objects transformed by a lambda-less step and a traversal lambda. (<strong>good</strong>)</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="traversalstrategy">TraversalStrategy</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/traversal-strategy.png" alt="traversal strategy" width="125"></span> A <code>TraversalStrategy</code> analyzes a <code>Traversal</code> and, if the traversal meets its criteria, can mutate it accordingly. Traversal strategies are executed at compile-time and form the foundation of the Gremlin traversal machine’s compiler. There are 5 categories of strategies which are itemized below:</p> </div> <div class="ulist"> <ul> <li> <p>There is an application-level feature that can be embedded into the traversal logic (<strong>decoration</strong>).</p> </li> <li> <p>There is a more efficient way to express the traversal at the TinkerPop level (<strong>optimization</strong>).</p> </li> <li> <p>There is a more efficient way to express the traversal at the graph system/language/driver level (<strong>provider optimization</strong>).</p> </li> <li> <p>There are some final adjustments/cleanups/analyses required before executing the traversal (<strong>finalization</strong>).</p> </li> <li> <p>There are certain traversals that are not legal for the application or traversal engine (<strong>verification</strong>).</p> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <a href="#explain-step"><code>explain()</code></a>-step shows the user how each registered strategy mutates the traversal. </td> </tr> </table> </div> <div class="paragraph"> <p>TinkerPop ships with a generous number of <code>TraversalStrategy</code> definitions, most of which are applied implicitly when executing a gremlin traversal. Users and providers can add <code>TraversalStrategy</code> definitions for particular needs. The following sections detail how traversal strategies are applied and defined and describe a collection of traversal strategies that are generally useful to end-users.</p> </div> <div class="sect2"> <h3 id="_application">Application</h3> <div class="paragraph"> <p>One can explicitly add or remove <code>TraversalStrategy</code> strategies on the <code>GraphTraversalSource</code> with the <code>withStrategies()</code> and <code>withoutStrategies()</code> <a href="#start-steps">start steps</a>, see the <a href="#readonlystrategy">ReadOnlyStrategy</a> and the <a href="#barrier-step">barrier() step</a> for examples. End users typically do this as part of issuing a gremlin traversal, either on a locally opened graph or a remotely accessed graph. However, when configuring Gremlin Server, traversal strategies can also be applied on exposed <code>GraphTraversalSource</code> instances and as part of an <code>Authorizer</code> implementation, see <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#authorization">Gremlin Server Authorization</a>. Therefore, one should keep the following in mind when modifying the list of <code>TraversalStrategy</code> strategies:</p> </div> <div class="ulist"> <ul> <li> <p>A <code>TraversalStrategy</code> added to the traversal can be removed again later on. An example is the <code>conf/gremlin-server-modern-readonly.yaml</code> file from the Gremlin Server distribution, which applies the <code>ReadOnlyStrategy</code> to the <code>GraphTraversalSource</code> that remote clients can connect to. However, a remote client can remove it on its turn by applying the <code>withoutStrategies()</code> step with the <code>ReadOnlyStrategy</code>.</p> </li> <li> <p>When a <code>TraversalStrategy</code> of a particular type is added, it replaces any instances of its type that exist prior to it. Multiple instances of a <code>TraversalStrategy</code> can therefore not be registered and their functionality is no way merged automatically. Therefore, if there is a particular strategy registered whose functionality needs to be changed it is important to either find and modify the existing instance or construct a new one copying the options to keep from the old to the new instance.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_definition">Definition</h3> <div class="paragraph"> <p>A simple <code>OptimizationStrategy</code> is the <code>IdentityRemovalStrategy</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">final</span> <span class="type">class</span> <span class="class">IdentityRemovalStrategy</span> <span class="directive">extends</span> AbstractTraversalStrategy<TraversalStrategy.OptimizationStrategy> <span class="directive">implements</span> TraversalStrategy.OptimizationStrategy { <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> IdentityRemovalStrategy INSTANCE = <span class="keyword">new</span> IdentityRemovalStrategy(); <span class="directive">private</span> IdentityRemovalStrategy() { } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> apply(Traversal.Admin<?, ?> traversal) { <span class="keyword">if</span> (traversal.getSteps().size() <= <span class="integer">1</span>) <span class="keyword">return</span>; <span class="keyword">for</span> (IdentityStep<?> identityStep : TraversalHelper.getStepsOfClass(IdentityStep.class, traversal)) { <span class="keyword">if</span> (identityStep.getLabels().isEmpty() || !(identityStep.getPreviousStep() <span class="keyword">instanceof</span> EmptyStep)) { TraversalHelper.copyLabels(identityStep, identityStep.getPreviousStep(), <span class="predefined-constant">false</span>); traversal.removeStep(identityStep); } } } <span class="directive">public</span> <span class="directive">static</span> IdentityRemovalStrategy instance() { <span class="keyword">return</span> INSTANCE; } }</code></pre> </div> </div> <div class="paragraph"> <p>This strategy simply removes any <code>IdentityStep</code> steps in the Traversal as <code>aStep().identity().identity().bStep()</code> is equivalent to <code>aStep().bStep()</code>. For those traversal strategies that require other strategies to execute prior or post to the strategy, then the following two methods can be defined in <code>TraversalStrategy</code> (with defaults being an empty set). If the <code>TraversalStrategy</code> is in a particular traversal category (i.e. decoration, optimization, provider-optimization, finalization, or verification), then priors and posts are only possible within the respective category.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="predefined-type">Set</span><<span class="predefined-type">Class</span><? <span class="directive">extends</span> S>> applyPrior(); <span class="directive">public</span> <span class="predefined-type">Set</span><<span class="predefined-type">Class</span><? <span class="directive">extends</span> S>> applyPost();</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> <code>TraversalStrategy</code> categories are sorted within their category and the categories are then executed in the following order: decoration, optimization, provider optimization, finalization, and verification. If a designed strategy does not fit cleanly into these categories, then it can implement <code>TraversalStrategy</code> and its prior and posts can reference strategies within any category. However, such generalization are strongly discouraged. </td> </tr> </table> </div> <div class="paragraph"> <p>An example of a <code>GraphSystemOptimizationStrategy</code> is provided below.</p> </div> <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 class="paragraph"> <p>The expression above can be executed in a <code>O(|V|)</code> or <code>O(log(|V|)</code> fashion in <a href="#tinkergraph-gremlin">TinkerGraph</a> depending on whether there is or is not an index defined for "name."</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="directive">final</span> <span class="type">class</span> <span class="class">TinkerGraphStepStrategy</span> <span class="directive">extends</span> AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy> <span class="directive">implements</span> TraversalStrategy.ProviderOptimizationStrategy { <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> TinkerGraphStepStrategy INSTANCE = <span class="keyword">new</span> TinkerGraphStepStrategy(); <span class="directive">private</span> TinkerGraphStepStrategy() { } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> apply(Traversal.Admin<?, ?> traversal) { <span class="keyword">if</span> (TraversalHelper.onGraphComputer(traversal)) <span class="keyword">return</span>; <span class="keyword">for</span> (GraphStep originalGraphStep : TraversalHelper.getStepsOfClass(GraphStep.class, traversal)) { TinkerGraphStep<?, ?> tinkerGraphStep = <span class="keyword">new</span> TinkerGraphStep<>(originalGraphStep); TraversalHelper.replaceStep(originalGraphStep, tinkerGraphStep, traversal); Step<?, ?> currentStep = tinkerGraphStep.getNextStep(); <span class="keyword">while</span> (currentStep <span class="keyword">instanceof</span> HasStep || currentStep <span class="keyword">instanceof</span> NoOpBarrierStep) { <span class="keyword">if</span> (currentStep <span class="keyword">instanceof</span> HasStep) { <span class="keyword">for</span> (HasContainer hasContainer : ((HasContainerHolder) currentStep).getHasContainers()) { <span class="keyword">if</span> (!GraphStep.processHasContainerIds(tinkerGraphStep, hasContainer)) tinkerGraphStep.addHasContainer(hasContainer); } TraversalHelper.copyLabels(currentStep, currentStep.getPreviousStep(), <span class="predefined-constant">false</span>); traversal.removeStep(currentStep); } currentStep = currentStep.getNextStep(); } } } <span class="directive">public</span> <span class="directive">static</span> TinkerGraphStepStrategy instance() { <span class="keyword">return</span> INSTANCE; } }</code></pre> </div> </div> <div class="paragraph"> <p>The traversal is redefined by simply taking a chain of <code>has()</code>-steps after <code>g.V()</code> (<code>TinkerGraphStep</code>) and providing their <code>HasContainers</code> to <code>TinkerGraphStep</code>. Then its up to <code>TinkerGraphStep</code> to determine if an appropriate index exists. Given that the strategy uses non-TinkerPop provided steps, it should go into the <code>ProviderOptimizationStrategy</code> category to ensure the added step does not interfere with the assumptions of the <code>OptimizationStrategy</code> strategies.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-427" type="radio" name="radio-set-1729796988-427" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-427" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-428" type="radio" name="radio-set-1729796988-427" class="tab-selector-2" /> <label for="tab-1729796988-428" 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> t = 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="predefined-constant">null</span> ==><span class="predefined-constant">null</span> gremlin> t.toString() ==>[GraphStep(vertex,<span class="type">[]</span>), HasStep([name.eq(marko)])] gremlin> t.iterate(); <span class="predefined-constant">null</span> ==><span class="predefined-constant">null</span> gremlin> t.toString() ==>[TinkerGraphStep(vertex,[name.eq(marko)]), NoneStep]</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().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="predefined-constant">null</span> t.toString() t.iterate(); <span class="predefined-constant">null</span> t.toString()</code></pre> </div> </div> </div> </div> </section> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> The reason that <code>OptimizationStrategy</code> and <code>ProviderOptimizationStrategy</code> are two different categories is that optimization strategies should only rewrite the traversal using TinkerPop steps. This ensures that the optimizations executed at the end of the optimization strategy round are TinkerPop compliant. From there, provider optimizations can analyze the traversal and rewrite the traversal as desired using graph system specific steps (e.g. replacing <code>GraphStep.HasStep…​HasStep</code> with <code>TinkerGraphStep</code>). If provider optimizations use graph system specific steps and implement <code>OptimizationStrategy</code>, then other TinkerPop optimizations may fail to optimize the traversal or mis-understand the graph system specific step behaviors (e.g. <code>ProviderVertexStep extends VertexStep</code>) and yield incorrect semantics. </td> </tr> </table> </div> <div class="paragraph"> <p>Finally, here is a complicated traversal that has various components that are optimized by the default TinkerPop strategies.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-429" type="radio" name="radio-set-1729796988-429" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-429" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-430" type="radio" name="radio-set-1729796988-429" class="tab-selector-2" /> <label for="tab-1729796988-430" 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> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> and(has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(2)</b> 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>), filter(has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,gt(<span class="integer">20</span>)))). <span class="comment">//</span>// <b class="conum">(3)</b> match(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,lt(<span class="integer">32</span>)), <span class="comment">//</span>// <b class="conum">(4)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(outE().inV()).times(<span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(6)</b> where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).both().count().is(gt(<span class="integer">1</span>))). <span class="comment">//</span>// <b class="conum">(7)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(8)</b> groupCount(). by(out().count()). <span class="comment">//</span>// <b class="conum">(9)</b> explain() ==>Traversal Explanation ================================================================================================================================================================================================================================================ Original Traversal [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), AndStep([[TraversalFilterStep([PropertiesStep([name],value)])], [HasStep([name.eq(marko)])], [TraversalFilterStep([HasStep([age.gt(<span class="integer">20</span>)])])]]), Mat chStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([age.lt(<span class="integer">32</span>)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)]]), WherePredicateStep(a,neq(b)), WhereTraversalStep([WhereStartStep(b), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,vertex), CountGlobalStep])] ConnectiveStrategy [D] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), AndStep([[TraversalFilterStep([PropertiesStep([name],value)])], [HasStep([name.eq(marko)])], [TraversalFilterStep([HasStep([age.gt(<span class="integer">20</span>)])])]]), Mat chStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([age.lt(<span class="integer">32</span>)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)]]), WherePredicateStep(a,neq(b)), WhereTraversalStep([WhereStartStep(b), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,vertex), CountGlobalStep])] IdentityRemovalStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), AndStep([[TraversalFilterStep([PropertiesStep([name],value)])], [HasStep([name.eq(marko)])], [TraversalFilterStep([HasStep([age.gt(<span class="integer">20</span>)])])]]), Mat chStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([age.lt(<span class="integer">32</span>)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)]]), WherePredicateStep(a,neq(b)), WhereTraversalStep([WhereStartStep(b), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,vertex), CountGlobalStep])] MatchPredicateStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), AndStep([[TraversalFilterStep([PropertiesStep([name],value)])], [HasStep([name.eq(marko)])], [TraversalFilterStep([HasStep([age.gt(<span class="integer">20</span>)])])]]), Mat chStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([age.lt(<span class="integer">32</span>)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,vertex), CountGlobalStep, Is Step(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobalStep])] FilterRankingStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), AndStep([[TraversalFilterStep([PropertiesStep([name],value)])], [HasStep([name.eq(marko)])], [TraversalFilterStep([HasStep([age.gt(<span class="integer">20</span>)])])]]), Mat chStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), HasStep([age.lt(<span class="integer">32</span>)]), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(a), RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,vertex), CountGlobalStep, Is Step(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobalStep])] InlineFilterStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],value)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a) , RepeatStep([VertexStep(OUT,edge), EdgeVertexStep(IN), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [Match StartStep(b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,ve rtex), CountGlobalStep])] IncidentToAdjacentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],value)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a) , RepeatStep([VertexStep(OUT,vertex), RepeatEndStep],until(loops(<span class="integer">2</span>)),emit(<span class="predefined-constant">false</span>)), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), Wher eTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobal Step])] RepeatUnrollStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],value)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a) , VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep(nu ll), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobalStep])] PathRetractionStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],value)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a) , VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep(nu ll), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobalStep])] EarlyLimitStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],value)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a) , VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep(nu ll), VertexStep(BOTH,vertex), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,vertex), CountGlobalStep])] AdjacentToIncidentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep (a), VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep (<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,edge), CountGlobalStep])] ByModulatorOptimizationStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep (a), VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep (<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,edge), CountGlobalStep])] CountStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep (a), VertexStep(OUT,vertex), VertexStep(OUT,vertex), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep(b), WhereTraversalStep([WhereStartStep (<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([VertexStep(OUT,edge), CountGlobalStep])] LazyBarrierStrategy [O] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep (a), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchSt artStep(b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep( [VertexStep(OUT,edge), CountGlobalStep])] TinkerGraphCountStrategy [P] [GraphStep(vertex,<span class="type">[]</span>), HasStep([~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep (a), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchSt artStep(b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep( [VertexStep(OUT,edge), CountGlobalStep])] TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,[~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), Ve rtexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep (b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,edge), CountGlobalStep])] ProfileStrategy [F] [TinkerGraphStep(vertex,[~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), Ve rtexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep (b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,edge), CountGlobalStep])] StandardVerificationStrategy [V] [TinkerGraphStep(vertex,[~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), Ve rtexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep (b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,edge), CountGlobalStep])] Final Traversal [TinkerGraphStep(vertex,[~label.eq(person)]), TraversalFilterStep([PropertiesStep([name],property)]), HasStep([name.eq(marko), age.gt(<span class="integer">20</span>), age.lt(<span class="integer">32</span>)])<span class="error">@</span>[a], MatchStep(<span class="predefined-constant">null</span>,AND,[[MatchStartStep(a), Ve rtexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), VertexStep(OUT,vertex), NoOpBarrierStep(<span class="integer">2500</span>), MatchEndStep(b)], [MatchStartStep(a), WherePredicateStep(<span class="predefined-constant">null</span>,neq(b)), MatchEndStep(<span class="predefined-constant">null</span>)], [MatchStartStep (b), WhereTraversalStep([WhereStartStep(<span class="predefined-constant">null</span>), VertexStep(BOTH,edge), RangeGlobalStep(<span class="integer">0</span>,<span class="integer">2</span>), CountGlobalStep, IsStep(gt(<span class="integer">1</span>))]), MatchEndStep(<span class="predefined-constant">null</span>)]]), SelectOneStep(last,b,<span class="predefined-constant">null</span>), GroupCountStep([Vertex Step(OUT,edge), CountGlobalStep])]</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>). <span class="comment">//</span>// <b class="conum">(1)</b> and(has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>), <span class="comment">//</span>// <b class="conum">(2)</b> 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>), filter(has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,gt(<span class="integer">20</span>)))). <span class="comment">//</span>// <b class="conum">(3)</b> match(__.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).has(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,lt(<span class="integer">32</span>)), <span class="comment">//</span>// <b class="conum">(4)</b> __.as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).repeat(outE().inV()).times(<span class="integer">2</span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(5)</b> where(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>,neq(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>)). <span class="comment">//</span>// <b class="conum">(6)</b> where(__.as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).both().count().is(gt(<span class="integer">1</span>))). <span class="comment">//</span>// <b class="conum">(7)</b> select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(8)</b> groupCount(). by(out().count()). <span class="comment">//</span>// <b class="conum">(9)</b> explain()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p><code>TinkerGraphStepStrategy</code> pulls in <code>has()</code>-step predicates for global, graph-centric index lookups.</p> </li> <li> <p><code>FilterRankStrategy</code> sorts filter steps by their time/space execution costs.</p> </li> <li> <p><code>InlineFilterStrategy</code> de-nests filters to increase the likelihood of filter concatenation and aggregation.</p> </li> <li> <p><code>InlineFilterStrategy</code> pulls out named predicates from <code>match()</code>-step to more easily allow provider strategies to use indices.</p> </li> <li> <p><code>RepeatUnrollStrategy</code> will unroll loops and <code>IncidentToAdjacentStrategy</code> will turn <code>outE().inV()</code>-patterns into <code>out()</code>.</p> </li> <li> <p><code>MatchPredicateStrategy</code> will pull in <code>where()</code>-steps so that they can be subjected to <code>match()</code>-steps runtime query optimizer.</p> </li> <li> <p><code>CountStrategy</code> will limit the traversal to only the number of traversers required for the <code>count().is(x)</code>-check.</p> </li> <li> <p><code>PathRetractionStrategy</code> will remove paths from the traversers and increase the likelihood of bulking as path data is not required after <code>select('b')</code>.</p> </li> <li> <p><code>AdjacentToIncidentStrategy</code> will turn <code>out()</code> into <code>outE()</code> to increase data access locality.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="_edgelabelverificationstrategy">EdgeLabelVerificationStrategy</h3> <div class="paragraph"> <p><code>EdgeLabelVerificationStrategy</code> prevents traversals from writing traversals that do not explicitly specify and edge label when using steps like <code>out()</code>, 'in()', 'both()' and their related <code>E</code> oriented steps, providing the option to throw an exception, log a warning or do both when one of these keys is encountered in a mutating step.</p> </div> <section class="tabs tabs-5"> <input id="tab-1729796988-431" type="radio" name="radio-set-1729796988-431" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-431" class="tab-label-1">java</label> <input id="tab-1729796988-432" type="radio" name="radio-set-1729796988-431" class="tab-selector-2" /> <label for="tab-1729796988-432" class="tab-label-2">groovy</label> <input id="tab-1729796988-433" type="radio" name="radio-set-1729796988-431" class="tab-selector-3" /> <label for="tab-1729796988-433" class="tab-label-3">csharp</label> <input id="tab-1729796988-434" type="radio" name="radio-set-1729796988-431" class="tab-selector-4" /> <label for="tab-1729796988-434" class="tab-label-4">javascript</label> <input id="tab-1729796988-435" type="radio" name="radio-set-1729796988-431" class="tab-selector-5" /> <label for="tab-1729796988-435" class="tab-label-5">python</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">EdgeLabelVerificationStrategy verificationStrategy = EdgeLabelVerificationStrategy.build() .throwException().create() <span class="comment">// results in VerificationException - as out() does not have a label specified</span> g.withStrategies(verificationStrategy).V(<span class="integer">1</span>).out().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"><span class="comment">// results in VerificationException - as out() does not have a label specified</span> g.withStrategies(<span class="keyword">new</span> EdgeLabelVerificationStrategy(<span class="key">throwException</span>: <span class="predefined-constant">true</span>)) .V(<span class="integer">1</span>).out().iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">// results in VerificationException - as out() does not have a label specified g.WithStrategies(new EdgeLabelVerificationStrategy(throwException: true)) .V(1).Out().Iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">// results in Error - as out() does not have a label specified</span> g.withStrategies(<span class="keyword">new</span> EdgeLabelVerificationStrategy(throwException: <span class="predefined-constant">true</span>)) .V(<span class="integer">1</span>).out().iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">// results <span class="keyword">in</span> Error - <span class="keyword">as</span> out() does <span class="keyword">not</span> have a label specified g.withStrategies(EdgeLabelVerificationStrategy(throwException=true)) .V(<span class="integer">1</span>).out().iterate()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_elementidstrategy">ElementIdStrategy</h3> <div class="paragraph"> <p><code>ElementIdStrategy</code> provides control over element identifiers. Some Graph implementations, such as TinkerGraph, allow specification of custom identifiers when creating elements:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-436" type="radio" name="radio-set-1729796988-436" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-436" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-437" type="radio" name="radio-set-1729796988-436" class="tab-selector-2" /> <label for="tab-1729796988-437" 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> g = traversal().withEmbedded(TinkerGraph.open()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> v = g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>).next() ==>v[<span class="integer">42</span>a] gremlin> g.V(<span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">42</span>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 = traversal().withEmbedded(TinkerGraph.open()) v = g.addV().property(id,<span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>).next() g.V(<span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Other <code>Graph</code> implementations, such as Neo4j, generate element identifiers automatically and cannot be assigned. As a helper, <code>ElementIdStrategy</code> can be used to make identifier assignment possible by using vertex and edge indices under the hood.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-438" type="radio" name="radio-set-1729796988-438" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-438" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-439" type="radio" name="radio-set-1729796988-438" class="tab-selector-2" /> <label for="tab-1729796988-439" 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> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> strategy = ElementIdStrategy.build().create() ==>ElementIdStrategy gremlin> g = traversal().withEmbedded(graph).withStrategies(strategy) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>).id() ==><span class="integer">42</span>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">graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) strategy = ElementIdStrategy.build().create() g = traversal().withEmbedded(graph).withStrategies(strategy) g.addV().property(id, <span class="string"><span class="delimiter">'</span><span class="content">42a</span><span class="delimiter">'</span></span>).id()</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 key that is used to store the assigned identifier should be indexed in the underlying graph database. If it is not indexed, then lookups for the elements that use these identifiers will perform a linear scan. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_eventstrategy">EventStrategy</h3> <div class="paragraph"> <p>The purpose of the <code>EventStrategy</code> is to raise events to one or more <code>MutationListener</code> objects as changes to the underlying <code>Graph</code> occur within a <code>Traversal</code>. Such a strategy is useful for logging changes, triggering certain actions based on change, or any application that needs notification of some mutating operation during a <code>Traversal</code>. If the transaction is rolled back, the event queue is reset.</p> </div> <div class="paragraph"> <p>The following events are raised to the <code>MutationListener</code>:</p> </div> <div class="ulist"> <ul> <li> <p>New vertex</p> </li> <li> <p>New edge</p> </li> <li> <p>Vertex property changed</p> </li> <li> <p>Edge property changed</p> </li> <li> <p>Vertex property removed</p> </li> <li> <p>Edge property removed</p> </li> <li> <p>Vertex removed</p> </li> <li> <p>Edge removed</p> </li> </ul> </div> <div class="paragraph"> <p>To start processing events from a <code>Traversal</code> first implement the <code>MutationListener</code> interface. An example of this implementation is the <code>ConsoleMutationListener</code> which writes output to the console for each event. The following console session displays the basic usage:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-440" type="radio" name="radio-set-1729796988-440" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-440" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-441" type="radio" name="radio-set-1729796988-440" class="tab-selector-2" /> <label for="tab-1729796988-441" 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> <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.step.util.event.*</span> ==>org.apache.tinkerpop.gremlin.process.traversal.step.util.event.* gremlin> graph = TinkerFactory.createModern() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> l = <span class="keyword">new</span> ConsoleMutationListener(graph) ==>MutationListener[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>]] gremlin> strategy = EventStrategy.build().addListener(l).create() ==>EventStrategy gremlin> g = traversal().withEmbedded(graph).withStrategies(strategy) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> 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">stephen</span><span class="delimiter">'</span></span>) Vertex [v[<span class="integer">0</span>]] added to graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] ==>v[<span class="integer">0</span>] gremlin> 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">stephen</span><span class="delimiter">'</span></span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">centreville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">1990</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2000</span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">dulles</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2000</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">purcellville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</span>) Vertex [v[<span class="integer">0</span>]] property [vp[empty]] change to [centreville] <span class="keyword">in</span> graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] Vertex [v[<span class="integer">0</span>]] property [vp[empty]] change to [dulles] <span class="keyword">in</span> graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] Vertex [v[<span class="integer">0</span>]] property [vp[empty]] change to [purcellville] <span class="keyword">in</span> graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] ==>v[<span class="integer">0</span>] gremlin> 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">stephen</span><span class="delimiter">'</span></span>). property(set, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">purcellville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2019</span>) Vertex [v[<span class="integer">0</span>]] property [vp[location->purcellville]] change to [purcellville] <span class="keyword">in</span> graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] ==>v[<span class="integer">0</span>] gremlin> g.E().drop() Edge [e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>]] Edge [e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">5</span>]] Edge [e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">4</span>]] Edge [e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">3</span>]] Edge [e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">2</span>]] Edge [e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>]] removed from graph [tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</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"><span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.step.util.event.*</span> graph = TinkerFactory.createModern() l = <span class="keyword">new</span> ConsoleMutationListener(graph) strategy = EventStrategy.build().addListener(l).create() g = traversal().withEmbedded(graph).withStrategies(strategy) 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">stephen</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">stephen</span><span class="delimiter">'</span></span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">centreville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">1990</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2000</span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">dulles</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2000</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</span>). property(list, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">purcellville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</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">stephen</span><span class="delimiter">'</span></span>). property(set, <span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">purcellville</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">startTime</span><span class="delimiter">'</span></span>, <span class="integer">2006</span>, <span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>, <span class="integer">2019</span>) g.E().drop()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>By default, the <code>EventStrategy</code> is configured with an <code>EventQueue</code> that raises events as they occur within execution of a <code>Step</code>. As such, the final line of Gremlin execution that drops all edges shows a bit of an inconsistent count, where the removed edge count is accounted for after the event is raised. The strategy can also be configured with a <code>TransactionalEventQueue</code> that captures the changes within a transaction and does not allow them to fire until the transaction is committed.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> <code>EventStrategy</code> is not meant for usage in tracking global mutations across separate processes. In other words, a mutation in one JVM process is not raised as an event in a different JVM process. In addition, events are not raised when mutations occur outside of the <code>Traversal</code> context. </td> </tr> </table> </div> <div class="paragraph"> <p>Another default configuration for <code>EventStrategy</code> revolves around the concept of "detachment". Graph elements are detached from the graph as copies when passed to referring mutation events. Therefore, when adding a new <code>Vertex</code> in TinkerGraph, the event will not contain a <code>TinkerVertex</code> but will instead include a <code>DetachedVertex</code>. This behavior can be modified with the <code>detach()</code> method on the <code>EventStrategy.Builder</code> which accepts the following inputs: <code>null</code> meaning no detachment and the return of the original element, <code>DetachedFactory</code> which is the same as the default behavior, and <code>ReferenceFactory</code> which will return "reference" elements only with no properties.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> If setting the <code>detach()</code> configuration to <code>null</code>, be aware that transactional graphs will likely create a new transaction immediately following the <code>commit()</code> that raises the events. The graph elements raised in the events may also not behave as "snapshots" at the time of their creation as they are "live" references to actual database elements. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="partitionstrategy">PartitionStrategy</h3> <div class="imageblock"> <div class="content"> <img src="../images/partition-graph.png" alt="partition graph" width="325"> </div> </div> <div class="paragraph"> <p><code>PartitionStrategy</code> partitions the vertices and edges of a graph into <code>String</code> named partitions (i.e. buckets, subgraphs, etc.). The idea behind <code>PartitionStrategy</code> is presented in the image above where each element is in a single partition (represented by its color). Partitions can be read from, written to, and linked/joined by edges that span one or two partitions (e.g. a tail vertex in one partition and a head vertex in another).</p> </div> <div class="paragraph"> <p>There are three primary configurations in <code>PartitionStrategy</code>:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Partition Key - The property key that denotes a String value representing a partition.</p> </li> <li> <p>Write Partition - A <code>String</code> denoting what partition all future written elements will be in.</p> </li> <li> <p>Read Partitions - A <code>Set<String></code> of partitions that can be read from.</p> </li> </ol> </div> <div class="paragraph"> <p>The best way to understand <code>PartitionStrategy</code> is via example.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-442" type="radio" name="radio-set-1729796988-442" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-442" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-443" type="radio" name="radio-set-1729796988-442" class="tab-selector-2" /> <label for="tab-1729796988-443" 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> graph = TinkerFactory.createModern() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> strategyA = <span class="keyword">new</span> PartitionStrategy(<span class="key">partitionKey</span>: <span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, <span class="key">writePartition</span>: <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>, <span class="key">readPartitions</span>: [<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>]) ==>PartitionStrategy gremlin> strategyB = <span class="keyword">new</span> PartitionStrategy(<span class="key">partitionKey</span>: <span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, <span class="key">writePartition</span>: <span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>, <span class="key">readPartitions</span>: [<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>]) ==>PartitionStrategy gremlin> gA = traversal().withEmbedded(graph).withStrategies(strategyA) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> gA.addV() <span class="comment">// this vertex has a property of {_partition:"a"}</span> ==>v[<span class="integer">0</span>] gremlin> gB = traversal().withEmbedded(graph).withStrategies(strategyB) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">7</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> gB.addV() <span class="comment">// this vertex has a property of {_partition:"b"}</span> ==>v[<span class="integer">13</span>] gremlin> gA.V() ==>v[<span class="integer">0</span>] gremlin> gB.V() ==>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">graph = TinkerFactory.createModern() strategyA = <span class="keyword">new</span> PartitionStrategy(<span class="key">partitionKey</span>: <span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, <span class="key">writePartition</span>: <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>, <span class="key">readPartitions</span>: [<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>]) strategyB = <span class="keyword">new</span> PartitionStrategy(<span class="key">partitionKey</span>: <span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, <span class="key">writePartition</span>: <span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>, <span class="key">readPartitions</span>: [<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>]) gA = traversal().withEmbedded(graph).withStrategies(strategyA) gA.addV() <span class="comment">// this vertex has a property of {_partition:"a"}</span> gB = traversal().withEmbedded(graph).withStrategies(strategyB) gB.addV() <span class="comment">// this vertex has a property of {_partition:"b"}</span> gA.V() gB.V()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The following examples demonstrate the above <code>PartitionStrategy</code> definition for "strategyA" in other programming languages:</p> </div> <section class="tabs tabs-4"> <input id="tab-1729796988-444" type="radio" name="radio-set-1729796988-444" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-444" class="tab-label-1">java</label> <input id="tab-1729796988-445" type="radio" name="radio-set-1729796988-444" class="tab-selector-2" /> <label for="tab-1729796988-445" class="tab-label-2">csharp</label> <input id="tab-1729796988-446" type="radio" name="radio-set-1729796988-444" class="tab-selector-3" /> <label for="tab-1729796988-446" class="tab-label-3">javascript</label> <input id="tab-1729796988-447" type="radio" name="radio-set-1729796988-444" class="tab-selector-4" /> <label for="tab-1729796988-447" class="tab-label-4">python</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">PartitionStrategy strategyA = PartitionStrategy.build().partitionKey(<span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>) .writePartition(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>) .readPartitions(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).create();</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="csharp">PartitionStrategy strategyA = new PartitionStrategy( partitionKey: "_partition", writePartition: "a", readPartitions: new List<string>(){"a"});</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const strategyA = <span class="keyword">new</span> PartitionStrategy(partitionKey: <span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, <span class="key">writePartition</span>: <span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>, <span class="key">readPartitions</span>: [<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>])</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">strategyA = PartitionStrategy(partitionKey=<span class="string"><span class="delimiter">"</span><span class="content">_partition</span><span class="delimiter">"</span></span>, writePartition=<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>, readPartitions=[<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>Partitions may also extend to <code>VertexProperty</code> elements if the <code>Graph</code> can support meta-properties and if the <code>includeMetaProperties</code> value is set to <code>true</code> when the <code>PartitionStrategy</code> is built. The <code>partitionKey</code> will be stored in the meta-properties of the <code>VertexProperty</code> and blind the traversal to those properties. Please note that the <code>VertexProperty</code> will only be hidden by way of the <code>Traversal</code> itself. For example, calling <code>Vertex.property(k)</code> bypasses the context of the <code>PartitionStrategy</code> and will thus allow all properties to be accessed.</p> </div> <div class="paragraph"> <p>By writing elements to particular partitions and then restricting read partitions, the developer is able to create multiple graphs within a single address space. Moreover, by supporting references between partitions, it is possible to merge those multiple graphs (i.e. join partitions).</p> </div> </div> <div class="sect2"> <h3 id="readonlystrategy">ReadOnlyStrategy</h3> <div class="paragraph"> <p><code>ReadOnlyStrategy</code> is largely self-explanatory. A <code>Traversal</code> that has this strategy applied will throw an <code>IllegalStateException</code> if the <code>Traversal</code> has any mutating steps within it.</p> </div> <section class="tabs tabs-5"> <input id="tab-1729796988-448" type="radio" name="radio-set-1729796988-448" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-448" class="tab-label-1">java</label> <input id="tab-1729796988-449" type="radio" name="radio-set-1729796988-448" class="tab-selector-2" /> <label for="tab-1729796988-449" class="tab-label-2">groovy</label> <input id="tab-1729796988-450" type="radio" name="radio-set-1729796988-448" class="tab-selector-3" /> <label for="tab-1729796988-450" class="tab-label-3">csharp</label> <input id="tab-1729796988-451" type="radio" name="radio-set-1729796988-448" class="tab-selector-4" /> <label for="tab-1729796988-451" class="tab-label-4">javascript</label> <input id="tab-1729796988-452" type="radio" name="radio-set-1729796988-448" class="tab-selector-5" /> <label for="tab-1729796988-452" class="tab-label-5">python</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">ReadOnlyStrategy verificationStrategy = ReadOnlyStrategy.instance(); <span class="comment">// results in VerificationException</span> g.withStrategies(verificationStrategy).addV(<span class="string"><span class="delimiter">'</span><span class="content">person</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"><span class="comment">// results in VerificationException</span> g.withStrategies(ReadOnlyStrategy).addV(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">// results in VerificationException g.WithStrategies(new ReadOnlyStrategy()).addV("person").Iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">// results in Error</span> g.withStrategies(<span class="keyword">new</span> ReadOnlyStrategy()).addV(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">// results <span class="keyword">in</span> Error g.withStrategies(ReadOnlyStrategy).addV(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).iterate()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_reservedkeysverificationstrategy">ReservedKeysVerificationStrategy</h3> <div class="paragraph"> <p><code>ReservedKeysVerificationStrategy</code> prevents traversals from adding property keys that are protected, providing the option to throw an exception, log a warning or do both when one of these keys is encountered in a mutating step. By default "id" and "label" are considered "reserved" but the default can be changed by building with the <code>reservedKeys()</code> options and supply a <code>Set</code> of keys to trigger the <code>VerificationException</code>.</p> </div> <section class="tabs tabs-5"> <input id="tab-1729796988-453" type="radio" name="radio-set-1729796988-453" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-453" class="tab-label-1">java</label> <input id="tab-1729796988-454" type="radio" name="radio-set-1729796988-453" class="tab-selector-2" /> <label for="tab-1729796988-454" class="tab-label-2">groovy</label> <input id="tab-1729796988-455" type="radio" name="radio-set-1729796988-453" class="tab-selector-3" /> <label for="tab-1729796988-455" class="tab-label-3">csharp</label> <input id="tab-1729796988-456" type="radio" name="radio-set-1729796988-453" class="tab-selector-4" /> <label for="tab-1729796988-456" class="tab-label-4">javascript</label> <input id="tab-1729796988-457" type="radio" name="radio-set-1729796988-453" class="tab-selector-5" /> <label for="tab-1729796988-457" class="tab-label-5">python</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">ReservedKeysVerificationStrategy verificationStrategy = ReservedKeysVerificationStrategy.build() .throwException().create() <span class="comment">// results in VerificationException</span> g.withStrategies(verificationStrategy).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">id</span><span class="delimiter">"</span></span>,<span class="integer">123</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"><span class="comment">// results in VerificationException</span> g.withStrategies(<span class="keyword">new</span> ReservedKeysVerificationStrategy(<span class="key">throwException</span>: <span class="predefined-constant">true</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">id</span><span class="delimiter">"</span></span>,<span class="integer">123</span>).iterate()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">// results in VerificationException g.WithStrategies(new ReservedKeysVerificationStrategy(throwException: true)) .AddV('person').Property("id",123).Iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">// results in Error</span> g.withStrategies(<span class="keyword">new</span> ReservedKeysVerificationStrategy(throwException: <span class="predefined-constant">true</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">id</span><span class="delimiter">"</span></span>,<span class="integer">123</span>).iterate();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">// results <span class="keyword">in</span> Error g.withStrategies(ReservedKeysVerificationStrategy(throwException=true)) .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">id</span><span class="delimiter">"</span></span>,<span class="integer">123</span>).iterate()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_seedstrategy">SeedStrategy</h3> <div class="paragraph"> <p>There are number of components of the Gremlin language that, by design, can produce non-deterministic results:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#coin-step">coin()</a></p> </li> <li> <p><a href="#order-step">order()</a> when <code>Order.shuffle</code> is used</p> </li> <li> <p><a href="#sample-step">sample()</a></p> </li> </ul> </div> <div class="paragraph"> <p>To get these steps to return deterministic results, <code>SeedStrategy</code> allows assignment of a seed value to the <code>Random</code> operations of the steps. The following example demonstrates the random nature of <code>shuffle</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-458" type="radio" name="radio-set-1729796988-458" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-458" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-459" type="radio" name="radio-set-1729796988-458" class="tab-selector-2" /> <label for="tab-1729796988-459" 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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[vadas,ripple,lop,marko,peter,josh] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[lop,peter,vadas,josh,marko,ripple] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[marko,ripple,josh,peter,lop,vadas] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[vadas,josh,lop,marko,peter,ripple] gremlin> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[vadas,ripple,marko,peter,josh,lop]</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">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>With <code>SeedStrategy</code> in place, however, the same order is applied each time:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-460" type="radio" name="radio-set-1729796988-460" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-460" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-461" type="radio" name="radio-set-1729796988-460" class="tab-selector-2" /> <label for="tab-1729796988-461" 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> seedStrategy = SeedStrategy.build().seed(<span class="integer">999998L</span>).create() ==>SeedStrategy gremlin> g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[peter,josh,marko,lop,ripple,vadas] gremlin> g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[peter,josh,marko,lop,ripple,vadas] gremlin> g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[peter,josh,marko,lop,ripple,vadas] gremlin> g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[peter,josh,marko,lop,ripple,vadas] gremlin> g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) ==>[peter,josh,marko,lop,ripple,vadas]</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">seedStrategy = SeedStrategy.build().seed(<span class="integer">999998L</span>).create() g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle) g.withStrategies(seedStrategy).V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).fold().order(local).by(shuffle)</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"> <code>SeedStrategy</code> only makes specific steps behave in a deterministic fashion and does not necessarily make the entire traversal deterministic itself. If the underlying graph database or processing engine happens to not guarantee iteration order, then it is possible that the final result of the traversal will appear to be non-deterministic. In these cases, it would be necessary to enforce a deterministic iteration with <code>order()</code> prior to these steps that make use of randomness to return results. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="subraphstrategy">SubgraphStrategy</h3> <div class="paragraph"> <p><code>SubgraphStrategy</code> is similar to <code>PartitionStrategy</code> in that it constrains a <code>Traversal</code> to certain vertices, edges, and vertex properties as determined by a <code>Traversal</code>-based criterion defined individually for each.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-462" type="radio" name="radio-set-1729796988-462" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-462" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-463" type="radio" name="radio-set-1729796988-462" class="tab-selector-2" /> <label for="tab-1729796988-463" 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> graph = TinkerFactory.createTheCrew() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>], standard] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() ==>[<span class="key">a</span>:marko,<span class="key">b</span>:san diego] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:santa cruz] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:brussels] ==>[<span class="key">a</span>:marko,<span class="key">b</span>:santa fe] ==>[<span class="key">a</span>:stephen,<span class="key">b</span>:centreville] ==>[<span class="key">a</span>:stephen,<span class="key">b</span>:dulles] ==>[<span class="key">a</span>:stephen,<span class="key">b</span>:purcellville] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:bremen] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:baltimore] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:oakland] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:seattle] ==>[<span class="key">a</span>:daniel,<span class="key">b</span>:spremberg] ==>[<span class="key">a</span>:daniel,<span class="key">b</span>:kaiserslautern] ==>[<span class="key">a</span>:daniel,<span class="key">b</span>:aachen] gremlin> g = g.withStrategies(<span class="keyword">new</span> SubgraphStrategy(<span class="key">vertexProperties</span>: hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>))) <span class="comment">//</span>// <b class="conum">(2)</b> ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>], standard] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() ==>[<span class="key">a</span>:marko,<span class="key">b</span>:santa fe] ==>[<span class="key">a</span>:stephen,<span class="key">b</span>:purcellville] ==>[<span class="key">a</span>:matthias,<span class="key">b</span>:seattle] ==>[<span class="key">a</span>:daniel,<span class="key">b</span>:aachen] gremlin> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by().explain() ==>Traversal Explanation ============================================================================================================================================================================================================================================= Original Traversal [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],value)<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] SubgraphStrategy [D] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), TraversalFilterStep([NotStep([PropertiesStep([endTime],value)])]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity ])] ConnectiveStrategy [D] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), TraversalFilterStep([NotStep([PropertiesStep([endTime],value)])]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity ])] IdentityRemovalStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), TraversalFilterStep([NotStep([PropertiesStep([endTime],value)])]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity ])] MatchPredicateStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), TraversalFilterStep([NotStep([PropertiesStep([endTime],value)])]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity ])] FilterRankingStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), TraversalFilterStep([NotStep([PropertiesStep([endTime],value)])]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity ])] InlineFilterStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],value)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] IncidentToAdjacentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],value)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] RepeatUnrollStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],value)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] PathRetractionStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],value)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] EarlyLimitStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],value)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] AdjacentToIncidentStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] ByModulatorOptimizationStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] CountStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] LazyBarrierStrategy [O] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] TinkerGraphCountStrategy [P] [GraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] ProfileStrategy [F] [TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] StandardVerificationStrategy [V] [TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])] Final Traversal [TinkerGraphStep(vertex,<span class="type">[]</span>)<span class="error">@</span>[a], PropertiesStep([location],property), NotStep([PropertiesStep([endTime],property)]), PropertyValueStep<span class="error">@</span>[b], SelectStep(last,[a, b],[value(name), identity])]</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">graph = TinkerFactory.createTheCrew() g = traversal().withEmbedded(graph) g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(1)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() g = g.withStrategies(<span class="keyword">new</span> SubgraphStrategy(<span class="key">vertexProperties</span>: hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>))) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>). <span class="comment">//</span>// <b class="conum">(3)</b> 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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by() g.V().as(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).as(<span class="string"><span class="delimiter">'</span><span class="content">b</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>).by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).by().explain()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Get all vertices and their vertex property locations.</p> </li> <li> <p>Create a <code>SubgraphStrategy</code> where vertex properties must not have an <code>endTime</code>-property (thus, the current location).</p> </li> <li> <p>Get all vertices and their current vertex property locations.</p> </li> </ol> </div> <div class="paragraph"> <p>The following examples demonstrate the above <code>SubgraphStrategy</code> definition in other programming languages:</p> </div> <section class="tabs tabs-4"> <input id="tab-1729796988-464" type="radio" name="radio-set-1729796988-464" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-464" class="tab-label-1">java</label> <input id="tab-1729796988-465" type="radio" name="radio-set-1729796988-464" class="tab-selector-2" /> <label for="tab-1729796988-465" class="tab-label-2">csharp</label> <input id="tab-1729796988-466" type="radio" name="radio-set-1729796988-464" class="tab-selector-3" /> <label for="tab-1729796988-466" class="tab-label-3">javascript</label> <input id="tab-1729796988-467" type="radio" name="radio-set-1729796988-464" class="tab-selector-4" /> <label for="tab-1729796988-467" class="tab-label-4">python</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.withStrategies(SubgraphStrategy.build().vertexProperties(hasNot(<span class="string"><span class="delimiter">"</span><span class="content">endTime</span><span class="delimiter">"</span></span>)).create());</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="csharp">g.WithStrategies(new SubgraphStrategy(vertexProperties: HasNot("endTime")));</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">g.withStrategies(<span class="keyword">new</span> SubgraphStrategy(vertexProperties: hasNot(<span class="string"><span class="delimiter">"</span><span class="content">endTime</span><span class="delimiter">"</span></span>)));</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">g.withStrategies(new SubgraphStrategy(vertexProperties=hasNot(<span class="string"><span class="delimiter">"</span><span class="content">endTime</span><span class="delimiter">"</span></span>)))</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"> This strategy is implemented such that the vertices attached to an <code>Edge</code> must both satisfy the vertex criterion (if present) in order for the <code>Edge</code> to be considered a part of the subgraph. </td> </tr> </table> </div> <div class="paragraph"> <p>The example below uses all three filters: vertex, edge, and vertex property. People vertices must have lived in more than three places, edges must be labeled "develops," and vertex properties must be the persons current location or a non-location property.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729796988-468" type="radio" name="radio-set-1729796988-468" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-468" class="tab-label-1">console (groovy)</label> <input id="tab-1729796988-469" type="radio" name="radio-set-1729796988-468" class="tab-selector-2" /> <label for="tab-1729796988-469" 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> graph = TinkerFactory.createTheCrew() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>] gremlin> g = traversal().withEmbedded(graph).withStrategies(SubgraphStrategy.build(). vertices(or(hasNot(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>),properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">3</span>)))). edges(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">develops</span><span class="delimiter">'</span></span>)). vertexProperties(or(hasLabel(neq(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>)),hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>))).create()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>], standard] gremlin> g.V().elementMap() ==>[<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">location</span>:santa fe] ==>[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:person,<span class="key">name</span>:matthias,<span class="key">location</span>:seattle] ==>[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:software,<span class="key">name</span>:gremlin] ==>[<span class="key">id</span>:<span class="integer">11</span>,<span class="key">label</span>:software,<span class="key">name</span>:tinkergraph] gremlin> g.E().elementMap() ==>[<span class="key">id</span>:<span class="integer">13</span>,<span class="key">label</span>:develops,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">10</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">since</span>:<span class="integer">2009</span>] ==>[<span class="key">id</span>:<span class="integer">14</span>,<span class="key">label</span>:develops,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">11</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">since</span>:<span class="integer">2010</span>] ==>[<span class="key">id</span>:<span class="integer">21</span>,<span class="key">label</span>:develops,<span class="key">IN</span>:[<span class="key">id</span>:<span class="integer">10</span>,<span class="key">label</span>:software],<span class="key">OUT</span>:[<span class="key">id</span>:<span class="integer">8</span>,<span class="key">label</span>:person],<span class="key">since</span>:<span class="integer">2012</span>] gremlin> g.V().outE().inV(). path(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[marko,e[<span class="integer">13</span>][<span class="integer">1</span>-develops-><span class="integer">10</span>],gremlin] ==>[marko,e[<span class="integer">14</span>][<span class="integer">1</span>-develops-><span class="integer">11</span>],tinkergraph] ==>[matthias,e[<span class="integer">21</span>][<span class="integer">8</span>-develops-><span class="integer">10</span>],gremlin]</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">graph = TinkerFactory.createTheCrew() g = traversal().withEmbedded(graph).withStrategies(SubgraphStrategy.build(). vertices(or(hasNot(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>),properties(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>).count().is(gt(<span class="integer">3</span>)))). edges(hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">develops</span><span class="delimiter">'</span></span>)). vertexProperties(or(hasLabel(neq(<span class="string"><span class="delimiter">'</span><span class="content">location</span><span class="delimiter">'</span></span>)),hasNot(<span class="string"><span class="delimiter">'</span><span class="content">endTime</span><span class="delimiter">'</span></span>))).create()) g.V().elementMap() g.E().elementMap() g.V().outE().inV(). path(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>). by(). 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> <div class="sect2"> <h3 id="_vertexprogramdenystrategy">VertexProgramDenyStrategy</h3> <div class="paragraph"> <p>Like the <code>ReadOnlyStrategy</code>, the <code>VertexProgramDenyStrategy</code> denies the execution of specific traversals. A <code>Traversal</code> that has the <code>VertexProgramDenyStrategy</code> applied will throw an <code>IllegalStateException</code> if it uses the <code>withComputer()</code> step. This <code>TraversalStrategy</code> can be useful for configuring <code>GraphTraversalSource</code> instances in Gremlin Server with the <code>ScriptFileGremlinPlugin</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> oltpOnly = g.withStrategies(VertexProgramDenyStrategy.instance()) ==>graphtraversalsource[tinkergraph[vertices:5 edges:7], standard] gremlin> oltpOnly.withComputer().V().elementMap() The TraversalSource does not allow the use of a GraphComputer Type ':help' or ':h' for help. Display stack trace? [yN]</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="dsl">Domain Specific Languages</h2> <div class="sectionbody"> <div class="paragraph"> <p>Gremlin is a <a href="http://en.wikipedia.org/wiki/Domain-specific_language">domain specific language</a> (DSL) for traversing graphs. It operates in the language of vertices, edges and properties. Typically, applications built with Gremlin are not of the graph domain, but instead model their domain within a graph. For example, the <a href="https://tinkerpop.apache.org/docs/3.7.3/images/tinkerpop-modern.png">"modern" toy graph</a> models software and person domain objects with the relationships between them (i.e. a person "knows" another person and a person "created" software).</p> </div> <div class="paragraph"> <p>An analyst who wanted to find out if "marko" knows "josh" could write the following Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">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>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).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">josh</span><span class="delimiter">'</span></span>).hasNext()</code></pre> </div> </div> <div class="paragraph"> <p>While this method achieves the desired answer, it requires the analyst to traverse the graph in the domain language of the graph rather than the domain language of the social network. A more natural way for the analyst to write this traversal might be:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.persons(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).knows(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>).hasNext()</code></pre> </div> </div> <div class="paragraph"> <p>In the statement above, the traversal is written in the language of the domain, abstracting away the underlying graph structure from the query. The two traversal results are equivalent and, indeed, the "Social DSL" produces the same set of traversal steps as the "Graph DSL" thus producing equivalent strategy application and performance runtimes.</p> </div> <div class="paragraph"> <p>To further the example of the Social DSL consider the following:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// Graph DSL - find the number of persons who created at least 2 projects</span> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). where(outE(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).count().is(P.gte(<span class="integer">2</span>))).count() <span class="comment">// Social DSL - find the number of persons who created at least 2 projects</span> social.persons().where(createdAtLeast(<span class="integer">2</span>)).count() <span class="comment">// Graph DSL - determine the age of the youngest friend "marko" has</span> 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>). out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).min() <span class="comment">// Social DSL - determine the age of the youngest friend "marko" has</span> social.persons(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>).youngestFriendsAge()</code></pre> </div> </div> <div class="paragraph"> <p>Learn more about how to implement these DSLs in the <a href="#gremlin-drivers-variants">Gremlin Language Variants</a> section specific to the programming language of interest.</p> </div> </div> </div> <div class="sect1"> <h2 id="translators">Translators</h2> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-translator.png" alt="gremlin translator" width="1024"> </div> </div> <div class="paragraph"> <p>There are times when is helpful to translate Gremlin from one programming language to another. Perhaps a large Gremlin example is found on StackOverflow written in Java, but the programming language the developer has chosen is Python. Fortunately, TinkerPop has developed <code>Translator</code> infrastructure that will convert Gremlin from one programming language syntax to another.</p> </div> <div class="paragraph"> <p>The functionality relevant to most users is actually a sub-function of <code>Translator</code> infrastructure and is more specifically a <code>ScriptTranslator</code> which takes Gremlin <code>Bytecode</code> of a traversal and generates a <code>String</code> representation of that <code>Bytecode</code> in the programming language syntax that the <code>ScriptTranslator</code> instance supports. The translation therefore allows Gremlin to be converted from the host programming language of the <code>Translator</code> to another.</p> </div> <div class="paragraph"> <p>The following translators are available, where the first column identifies the host programming language and the columns represent the language that Gremlin can be generated in:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 14.2857%;"> <col style="width: 14.2857%;"> <col style="width: 14.2857%;"> <col style="width: 14.2857%;"> <col style="width: 14.2857%;"> <col style="width: 14.2857%;"> <col style="width: 14.2858%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top"></th> <th class="tableblock halign-center valign-top">Java</th> <th class="tableblock halign-center valign-top">Groovy</th> <th class="tableblock halign-center valign-top">Javascript</th> <th class="tableblock halign-center valign-top">.NET</th> <th class="tableblock halign-center valign-top">Python</th> <th class="tableblock halign-center valign-top">Go</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Java</strong></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Groovy</strong></p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Javascript</strong></p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>.NET</strong></p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Python</strong></p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td> <td class="tableblock halign-center valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Go</strong></p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"></td> <td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Each programming language has its own API for translation, but the pattern is quite similar from one to the next:</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> While <code>Translator</code> implementations have been around for some time, they are still in their early stages from an interface perspective. API changes may occur in the near future. </td> </tr> </table> </div> <section class="tabs tabs-5"> <input id="tab-1729796988-470" type="radio" name="radio-set-1729796988-470" class="tab-selector-1" checked="checked" /> <label for="tab-1729796988-470" class="tab-label-1">java</label> <input id="tab-1729796988-471" type="radio" name="radio-set-1729796988-470" class="tab-selector-2" /> <label for="tab-1729796988-471" class="tab-label-2">javascript</label> <input id="tab-1729796988-472" type="radio" name="radio-set-1729796988-470" class="tab-selector-3" /> <label for="tab-1729796988-472" class="tab-label-3">python</label> <input id="tab-1729796988-473" type="radio" name="radio-set-1729796988-470" class="tab-selector-4" /> <label for="tab-1729796988-473" class="tab-label-4">csharp</label> <input id="tab-1729796988-474" type="radio" name="radio-set-1729796988-470" class="tab-selector-5" /> <label for="tab-1729796988-474" class="tab-label-5">go</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// gremlin-core module</span> <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.translator</span>.*; GraphTraversalSource g = ...; Traversal<Vertex,<span class="predefined-type">Integer</span>> t = 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>). where(in(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>)). values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>). map(Lambda.function(<span class="string"><span class="delimiter">"</span><span class="content">it.get() + 1</span><span class="delimiter">"</span></span>)); Translator.ScriptTranslator groovyTranslator = GroovyTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">System</span>.out.println(groovyTranslator.translate(t).getScript()); <span class="comment">// OUTPUT: g.V().has("person","name","marko").where(__.in("knows")).values("age").map({it.get() + 1})</span> Translator.ScriptTranslator dotnetTranslator = DotNetTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">System</span>.out.println(dotnetTranslator.translate(t).getScript()); <span class="comment">// OUTPUT: g.V().Has("person","name","marko").Where(__.In("knows")).Values<object>("age").Map<object>(Lambda.Groovy("it.get() + 1"))</span> Translator.ScriptTranslator pythonTranslator = PythonTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">System</span>.out.println(pythonTranslator.translate(t).getScript()); <span class="comment">// OUTPUT: g.V().has('person','name','marko').where(__.in_('knows')).age.map(lambda: "it.get() + 1")</span> Translator.ScriptTranslator javascriptTranslator = JavascriptTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">System</span>.out.println(javascriptTranslator.translate(t).getScript()); <span class="comment">// OUTPUT: g.V().has("person","name","marko").where(__.in_("knows")).values("age").map(() => "it.get() + 1")</span> Translator.ScriptTranslator golangTranslator = GolangTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">System</span>.out.println(golangTranslator.translate(t).getScript()); <span class="comment">// OUTPUT: g.V().Has("person", "name", "marko").Where(gremlingo.T__.In("knows")).Values("age").Map(&gremlingo.Lambda{Script:"it.get() + 1", Language:""})</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="javascript">const g = ...; const t = 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>). where(in_(<span class="string"><span class="delimiter">"</span><span class="content">knows</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">// Groovy</span> const translator = <span class="keyword">new</span> gremlin.process.Translator(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>); console.log(translator.translate(t)); <span class="comment">// OUTPUT: g.V().has('person','name','marko').where(__.in('knows')).values('age')</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">from</span> <span class="include">gremlin_python.process.translator</span> <span class="keyword">import</span> <span class="include">*</span> g = ... t = (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>). where(__.in_(<span class="string"><span class="delimiter">"</span><span class="content">knows</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"># Groovy</span> translator = Translator().of(<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>); print(translator.translate(t.bytecode)); <span class="comment"># OUTPUT: g.V().has('person','name','marko').where(__.in('knows')).values('age')</span></code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var g = ...; var t = g.V().Has("person", "name", "marko").Where(In("knows")).Values<int>("age"); // Groovy var translator = GroovyTranslator.Of("g"); Console.WriteLine(translator.Translate(t)); // OUTPUT: g.V().has('person', 'name', 'marko').where(__.in('knows')).values('age')</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">g := ... t := 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>). Where(T__.In(<span class="string"><span class="delimiter">"</span><span class="content">knows</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">// Groovy</span> translator := NewTranslator(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>) <span class="predefined">print</span>(translator.Translate(t.Bytecode)) <span class="comment">// OUTPUT: g.V().has('person','name','marko').where(in('knows')).values('age')</span></code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The JVM-based translator has the added option of parameter extraction, where the translation process will attempt to identify opportunities to generate an output that would replace constant values with parameters. The parameters would then be extracted and returned as part of the <code>Script</code> object:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Traversal<Vertex,<span class="predefined-type">Integer</span>> t = 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>). where(__.in(<span class="string"><span class="delimiter">"</span><span class="content">knows</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">// specify true to attempt parameter extraction</span> Translator.ScriptTranslator translator = GroovyTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>, <span class="predefined-constant">true</span>); Script s = translator.translate(t); <span class="predefined-type">System</span>.out.println(s.getScript()); <span class="comment">// OUTPUT: g.V().has(_args_0,_args_1,_args_2).where(__.in(_args_3)).values(_args_4)</span> <span class="predefined-type">System</span>.out.println(s.parameters); <span class="comment">// OUTPUT: Optional[{_args_0=person, _args_2=marko, _args_1=name, _args_4=age, _args_3=knows}]</span></code></pre> </div> </div> <div class="paragraph"> <p>The <code>GroovyTranslator</code> can take a <code>TypeTranslator</code> argument which allows some customization of how types get converted to script form. The <code>DefaultTypeTranslator</code> is used if a specific implementation is not specified. A built-in alternative to this implementation is the <code>LanguageTypeTranslator</code> which will prefer use of the Gremlin language <code>datetime()</code> function rather than the JVM specific <code>Date</code> and <code>Timestamp</code> conversions. This translator can be helpful when generating scripts that will be sent to Gremlin Server or Remote Graph Providers supporting the <code>datetime()</code> form.</p> </div> <div class="paragraph"> <p>The <code>PythonTranslator</code> can take a <code>TypeTranslator</code> argument to disable the syntactic sugar which the default translator applies to converted queries. The <code>DefaultTypeTranslator</code> is used if a specific implementation is not specified.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Traversal<Vertex,<span class="predefined-type">String</span>> t = g.V().range(<span class="integer">0</span>, <span class="integer">10</span>).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>). limit(<span class="integer">2</span>). values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>); <span class="comment">// default translator</span> Translator.ScriptTranslator translator = PythonTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>); <span class="predefined-type">String</span> defaultQueryTranslation = translator.translate(t) <span class="predefined-type">System</span>.out.println(defaultQueryTranslation); <span class="comment">// OUTPUT: g.V()[0:10].has('person','name','marko')[0:2].name</span> <span class="comment">// no synantic sugar translator</span> Translator.ScriptTranslator noSugarTranslator = PythonTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> PythonTranslator.NoSugarTranslator(<span class="predefined-constant">false</span>)); <span class="predefined-type">String</span> noSugarTranslation = noSugarTranslator.translate(t) <span class="predefined-type">System</span>.out.println(noSugarTranslation); <span class="comment">// OUTPUT: g.V().range_(0,10).has('person','name','marko').limit(2).values('name')</span> <span class="comment">// With parameter extraction</span> Translator.ScriptTranslator noSugarTranslatorWithParameters = PythonTranslator.of(<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> PythonTranslator.NoSugarTranslator(<span class="predefined-constant">true</span>)); <span class="predefined-type">String</span> noSugarTranslationWithParameters = noSugarTranslatorWithParameters.translate(t) <span class="predefined-type">System</span>.out.println(noSugarTranslationWithParameters); <span class="comment">// OUTPUT: g.V().range_(0,10).has(_args_0,_args_1,_args_2).limit(2).values(_args_1)</span></code></pre> </div> </div> </div> </div> <h1 id="graphcomputer" class="sect0">The GraphComputer</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p><span class="image right"><img src="../images/graphcomputer-puffers.png" alt="graphcomputer puffers" width="350"></span> TinkerPop provides two primary means of interacting with a graph: <a href="http://en.wikipedia.org/wiki/Online_transaction_processing">online transaction processing</a> (OLTP) and <a href="http://en.wikipedia.org/wiki/Online_analytical_processing">online analytical processing</a> (OLAP). OLTP-based graph systems allow the user to query the graph in real-time. However, typically, real-time performance is only possible when a local traversal is enacted. A local traversal is one that starts at a particular vertex (or small set of vertices) and touches a small set of connected vertices (by any arbitrary path of arbitrary length). In short, OLTP queries interact with a limited set of data and respond on the order of milliseconds or seconds. On the other hand, with OLAP graph processing, the entire graph is processed and thus, every vertex and edge is analyzed (some times more than once for iterative, recursive algorithms). Due to the amount of data being processed, the results are typically not returned in real-time and for massive graphs (i.e. graphs represented across a cluster of machines), results can take on the order of minutes or hours.</p> </div> <div class="ulist"> <ul> <li> <p><strong>OLTP</strong>: real-time, limited data accessed, random data access, sequential processing, querying</p> </li> <li> <p><strong>OLAP</strong>: long running, entire data set accessed, sequential data access, parallel processing, batch processing</p> </li> </ul> </div> <div class="imageblock"> <div class="content"> <img src="../images/oltp-vs-olap.png" alt="oltp vs olap" width="600"> </div> </div> <div class="paragraph"> <p>The image above demonstrates the difference between Gremlin OLTP and Gremlin OLAP. With Gremlin OLTP, the graph is walked by moving from vertex-to-vertex via incident edges. With Gremlin OLAP, all vertices are provided a <code>VertexProgram</code>. The programs send messages to one another with the topological structure of the graph acting as the communication network (though random message passing possible). In many respects, the messages passed are like the OLTP traversers moving from vertex-to-vertex. However, all messages are moving independent of one another, in parallel. Once a vertex program is finished computing, TinkerPop’s OLAP engine supports any number <a href="http://en.wikipedia.org/wiki/MapReduce"><code>MapReduce</code></a> jobs over the resultant graph.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> <code>GraphComputer</code> was designed from the start to be used within a multi-JVM, distributed environment — in other words, a multi-machine compute cluster. As such, all the computing objects must be able to be migrated between JVMs. The pattern promoted is to store state information in a <code>Configuration</code> object to later be regenerated by a loading process. It is important to realize that <code>VertexProgram</code>, <code>MapReduce</code>, and numerous particular instances rely heavily on the state of the computing classes (not the structure, but the processes) to be stored in a <code>Configuration</code>. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="vertexprogram">VertexProgram</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/bsp-diagram.png" alt="bsp diagram" width="400"></span> GraphComputer takes a <code>VertexProgram</code>. A VertexProgram can be thought of as a piece of code that is executed at each vertex in logically parallel manner until some termination condition is met (e.g. a number of iterations have occurred, no more data is changing in the graph, etc.). A submitted <code>VertexProgram</code> is copied to all the workers in the graph. A worker is not an explicit concept in the API, but is assumed of all <code>GraphComputer</code> implementations. At minimum each vertex is a worker (though this would be inefficient due to the fact that each vertex would maintain a VertexProgram). In practice, the workers partition the vertex set and are responsible for the execution of the VertexProgram over all the vertices within their sphere of influence. The workers orchestrate the execution of the <code>VertexProgram.execute()</code> method on all their vertices in an <a href="http://en.wikipedia.org/wiki/Bulk_synchronous_parallel">bulk synchronous parallel</a> (BSP) fashion. The vertices are able to communicate with one another via messages. There are two kinds of messages in Gremlin OLAP: <code>MessageScope.Local</code> and <code>MessageScope.Global</code>. A local message is a message to an adjacent vertex. A global message is a message to any arbitrary vertex in the graph. Once the VertexProgram has completed its execution, any number of <code>MapReduce</code> jobs are evaluated. MapReduce jobs are provided by the user via <code>GraphComputer.mapReduce()</code> or by the <code>VertexProgram</code> via <code>VertexProgram.getMapReducers()</code>.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/graphcomputer.png" alt="graphcomputer" width="500"> </div> </div> <div class="paragraph"> <p>The example below demonstrates how to submit a VertexProgram to a graph’s GraphComputer. <code>GraphComputer.submit()</code> yields a <code>Future<ComputerResult></code>. The <code>ComputerResult</code> has the resultant computed graph which can be a full copy of the original graph (see <a href="#hadoop-gremlin">Hadoop-Gremlin</a>) or a view over the original graph (see <a href="#tinkergraph-gremlin">TinkerGraph</a>). The ComputerResult also provides access to computational side-effects called <code>Memory</code> (which includes, for example, runtime, number of iterations, results of MapReduce jobs, and VertexProgram-specific memory manipulations).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-1" type="radio" name="radio-set-1729797114-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-2" type="radio" name="radio-set-1729797114-1" class="tab-selector-2" /> <label for="tab-1729797114-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> result = graph.compute().program(PageRankVertexProgram.build().create()).submit().get() ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>],memory[<span class="key">size</span>:<span class="integer">0</span>]] gremlin> result.memory().runtime ==><span class="integer">19</span> gremlin> g = traversal().withEmbedded(result.graph()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.V().elementMap() ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865538</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719103</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.30472009079122486</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719103</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.1757988989970823</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865538</span>,<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">result = graph.compute().program(PageRankVertexProgram.build().create()).submit().get() result.memory().runtime g = traversal().withEmbedded(result.graph()) g.V().elementMap()</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"> This model of "vertex-centric graph computing" was made popular by Google’s <a href="http://googleresearch.blogspot.com/2009/06/large-scale-graph-computing-at-google.html">Pregel</a> graph engine. In the open source world, this model is found in OLAP graph computing systems such as <a href="https://giraph.apache.org/">Giraph</a>, <a href="https://hama.apache.org/">Hama</a>. TinkerPop extends the popularized model with integrated post-processing <a href="#mapreduce">MapReduce</a> jobs over the vertex set. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="mapreduce">MapReduce</h2> <div class="sectionbody"> <div class="paragraph"> <p>The BSP model proposed by Pregel stores the results of the computation in a distributed manner as properties on the elements in the graph. In many situations, it is necessary to aggregate those resultant properties into a single result set (i.e. a statistic). For instance, assume a VertexProgram that computes a nominal cluster for each vertex (i.e. <a href="http://en.wikipedia.org/wiki/Community_structure">a graph clustering algorithm</a>). At the end of the computation, each vertex will have a property denoting the cluster it was assigned to. TinkerPop provides the ability to answer global questions about the clusters. For instance, in order to answer the following questions, <code>MapReduce</code> jobs are required:</p> </div> <div class="ulist"> <ul> <li> <p>How many vertices are in each cluster? (<strong>presented below</strong>)</p> </li> <li> <p>How many unique clusters are there? (<strong>presented below</strong>)</p> </li> <li> <p>What is the average age of each vertex in each cluster?</p> </li> <li> <p>What is the degree distribution of the vertices in each cluster?</p> </li> </ul> </div> <div class="paragraph"> <p>A compressed representation of the <code>MapReduce</code> API in TinkerPop is provided below. The key idea is that the <code>map</code>-stage processes all vertices to emit key/value pairs. Those values are aggregated on their respective key for the <code>reduce</code>-stage to do its processing to ultimately yield more key/value pairs.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">interface</span> <span class="class">MapReduce</span><MK, MV, RK, RV, R> { <span class="directive">public</span> <span class="type">void</span> map(<span class="directive">final</span> Vertex vertex, <span class="directive">final</span> MapEmitter<MK, MV> emitter); <span class="directive">public</span> <span class="type">void</span> reduce(<span class="directive">final</span> MK key, <span class="directive">final</span> <span class="predefined-type">Iterator</span><MV> values, <span class="directive">final</span> ReduceEmitter<RK, RV> emitter); <span class="comment">// there are more methods</span> }</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The vertex that is passed into the <code>MapReduce.map()</code> method does not contain edges. The vertex only contains original and computed vertex properties. This reduces the amount of data required to be loaded and ensures that MapReduce is used for post-processing computed results. All edge-based computing should be accomplished in the <code>VertexProgram</code>. </td> </tr> </table> </div> <div class="paragraph"> <p><span class="image"><img src="../images/mapreduce.png" alt="mapreduce" width="650"></span></p> </div> <div class="paragraph"> <p>The <code>MapReduce</code> extension to GraphComputer is made explicit when examining the <a href="#peerpressurevertexprogram"><code>PeerPressureVertexProgram</code></a> and corresponding <code>ClusterPopulationMapReduce</code>. In the code below, the GraphComputer result returns the computed on <code>Graph</code> as well as the <code>Memory</code> of the computation (<code>ComputerResult</code>). The memory maintain the results of any MapReduce jobs. The cluster population MapReduce result states that there are 5 vertices in cluster 1 and 1 vertex in cluster 6. This can be verified (in a serial manner) by looking at the <code>PeerPressureVertexProgram.CLUSTER</code> property of the resultant graph. Notice that the property is "hidden" unless it is directly accessed via name.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-3" type="radio" name="radio-set-1729797114-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-4" type="radio" name="radio-set-1729797114-3" class="tab-selector-2" /> <label for="tab-1729797114-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> graph = TinkerFactory.createModern() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> result = graph.compute().program(PeerPressureVertexProgram.build().create()).mapReduce(ClusterPopulationMapReduce.build().create()).submit().get() ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>],memory[<span class="key">size</span>:<span class="integer">1</span>]] gremlin> result.memory().get(<span class="string"><span class="delimiter">'</span><span class="content">clusterPopulation</span><span class="delimiter">'</span></span>) ==><span class="integer">1</span>=<span class="integer">5</span> ==><span class="integer">6</span>=<span class="integer">1</span> gremlin> g = traversal().withEmbedded(result.graph()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.V().values(PeerPressureVertexProgram.CLUSTER).groupCount().next() ==><span class="integer">1</span>=<span class="integer">5</span> ==><span class="integer">6</span>=<span class="integer">1</span> gremlin> g.V().elementMap() ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,gremlin.peerPressureVertexProgram.cluster:<span class="integer">1</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,gremlin.peerPressureVertexProgram.cluster:<span class="integer">1</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,gremlin.peerPressureVertexProgram.cluster:<span class="integer">1</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,gremlin.peerPressureVertexProgram.cluster:<span class="integer">1</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,gremlin.peerPressureVertexProgram.cluster:<span class="integer">1</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,gremlin.peerPressureVertexProgram.cluster:<span class="integer">6</span>,<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">graph = TinkerFactory.createModern() result = graph.compute().program(PeerPressureVertexProgram.build().create()).mapReduce(ClusterPopulationMapReduce.build().create()).submit().get() result.memory().get(<span class="string"><span class="delimiter">'</span><span class="content">clusterPopulation</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(result.graph()) g.V().values(PeerPressureVertexProgram.CLUSTER).groupCount().next() g.V().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>If there are numerous statistics desired, then its possible to register as many MapReduce jobs as needed. For instance, the <code>ClusterCountMapReduce</code> determines how many unique clusters were created by the peer pressure algorithm. Below both <code>ClusterCountMapReduce</code> and <code>ClusterPopulationMapReduce</code> are computed over the resultant graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-5" type="radio" name="radio-set-1729797114-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-6" type="radio" name="radio-set-1729797114-5" class="tab-selector-2" /> <label for="tab-1729797114-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> result = graph.compute().program(PeerPressureVertexProgram.build().create()). mapReduce(ClusterPopulationMapReduce.build().create()). mapReduce(ClusterCountMapReduce.build().create()).submit().get() ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>],memory[<span class="key">size</span>:<span class="integer">2</span>]] gremlin> result.memory().clusterPopulation ==><span class="integer">1</span>=<span class="integer">5</span> ==><span class="integer">6</span>=<span class="integer">1</span> gremlin> result.memory().clusterCount ==><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">result = graph.compute().program(PeerPressureVertexProgram.build().create()). mapReduce(ClusterPopulationMapReduce.build().create()). mapReduce(ClusterCountMapReduce.build().create()).submit().get() result.memory().clusterPopulation result.memory().clusterCount</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 MapReduce model of TinkerPop does not support MapReduce chaining. Thus, the order in which the MapReduce jobs are executed is irrelevant. This is made apparent when realizing that the <code>map()</code>-stage takes a <code>Vertex</code> as its input and the <code>reduce()</code>-stage yields key/value pairs. Thus, the results of reduce can not fed back into a <code>map()</code>. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="_a_collection_of_vertexprograms">A Collection of VertexPrograms</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop provides a collection of VertexPrograms that implement common algorithms. This section discusses the various implementations.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The vertex programs presented are what are provided as of TinkerPop 3.7.3. Over time, with future releases, more algorithms will be added. </td> </tr> </table> </div> <div class="sect2"> <h3 id="pagerankvertexprogram">PageRankVertexProgram</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-pagerank.png" alt="gremlin pagerank" width="400"></span> <a href="http://en.wikipedia.org/wiki/PageRank">PageRank</a> is perhaps the most popular OLAP-oriented graph algorithm. This <a href="http://en.wikipedia.org/wiki/Centrality">eigenvector centrality</a> variant was developed by Brin and Page of Google. PageRank defines a centrality value for all vertices in the graph, where centrality is defined recursively where a vertex is central if it is connected to central vertices. PageRank is an iterative algorithm that converges to a <a href="http://en.wikipedia.org/wiki/Ergodicity">steady state distribution</a>. If the pageRank values are normalized to 1.0, then the pageRank value of a vertex is the probability that a random walker will be seen that that vertex in the graph at any arbitrary moment in time. In order to help developers understand the methods of a <code>VertexProgram</code>, the PageRankVertexProgram code is analyzed below.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">PageRankVertexProgram</span> <span class="directive">implements</span> VertexProgram<<span class="predefined-type">Double</span>> { <span class="invisible">//</span><b class="conum">1</b> <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> PAGE_RANK = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.pageRank</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> EDGE_COUNT = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.edgeCount</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> PROPERTY = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.property</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> VERTEX_COUNT = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.vertexCount</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> ALPHA = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.alpha</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> EPSILON = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.epsilon</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> MAX_ITERATIONS = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.maxIterations</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> EDGE_TRAVERSAL = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.edgeTraversal</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> INITIAL_RANK_TRAVERSAL = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.initialRankTraversal</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> TELEPORTATION_ENERGY = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.teleportationEnergy</span><span class="delimiter">"</span></span>; <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> CONVERGENCE_ERROR = <span class="string"><span class="delimiter">"</span><span class="content">gremlin.pageRankVertexProgram.convergenceError</span><span class="delimiter">"</span></span>; <span class="directive">private</span> MessageScope.Local<<span class="predefined-type">Double</span>> incidentMessageScope = MessageScope.Local.of(__::outE); <span class="invisible">//</span><b class="conum">2</b> <span class="directive">private</span> MessageScope.Local<<span class="predefined-type">Double</span>> countMessageScope = MessageScope.Local.of(<span class="keyword">new</span> MessageScope.Local.ReverseTraversalSupplier(<span class="local-variable">this</span>.incidentMessageScope)); <span class="directive">private</span> PureTraversal<Vertex, Edge> edgeTraversal = <span class="predefined-constant">null</span>; <span class="directive">private</span> PureTraversal<Vertex, ? <span class="directive">extends</span> <span class="predefined-type">Number</span>> initialRankTraversal = <span class="predefined-constant">null</span>; <span class="directive">private</span> <span class="type">double</span> alpha = <span class="float">0.85d</span>; <span class="directive">private</span> <span class="type">double</span> epsilon = <span class="float">0.00001d</span>; <span class="directive">private</span> <span class="type">int</span> maxIterations = <span class="integer">20</span>; <span class="directive">private</span> <span class="predefined-type">String</span> property = PAGE_RANK; <span class="invisible">//</span><b class="conum">3</b> <span class="directive">private</span> <span class="predefined-type">Set</span><VertexComputeKey> vertexComputeKeys; <span class="directive">private</span> <span class="predefined-type">Set</span><MemoryComputeKey> memoryComputeKeys; <span class="directive">private</span> PageRankVertexProgram() { } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> loadState(<span class="directive">final</span> Graph graph, <span class="directive">final</span> <span class="predefined-type">Configuration</span> configuration) { <span class="invisible">//</span><b class="conum">4</b> <span class="keyword">if</span> (configuration.containsKey(INITIAL_RANK_TRAVERSAL)) <span class="local-variable">this</span>.initialRankTraversal = PureTraversal.loadState(configuration, INITIAL_RANK_TRAVERSAL, graph); <span class="keyword">if</span> (configuration.containsKey(EDGE_TRAVERSAL)) { <span class="local-variable">this</span>.edgeTraversal = PureTraversal.loadState(configuration, EDGE_TRAVERSAL, graph); <span class="local-variable">this</span>.incidentMessageScope = MessageScope.Local.of(() -> <span class="local-variable">this</span>.edgeTraversal.get().clone()); <span class="local-variable">this</span>.countMessageScope = MessageScope.Local.of(<span class="keyword">new</span> MessageScope.Local.ReverseTraversalSupplier(<span class="local-variable">this</span>.incidentMessageScope)); } <span class="local-variable">this</span>.alpha = configuration.getDouble(ALPHA, <span class="local-variable">this</span>.alpha); <span class="local-variable">this</span>.epsilon = configuration.getDouble(EPSILON, <span class="local-variable">this</span>.epsilon); <span class="local-variable">this</span>.maxIterations = configuration.getInt(MAX_ITERATIONS, <span class="integer">20</span>); <span class="local-variable">this</span>.property = configuration.getString(PROPERTY, PAGE_RANK); <span class="local-variable">this</span>.vertexComputeKeys = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><>(<span class="predefined-type">Arrays</span>.asList( VertexComputeKey.of(<span class="local-variable">this</span>.property, <span class="predefined-constant">false</span>), VertexComputeKey.of(EDGE_COUNT, <span class="predefined-constant">true</span>))); <span class="invisible">//</span><b class="conum">5</b> <span class="local-variable">this</span>.memoryComputeKeys = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><>(<span class="predefined-type">Arrays</span>.asList( MemoryComputeKey.of(TELEPORTATION_ENERGY, Operator.sum, <span class="predefined-constant">true</span>, <span class="predefined-constant">true</span>), MemoryComputeKey.of(VERTEX_COUNT, Operator.sum, <span class="predefined-constant">true</span>, <span class="predefined-constant">true</span>), MemoryComputeKey.of(CONVERGENCE_ERROR, Operator.sum, <span class="predefined-constant">false</span>, <span class="predefined-constant">true</span>))); } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> storeState(<span class="directive">final</span> <span class="predefined-type">Configuration</span> configuration) { VertexProgram.super.storeState(configuration); configuration.setProperty(ALPHA, <span class="local-variable">this</span>.alpha); configuration.setProperty(EPSILON, <span class="local-variable">this</span>.epsilon); configuration.setProperty(PROPERTY, <span class="local-variable">this</span>.property); configuration.setProperty(MAX_ITERATIONS, <span class="local-variable">this</span>.maxIterations); <span class="keyword">if</span> (<span class="predefined-constant">null</span> != <span class="local-variable">this</span>.edgeTraversal) <span class="local-variable">this</span>.edgeTraversal.storeState(configuration, EDGE_TRAVERSAL); <span class="keyword">if</span> (<span class="predefined-constant">null</span> != <span class="local-variable">this</span>.initialRankTraversal) <span class="local-variable">this</span>.initialRankTraversal.storeState(configuration, INITIAL_RANK_TRAVERSAL); } <span class="annotation">@Override</span> <span class="directive">public</span> GraphComputer.ResultGraph getPreferredResultGraph() { <span class="keyword">return</span> GraphComputer.ResultGraph.NEW; } <span class="annotation">@Override</span> <span class="directive">public</span> GraphComputer.Persist getPreferredPersist() { <span class="keyword">return</span> GraphComputer.Persist.VERTEX_PROPERTIES; } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="predefined-type">Set</span><VertexComputeKey> getVertexComputeKeys() { <span class="invisible">//</span><b class="conum">6</b> <span class="keyword">return</span> <span class="local-variable">this</span>.vertexComputeKeys; } <span class="annotation">@Override</span> <span class="directive">public</span> Optional<MessageCombiner<<span class="predefined-type">Double</span>>> getMessageCombiner() { <span class="keyword">return</span> (Optional) PageRankMessageCombiner.instance(); } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="predefined-type">Set</span><MemoryComputeKey> getMemoryComputeKeys() { <span class="keyword">return</span> <span class="local-variable">this</span>.memoryComputeKeys; } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="predefined-type">Set</span><MessageScope> getMessageScopes(<span class="directive">final</span> Memory memory) { <span class="directive">final</span> <span class="predefined-type">Set</span><MessageScope> set = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><>(); set.add(memory.isInitialIteration() ? <span class="local-variable">this</span>.countMessageScope : <span class="local-variable">this</span>.incidentMessageScope); <span class="keyword">return</span> set; } <span class="annotation">@Override</span> <span class="directive">public</span> PageRankVertexProgram clone() { <span class="keyword">try</span> { <span class="directive">final</span> PageRankVertexProgram clone = (PageRankVertexProgram) <span class="local-variable">super</span>.clone(); <span class="keyword">if</span> (<span class="predefined-constant">null</span> != <span class="local-variable">this</span>.initialRankTraversal) clone.initialRankTraversal = <span class="local-variable">this</span>.initialRankTraversal.clone(); <span class="keyword">return</span> clone; } <span class="keyword">catch</span> (<span class="directive">final</span> <span class="exception">CloneNotSupportedException</span> e) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">IllegalStateException</span>(e.getMessage(), e); } } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> setup(<span class="directive">final</span> Memory memory) { memory.set(TELEPORTATION_ENERGY, <span class="predefined-constant">null</span> == <span class="local-variable">this</span>.initialRankTraversal ? <span class="float">1.0d</span> : <span class="float">0.0d</span>); memory.set(VERTEX_COUNT, <span class="float">0.0d</span>); memory.set(CONVERGENCE_ERROR, <span class="float">1.0d</span>); } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> execute(<span class="directive">final</span> Vertex vertex, Messenger<<span class="predefined-type">Double</span>> messenger, <span class="directive">final</span> Memory memory) { <span class="invisible">//</span><b class="conum">7</b> <span class="keyword">if</span> (memory.isInitialIteration()) { messenger.sendMessage(<span class="local-variable">this</span>.countMessageScope, <span class="float">1.0d</span>); <span class="invisible">//</span><b class="conum">8</b> memory.add(VERTEX_COUNT, <span class="float">1.0d</span>); } <span class="keyword">else</span> { <span class="directive">final</span> <span class="type">double</span> vertexCount = memory.<<span class="predefined-type">Double</span>>get(VERTEX_COUNT); <span class="directive">final</span> <span class="type">double</span> edgeCount; <span class="type">double</span> pageRank; <span class="keyword">if</span> (<span class="integer">1</span> == memory.getIteration()) { edgeCount = IteratorUtils.reduce(messenger.receiveMessages(), <span class="float">0.0d</span>, (a, b) -> a + b); vertex.property(VertexProperty.Cardinality.single, EDGE_COUNT, edgeCount); pageRank = <span class="predefined-constant">null</span> == <span class="local-variable">this</span>.initialRankTraversal ? <span class="float">0.0d</span> : TraversalUtil.apply(vertex, <span class="local-variable">this</span>.initialRankTraversal.get()).doubleValue(); <span class="invisible">//</span><b class="conum">9</b> } <span class="keyword">else</span> { edgeCount = vertex.value(EDGE_COUNT); pageRank = IteratorUtils.reduce(messenger.receiveMessages(), <span class="float">0.0d</span>, (a, b) -> a + b); <span class="invisible">//</span><b class="conum">10</b> } <span class="comment">//////////////////////////</span> <span class="directive">final</span> <span class="type">double</span> teleporationEnergy = memory.get(TELEPORTATION_ENERGY); <span class="keyword">if</span> (teleporationEnergy > <span class="float">0.0d</span>) { <span class="directive">final</span> <span class="type">double</span> localTerminalEnergy = teleporationEnergy / vertexCount; pageRank = pageRank + localTerminalEnergy; memory.add(TELEPORTATION_ENERGY, -localTerminalEnergy); } <span class="directive">final</span> <span class="type">double</span> previousPageRank = vertex.<<span class="predefined-type">Double</span>>property(<span class="local-variable">this</span>.property).orElse(<span class="float">0.0d</span>); memory.add(CONVERGENCE_ERROR, <span class="predefined-type">Math</span>.abs(pageRank - previousPageRank)); vertex.property(VertexProperty.Cardinality.single, <span class="local-variable">this</span>.property, pageRank); memory.add(TELEPORTATION_ENERGY, (<span class="float">1.0d</span> - <span class="local-variable">this</span>.alpha) * pageRank); pageRank = <span class="local-variable">this</span>.alpha * pageRank; <span class="keyword">if</span> (edgeCount > <span class="float">0.0d</span>) messenger.sendMessage(<span class="local-variable">this</span>.incidentMessageScope, pageRank / edgeCount); <span class="keyword">else</span> memory.add(TELEPORTATION_ENERGY, pageRank); } } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">boolean</span> terminate(<span class="directive">final</span> Memory memory) { <span class="invisible">//</span><b class="conum">11</b> <span class="type">boolean</span> terminate = memory.<<span class="predefined-type">Double</span>>get(CONVERGENCE_ERROR) < <span class="local-variable">this</span>.epsilon || memory.getIteration() >= <span class="local-variable">this</span>.maxIterations; memory.set(CONVERGENCE_ERROR, <span class="float">0.0d</span>); <span class="keyword">return</span> terminate; } <span class="annotation">@Override</span> <span class="directive">public</span> <span class="predefined-type">String</span> toString() { <span class="keyword">return</span> StringFactory.vertexProgramString(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">alpha=</span><span class="delimiter">"</span></span> + <span class="local-variable">this</span>.alpha + <span class="string"><span class="delimiter">"</span><span class="content">, epsilon=</span><span class="delimiter">"</span></span> + <span class="local-variable">this</span>.epsilon + <span class="string"><span class="delimiter">"</span><span class="content">, iterations=</span><span class="delimiter">"</span></span> + <span class="local-variable">this</span>.maxIterations); } }</code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p><code>PageRankVertexProgram</code> implements <code>VertexProgram<Double></code> because the messages it sends are Java doubles.</p> </li> <li> <p>The default path of energy propagation is via outgoing edges from the current vertex.</p> </li> <li> <p>The resulting PageRank values for the vertices are stored as a vertex property.</p> </li> <li> <p>A vertex program is constructed using an Apache <code>Configuration</code> to ensure easy dissemination across a cluster of JVMs.</p> </li> <li> <p><code>EDGE_COUNT</code> is a transient "scratch data" compute key while <code>PAGE_RANK</code> is not.</p> </li> <li> <p>A vertex program must define the "compute keys" that are the properties being operated on during the computation.</p> </li> <li> <p>The "while"-loop of the vertex program.</p> </li> <li> <p>In order to determine how to distribute the energy to neighbors, a "1"-count is used to determine how many incident vertices exist for the <code>MessageScope</code>.</p> </li> <li> <p>Initially, each vertex is provided an equal amount of energy represented as a double.</p> </li> <li> <p>Energy is aggregated, computed on according to the PageRank algorithm, and then disseminated according to the defined <code>MessageScope.Local</code>.</p> </li> <li> <p>The computation is terminated after epsilon-convergence is met or a pre-defined number of iterations have taken place.</p> </li> </ol> </div> <div class="paragraph"> <p>The above <code>PageRankVertexProgram</code> is used as follows.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-7" type="radio" name="radio-set-1729797114-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-8" type="radio" name="radio-set-1729797114-7" class="tab-selector-2" /> <label for="tab-1729797114-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> result = graph.compute().program(PageRankVertexProgram.build().create()).submit().get() ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>],memory[<span class="key">size</span>:<span class="integer">0</span>]] gremlin> result.memory().runtime ==><span class="integer">6</span> gremlin> g = traversal().withEmbedded(result.graph()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.V().elementMap() ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865537</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719103</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.3047200907912249</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719103</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.1757988989970823</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865537</span>,<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">result = graph.compute().program(PageRankVertexProgram.build().create()).submit().get() result.memory().runtime g = traversal().withEmbedded(result.graph()) g.V().elementMap()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note that <code>GraphTraversal</code> provides a <a href="#pagerank-step"><code>pageRank()</code></a>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-9" type="radio" name="radio-set-1729797114-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-10" type="radio" name="radio-set-1729797114-9" class="tab-selector-2" /> <label for="tab-1729797114-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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().pageRank().elementMap() ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.17579889899708231</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865541</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.3047200907912249</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719106</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.14598540152719106</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,gremlin.pageRankVertexProgram.pageRank:<span class="float">0.11375510357865541</span>,<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>] gremlin> g.V().pageRank(). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>). with(PageRank.times, <span class="integer">5</span>). order(). by(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>). elementMap() ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">pageRank</span>:<span class="float">0.11362166126141333</span>,<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>] ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">pageRank</span>:<span class="float">0.11362166126141333</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">pageRank</span>:<span class="float">0.30511923758466225</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">pageRank</span>:<span class="float">0.14598422136890218</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">pageRank</span>:<span class="float">0.14598422136890218</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">pageRank</span>:<span class="float">0.1756689971547068</span>,<span class="key">name</span>:ripple,<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 = traversal().withEmbedded(graph).withComputer() g.V().pageRank().elementMap() g.V().pageRank(). with(PageRank.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>). with(PageRank.times, <span class="integer">5</span>). order(). by(<span class="string"><span class="delimiter">'</span><span class="content">pageRank</span><span class="delimiter">'</span></span>). elementMap()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="peerpressurevertexprogram">PeerPressureVertexProgram</h3> <div class="paragraph"> <p>The <code>PeerPressureVertexProgram</code> is a clustering algorithm that assigns a nominal value to each vertex in the graph. The nominal value represents the vertex’s cluster. If two vertices have the same nominal value, then they are in the same cluster. The algorithm proceeds in the following manner.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Every vertex assigns itself to a unique cluster ID (initially, its vertex ID).</p> </li> <li> <p>Every vertex determines its per neighbor vote strength as 1.0d / incident edges count.</p> </li> <li> <p>Every vertex sends its cluster ID and vote strength to its adjacent vertices as a <code>Pair<Serializable,Double></code></p> </li> <li> <p>Every vertex generates a vote energy distribution of received cluster IDs and changes its current cluster ID to the most frequent cluster ID.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>If there is a tie, then the cluster with the lowest <code>toString()</code> comparison is selected.</p> </li> </ol> </div> </li> <li> <p>Steps 3 and 4 repeat until either a max number of iterations has occurred or no vertex has adjusted its cluster anymore.</p> </li> </ol> </div> <div class="paragraph"> <p>Note that <code>GraphTraversal</code> provides a <a href="#peerpressure-step"><code>peerPressure()</code></a>-step.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-11" type="radio" name="radio-set-1729797114-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-12" type="radio" name="radio-set-1729797114-11" class="tab-selector-2" /> <label for="tab-1729797114-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> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().peerPressure().with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>).elementMap() ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">6</span>,<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] gremlin> g.V().peerPressure(). with(PeerPressure.edges,outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>). elementMap() ==>[<span class="key">id</span>:<span class="integer">1</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">cluster</span>:<span class="integer">5</span>,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software,<span class="key">cluster</span>:<span class="integer">3</span>,<span class="key">name</span>:lop,<span class="key">lang</span>:java] ==>[<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">1</span>,<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person,<span class="key">cluster</span>:<span class="integer">6</span>,<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 = traversal().withEmbedded(graph).withComputer() g.V().peerPressure().with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>).elementMap() g.V().peerPressure(). with(PeerPressure.edges,outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>)). with(PeerPressure.propertyName, <span class="string"><span class="delimiter">'</span><span class="content">cluster</span><span class="delimiter">'</span></span>). elementMap()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="connectedcomponentvertexprogram">ConnectedComponentVertexProgram</h3> <div class="paragraph"> <p>The <code>ConnectedComponentVertexProgram</code> identifies <a href="https://en.wikipedia.org/wiki/Connected_component_(graph_theory)">Connected Component</a> instances in a graph. See <a href="#connectedcomponent-step"><code>connectedComponent()</code></a>-step for more information.</p> </div> </div> <div class="sect2"> <h3 id="shortestpathvertexprogram">ShortestPathVertexProgram</h3> <div class="paragraph"> <p>The <code>ShortestPathVertexProram</code> provides an easy way to find shortest non-cyclic paths in the graph. It provides several options to configure the output format, the start- and end-vertices, the direction, a custom distance function, as well as a distance limitation. By default it just finds all undirected, shortest paths in the graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-13" type="radio" name="radio-set-1729797114-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-14" type="radio" name="radio-set-1729797114-13" class="tab-selector-2" /> <label for="tab-1729797114-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> spvp = ShortestPathVertexProgram.build().create() <span class="comment">//</span>// <b class="conum">(1)</b> ==>ShortestPathVertexProgram[includeEdges=<span class="predefined-constant">false</span>] gremlin> result = graph.compute().program(spvp).submit().get() <span class="comment">//</span>// <b class="conum">(2)</b> ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>],memory[<span class="key">size</span>:<span class="integer">1</span>]] gremlin> result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">2</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">3</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">4</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">1</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">6</span>],v[<span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">5</span>],v[<span class="integer">4</span>],v[<span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">6</span>]] ==>[v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">2</span>],v[<span class="integer">1</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">3</span>],v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">4</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">5</span>]] ==>[v[<span class="integer">6</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">spvp = ShortestPathVertexProgram.build().create() <span class="comment">//</span>// <b class="conum">(1)</b> result = graph.compute().program(spvp).submit().get() <span class="comment">//</span>// <b class="conum">(2)</b> result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create a <code>ShortestPathVertexProgram</code> with its default configuration.</p> </li> <li> <p>Execute the <code>ShortestPathVertexProgram</code>.</p> </li> <li> <p>Get all shortest paths from the results memory.</p> </li> </ol> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-15" type="radio" name="radio-set-1729797114-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-16" type="radio" name="radio-set-1729797114-15" class="tab-selector-2" /> <label for="tab-1729797114-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> spvp = ShortestPathVertexProgram.build().includeEdges(<span class="predefined-constant">true</span>).create() <span class="comment">//</span>// <b class="conum">(1)</b> ==>ShortestPathVertexProgram[includeEdges=<span class="predefined-constant">true</span>] gremlin> result = graph.compute().program(spvp).submit().get() <span class="comment">//</span>// <b class="conum">(2)</b> ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>],memory[<span class="key">size</span>:<span class="integer">1</span>]] gremlin> result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <span class="comment">//</span>// <b class="conum">(3)</b> ==>[v[<span class="integer">1</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">2</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">1</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">2</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">3</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">4</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">8</span>][<span class="integer">1</span>-knows-><span class="integer">4</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">5</span>]] ==>[v[<span class="integer">6</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">4</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">5</span>]] ==>[v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">2</span>],e[<span class="integer">7</span>][<span class="integer">1</span>-knows-><span class="integer">2</span>],v[<span class="integer">1</span>],e[<span class="integer">9</span>][<span class="integer">1</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</span>]] ==>[v[<span class="integer">5</span>],e[<span class="integer">10</span>][<span class="integer">4</span>-created-><span class="integer">5</span>],v[<span class="integer">4</span>],e[<span class="integer">11</span>][<span class="integer">4</span>-created-><span class="integer">3</span>],v[<span class="integer">3</span>],e[<span class="integer">12</span>][<span class="integer">6</span>-created-><span class="integer">3</span>],v[<span class="integer">6</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">spvp = ShortestPathVertexProgram.build().includeEdges(<span class="predefined-constant">true</span>).create() <span class="comment">//</span>// <b class="conum">(1)</b> result = graph.compute().program(spvp).submit().get() <span class="comment">//</span>// <b class="conum">(2)</b> result.memory().get(ShortestPathVertexProgram.SHORTEST_PATHS) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Create a <code>ShortestPathVertexProgram</code> as before, but configure it to include edges in the result.</p> </li> <li> <p>Execute the <code>ShortestPathVertexProgram</code>.</p> </li> <li> <p>Get all shortest paths from the results memory.</p> </li> </ol> </div> <div class="paragraph"> <p>The <code>ShortestPathVertexProgram.Builder</code> provides the following configuration methods:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 13.0434%;"> <col style="width: 65.2173%;"> <col style="width: 21.7393%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Method</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>source(Traversal)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets a filter traversal for the start vertices (e.g. <code>__.has('name','marko')</code>).</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">all vertices (<code>__.identity()</code>)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>target(Traversal)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets a filter traversal for the end vertices.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">all vertices</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>edgeDirection(Direction)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the direction to traverse during the shortest path discovery.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Direction.BOTH</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>edgeTraversal(Traversal)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets a traversal that emits the edges to traverse from the current vertex.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>__.bothE()</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>distanceProperty(String)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the edge property to use for the distance calculations.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">none</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>distanceTraversal(Traversal)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the traversal that calculates the distance for the current edge.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>__.constant(1)</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>maxDistance(Traversal)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Limits the shortest path distance.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">none</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>includeEdges(Boolean)</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Whether to include edges in shortest paths or not.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p></td> </tr> </tbody> </table> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> If a maximum distance is provided, the discovery process will only stop to follow a path at this distance if there was no custom distance property or traversal provided. Custom distances can be negative, hence exceeding the maximum distance doesn’t mean that there can’t be any more valid paths. However, paths will be filtered at the end, when no more non-cyclic paths can be found. The bottom line is that custom distance properties or traversals can lead to much longer runtimes and a much higher memory consumption. </td> </tr> </table> </div> <div class="paragraph"> <p>Note that <code>GraphTraversal</code> provides a <a href="#shortestpath-step"><code>shortestPath()</code></a>-step.</p> </div> </div> <div class="sect2"> <h3 id="clonevertexprogram">CloneVertexProgram</h3> <div class="paragraph"> <p>The <code>CloneVertexProgram</code> (known in versions prior to 3.2.10 as <code>BulkDumperVertexProgram</code>) copies a whole graph from any graph <code>InputFormat</code> to any graph <code>OutputFormat</code>. TinkerPop provides the following:</p> </div> <div class="ulist"> <ul> <li> <p><code>OutputFormat</code></p> <div class="ulist"> <ul> <li> <p><code>GraphSONOutputFormat</code></p> </li> <li> <p><code>GryoOutputFormat</code></p> </li> <li> <p><code>ScriptOutputFormat</code></p> </li> </ul> </div> </li> <li> <p><code>InputFormat</code></p> <div class="ulist"> <ul> <li> <p><code>GraphSONInputFormat</code></p> </li> <li> <p><code>GryoInputFormat</code></p> </li> <li> <p><code>ScriptInputFormat</code>).</p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>An <a href="#clonevertexprogramusingspark">example</a> is provided in the SparkGraphComputer section.</p> </div> <div class="paragraph"> <p>Graph Providers should consider writing their own <code>OutputFormat</code> and <code>InputFormat</code> which would allow bulk loading and export capabilities through this <code>VertexProgram</code>. This topic is discussed further in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#bulk-import-export">Provider Documentation</a>.</p> </div> </div> <div class="sect2"> <h3 id="traversalvertexprogram">TraversalVertexProgram</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/traversal-vertex-program.png" alt="traversal vertex program" width="250"></span> The <code>TraversalVertexProgram</code> is a "special" VertexProgram in that it can be executed via a <code>Traversal</code> and a <code>GraphComputer</code>. In Gremlin, it is possible to have the same traversal executed using either the standard OLTP-engine or the <code>GraphComputer</code> OLAP-engine. The difference being where the traversal is submitted.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> This model of graph traversal in a BSP system was first implemented by the <a href="https://github.com/thinkaurelius/faunus/wiki">Faunus</a> graph analytics engine and originally described in <a href="https://dzone.com/articles/local-and-distributed-graph">Local and Distributed Traversal Engines</a>. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-17" type="radio" name="radio-set-1729797114-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-18" type="radio" name="radio-set-1729797114-17" class="tab-selector-2" /> <label for="tab-1729797114-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> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount().next() <span class="comment">// OLTP</span> ==><span class="integer">32</span>=<span class="integer">3</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">3</span> gremlin> g = traversal().withEmbedded(graph).withComputer() ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], graphcomputer] gremlin> g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount().next() <span class="comment">// OLAP</span> ==><span class="integer">32</span>=<span class="integer">3</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">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 = traversal().withEmbedded(graph) g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount().next() <span class="comment">// OLTP</span> g = traversal().withEmbedded(graph).withComputer() g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount().next() <span class="comment">// OLAP</span></code></pre> </div> </div> </div> </div> </section> <div class="imageblock"> <div class="content"> <img src="../images/olap-traversal.png" alt="olap traversal" width="650"> </div> </div> <div class="paragraph"> <p>In the OLAP example above, a <code>TraversalVertexProgram</code> is (logically) sent to each vertex in the graph. Each instance evaluation requires (logically) 5 BSP iterations and each iteration is interpreted as such:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>g.V()</code>: Put a traverser on each vertex in the graph.</p> </li> <li> <p><code>both()</code>: Propagate each traverser to the vertices <code>both</code>-adjacent to its current vertex.</p> </li> <li> <p><code>hasLabel('person')</code>: If the vertex is not a person, kill the traversers at that vertex.</p> </li> <li> <p><code>values('age')</code>: Have all the traversers reference the integer age of their current vertex.</p> </li> <li> <p><code>groupCount()</code>: Count how many times a particular age has been seen.</p> </li> </ol> </div> <div class="paragraph"> <p>While 5 iterations were presented, in fact, <code>TraversalVertexProgram</code> will execute the traversal in only 2 iterations. The reason being is that <code>g.V().both()</code> and <code>hasLabel('person').values('age').groupCount()</code> can be executed in a single iteration as any message sent would simply be to the current executing vertex. Thus, a simple optimization exists in Gremlin OLAP called "reflexive message passing" which simulates non-message-passing BSP iterations within a single BSP iteration.</p> </div> <div class="paragraph"> <p>The same OLAP traversal can be executed using the standard <code>graph.compute()</code> model, though at the expense of verbosity. <code>TraversalVertexProgram</code> provides a fluent <code>Builder</code> for constructing a <code>TraversalVertexProgram</code>. The specified <code>traversal()</code> can be either a direct <code>Traversal</code> object or a <a href="http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform">JSR-223</a> script that will generate a <code>Traversal</code>. There is no benefit to using the model below. It is demonstrated to help elucidate how Gremlin OLAP traversals are ultimately compiled for execution on a <code>GraphComputer</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797114-19" type="radio" name="radio-set-1729797114-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729797114-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729797114-20" type="radio" name="radio-set-1729797114-19" class="tab-selector-2" /> <label for="tab-1729797114-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> result = graph.compute().program(TraversalVertexProgram.build().traversal(g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).create()).submit().get() ==>result[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>],memory[<span class="key">size</span>:<span class="integer">2</span>]] gremlin> result.memory().a ==><span class="integer">32</span>=<span class="integer">3</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">3</span> gremlin> result.memory().iteration ==><span class="integer">1</span> gremlin> result.memory().runtime ==><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">result = graph.compute().program(TraversalVertexProgram.build().traversal(g.V().both().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).groupCount(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>)).create()).submit().get() result.memory().a result.memory().iteration result.memory().runtime</code></pre> </div> </div> </div> </div> </section> <div class="sect3"> <h4 id="distributed-gremlin-gotchas">Distributed Gremlin Gotchas</h4> <div class="paragraph"> <p>Gremlin OLTP is not identical to Gremlin OLAP.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> There are two primary theoretical differences between Gremlin OLTP and Gremlin OLAP. First, Gremlin OLTP (via <code>Traversal</code>) leverages a <a href="http://en.wikipedia.org/wiki/Depth-first_search">depth-first</a> execution engine. Depth-first execution has a limited memory footprint due to <a href="http://en.wikipedia.org/wiki/Lazy_evaluation">lazy evaluation</a>. On the other hand, Gremlin OLAP (via <code>TraversalVertexProgram</code>) leverages a <a href="http://en.wikipedia.org/wiki/Breadth-first_search">breadth-first</a> execution engine which maintains a larger memory footprint, but a better time complexity due to vertex-local traversers being able to be "bulked." The second difference is that Gremlin OLTP is executed in a serial/streaming fashion, while Gremlin OLAP is executed in a parallel/step-wise fashion. These two fundamental differences lead to the behaviors enumerated below. </td> </tr> </table> </div> <div class="imageblock right"> <div class="content"> <img src="../images/gremlin-without-a-cause.png" alt="gremlin without a cause" width="200"> </div> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Traversal sideEffects are represented as a distributed data structure across <code>GraphComputer</code> workers. It is not possible to get a global view of a sideEffect until after an iteration has occurred and global sideEffects are re-broadcasted to the workers. In some situations, a "stale" local representation of the sideEffect is sufficient to ensure the intended semantics of the traversal are respected. However, this is not generally true so be wary of traversals that require global views of a sideEffect. To ensure a fresh global representation, use <code>barrier()</code> prior to accessing the global sideEffect. Note that this only comes into play with custom steps and <a href="#general-steps">lambda steps</a>. The standard Gremlin step library is respective of OLAP semantics.</p> </li> <li> <p>When evaluating traversals that rely on path information (i.e. the history of the traversal), practical computational limits can easily be reached due the <a href="http://en.wikipedia.org/wiki/Combinatorial_explosion">combinatoric explosion</a> of data. With path computing enabled, every traverser is unique and thus, must be enumerated as opposed to being counted/merged. The difference being a collection of paths vs. a single 64-bit long at a single vertex. In other words, bulking is very unlikely with traversers that maintain path information. For more information on this concept, please see <a href="https://thinkaurelius.wordpress.com/2012/11/11/faunus-provides-big-graph-data-analytics/">Faunus Provides Big Graph Data</a>.</p> </li> <li> <p>Steps that are concerned with the global ordering of traversers do not have a meaningful representation in OLAP. For example, what does <a href="#order-step"><code>order()</code></a>-step mean when all traversers are being processed in parallel? Even if the traversers were aggregated and ordered, then at the next step they would return to being executed in parallel and thus, in an unpredictable order. When <code>order()</code>-like steps are executed at the end of a traversal (i.e the final step), <code>TraversalVertexProgram</code> ensures a serial representation is ordered accordingly. Moreover, it is intelligent enough to maintain the ordering of <code>g.V().hasLabel("person").order().by("age").values("name")</code>. However, the OLAP traversal <code>g.V().hasLabel("person").order().by("age").out().values("name")</code> will lose the original ordering as the <code>out()</code>-step will rebroadcast traversers across the cluster.</p> </li> </ol> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="graph-filter">Graph Filter</h2> <div class="sectionbody"> <div class="paragraph"> <p>Most OLAP jobs do not require the entire source graph to faithfully execute their <code>VertexProgram</code>. For instance, if <code>PageRankVertexProgram</code> is only going to compute the centrality of people in the friendship-graph, then the following <code>GraphFilter</code> can be applied.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">graph.computer(). vertices(hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>)). vertexProperties(__.properties(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)). edges(bothE(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>)). program(PageRankVertexProgram...)</code></pre> </div> </div> <div class="paragraph"> <p>There are three methods for constructing a <code>GraphFilter</code>.</p> </div> <div class="ulist"> <ul> <li> <p><code>vertices(Traversal<Vertex,Vertex>)</code>: A traversal that will be used that can only analyze a vertex and its properties. If the traversal <code>hasNext()</code>, the input <code>Vertex</code> is passed to the <code>GraphComputer</code>.</p> </li> <li> <p><code>vertexProperties(Traversal<Vertex, ? extends Property<?>)</code>: A traversal that will either let the vertex property pass or not.</p> </li> <li> <p><code>edges(Traversal<Vertex,Edge>)</code>: A traversal that will iterate all legal edges for the source vertex.</p> </li> </ul> </div> <div class="paragraph"> <p><code>GraphFilter</code> is a "push-down predicate" that providers can reason on to determine the most efficient way to provide graph data to the <code>GraphComputer</code>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Apache TinkerPop provides <code>GraphFilterStrategy</code> <a href="#traversalstrategy">traversal strategy</a> which analyzes a submitted OLAP traversal and, if possible, creates an appropriate <code>GraphFilter</code> automatically. For instance, <code>g.V().count()</code> would yield a <code>GraphFilter.edges(limit(0))</code>. Thus, for traversal submissions, users typically do not need to be aware of creating graph filters explicitly. Users can use the <a href="#explain-step"><code>explain()</code></a>-step to see the <code>GraphFilter</code> generated by <code>GraphFilterStrategy</code>. </td> </tr> </table> </div> </div> </div> <h1 id="gremlin-applications" class="sect0">Gremlin Applications</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>Gremlin applications represent tools that are built on top of the core APIs to help expose common functionality to users when working with graphs. There are two key applications:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Gremlin Console - A <a href="http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a> environment for interactive development and analysis</p> </li> <li> <p>Gremlin Server - A server that hosts a Gremlin Traversal Machine thus enabling remote Gremlin execution</p> </li> </ol> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-lab-coat.png" alt="gremlin lab coat" width="310"></span> Gremlin is designed to be extensible, making it possible for users and graph system/language providers to customize it to their needs. Such extensibility is also found in the Gremlin Console and Server, where a universal plugin system makes it possible to extend their capabilities. One of the important aspects of the plugin system is the ability to help the user install the plugins through the command line thus automating the process of gathering dependencies and other error prone activities.</p> </div> <div class="paragraph"> <p>The process of plugin installation is handled by <a href="http://www.groovy-lang.org/Grape">Grape</a>, which helps resolve dependencies into the classpath. It is therefore important to ensure that Grape is properly configured in order to use the automated capabilities of plugin installation. Grape is configured by <code>~/.groovy/grapeConfig.xml</code> and generally speaking, if that file is not present, the default settings will suffice. However, they will not suffice if a required dependency is not in one of the default configured repositories. Please see the <a href="http://www.groovy-lang.org/Grape#Grape-CustomizeIvysettings">Customize Ivy settings</a> section of the Grape documentation for more details on the defaults. For current TinkerPop plugins and dependencies the following configuration which is also the default for Ivy should be acceptable:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><ivysettings></span> <span class="tag"><settings</span> <span class="attribute-name">defaultResolver</span>=<span class="string"><span class="delimiter">"</span><span class="content">downloadGrapes</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"><resolvers></span> <span class="tag"><chain</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">downloadGrapes</span><span class="delimiter">"</span></span> <span class="attribute-name">returnFirst</span>=<span class="string"><span class="delimiter">"</span><span class="content">true</span><span class="delimiter">"</span></span><span class="tag">></span> <span class="tag"><filesystem</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">cachedGrapes</span><span class="delimiter">"</span></span><span class="tag">></span> <span class="tag"><ivy</span> <span class="attribute-name">pattern</span>=<span class="string"><span class="delimiter">"</span><span class="content">${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"><artifact</span> <span class="attribute-name">pattern</span>=<span class="string"><span class="delimiter">"</span><span class="content">${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision](-[classifier]).[ext]</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"></filesystem></span> <span class="tag"><ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">localm2</span><span class="delimiter">"</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">"</span><span class="content">${user.home.url}/.m2/repository/</span><span class="delimiter">"</span></span> <span class="attribute-name">checkmodified</span>=<span class="string"><span class="delimiter">"</span><span class="content">true</span><span class="delimiter">"</span></span> <span class="attribute-name">changingPattern</span>=<span class="string"><span class="delimiter">"</span><span class="content">.*</span><span class="delimiter">"</span></span> <span class="attribute-name">changingMatcher</span>=<span class="string"><span class="delimiter">"</span><span class="content">regexp</span><span class="delimiter">"</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">"</span><span class="content">true</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"><ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">jcenter</span><span class="delimiter">"</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">"</span><span class="content">https://jcenter.bintray.com/</span><span class="delimiter">"</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">"</span><span class="content">true</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"><ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">"</span><span class="content">ibiblio</span><span class="delimiter">"</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">"</span><span class="content">true</span><span class="delimiter">"</span></span><span class="tag">/></span> <span class="tag"></chain></span> <span class="tag"></resolvers></span> <span class="tag"></ivysettings></span></code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Please see the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/developer/#groovy-environment">Developer Documentation</a> for additional configuration options when working with "snapshot" releases. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-console">Gremlin Console</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-console.png" alt="gremlin console" width="325"></span> The Gremlin Console is an interactive terminal or <a href="http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a> that can be used to traverse graphs and interact with the data that they contain. It represents the most common method for performing ad hoc graph analysis, small to medium sized data loading projects and other exploratory functions. The Gremlin Console is highly extensible, featuring a rich plugin system that allows new tools, commands, <a href="http://en.wikipedia.org/wiki/Domain-specific_language">DSLs</a>, etc. to be exposed to users.</p> </div> <div class="paragraph"> <p>To start the Gremlin Console, run <code>gremlin.sh</code> or <code>gremlin.bat</code> (<code>gremlin-java8.bat</code> for Java 8):</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin loaded: tinkerpop.server plugin loaded: tinkerpop.utilities plugin loaded: tinkerpop.tinkergraph gremlin></code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> If the above plugins are not loaded then they will need to be enabled or else certain examples will not work. If using the standard Gremlin Console distribution, then the plugins should be enabled by default. See below for more information on the <code>:plugin use</code> command to manually enable plugins. These plugins, with the exception of <code>tinkerpop.tinkergraph</code>, cannot be removed from the Console as they are a part of the <code>gremlin-console.jar</code> itself. These plugins can only be deactivated. </td> </tr> </table> </div> <div class="paragraph"> <p>The Gremlin Console is loaded and ready for commands. Recall that the console hosts the Gremlin-Groovy language. Please review <a href="http://www.groovy-lang.org/">Groovy</a> for help on Groovy-related constructs. In short, Groovy is a superset of Java. What works in Java, works in Groovy. However, Groovy provides many shorthands to make it easier to interact with the Java API. Moreover, Gremlin provides many neat shorthands to make it easier to express paths through a property graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-1" type="radio" name="radio-set-1729797129-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-2" type="radio" name="radio-set-1729797129-1" class="tab-selector-2" /> <label for="tab-1729797129-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> i = <span class="string"><span class="delimiter">'</span><span class="content">goodbye</span><span class="delimiter">'</span></span> ==>goodbye gremlin> j = <span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span> ==>self gremlin> i + <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="delimiter">"</span></span> + j ==>goodbye self gremlin> <span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>i<span class="inline-delimiter">}</span></span><span class="content"> </span><span class="inline"><span class="inline-delimiter">${</span>j<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span> ==>goodbye self</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">i = <span class="string"><span class="delimiter">'</span><span class="content">goodbye</span><span class="delimiter">'</span></span> j = <span class="string"><span class="delimiter">'</span><span class="content">self</span><span class="delimiter">'</span></span> i + <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="delimiter">"</span></span> + j <span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>i<span class="inline-delimiter">}</span></span><span class="content"> </span><span class="inline"><span class="inline-delimiter">${</span>j<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span></code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The "toy" graph provides a way to get started with Gremlin quickly.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-3" type="radio" name="radio-set-1729797129-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-4" type="radio" name="radio-set-1729797129-3" class="tab-selector-2" /> <label for="tab-1729797129-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> g = traversal().withEmbedded(TinkerFactory.createModern()) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> g.V() ==>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> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> 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>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>vadas ==>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 = traversal().withEmbedded(TinkerFactory.createModern()) g.V() g.V().values(<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">knows</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="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When using Gremlin-Groovy in a Groovy class file, add <code>static { GremlinLoader.load() }</code> to the head of the file. </td> </tr> </table> </div> <div class="sect2"> <h3 id="_console_commands">Console Commands</h3> <div class="paragraph"> <p>In addition to the standard commands of the <a href="http://groovy-lang.org/groovysh.html">Groovy Shell</a>, Gremlin adds some other useful operations. The following table outlines the most commonly used commands:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 13.3333%;"> <col style="width: 66.6667%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Command</th> <th class="tableblock halign-center valign-top">Alias</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:help</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:?</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Displays list of commands and descriptions. When followed by a command name, it will display more specific help on that particular item.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:exit</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:x</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Ends the Console session.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">import</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:i</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Import a class into the Console session.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:cls</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:C</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Clear the screen of the Console.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:clear</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:c</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sometimes the Console can get into a state where the command buffer no longer understands input (e.g. a misplaced <code>(</code> or <code>}</code>). Use this command to clear that buffer.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:load</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:l</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Load a file or URL into the command buffer for execution.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:install</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:+</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Imports a Maven library and its dependencies into the Console.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:uninstall</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:-</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Removes a Maven library and its dependencies. A restart of the console is required for removal to fully take effect.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:plugin</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:pin</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Plugin management functions to list, activate and deactivate available plugins.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:remote</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:rem</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Configures a "remote" context where Gremlin or results of Gremlin will be processed via usage of <code>:submit</code>.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:submit</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Submit Gremlin to the currently active context defined by <code>:remote</code>.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">:bytecode</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">:bc</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Provides options for translating and evaluating <code>Bytecode</code> for debugging purposes.</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Many of the above commands are described elsewhere or are generally self-explanatory, but the <code>:bytecode</code> command could use some additional explanation. The following code shows example usage:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> :bytecode from g.V().out('knows') <span class="invisible">//</span><b class="conum">1</b> ==>{"@type":"g:Bytecode","@value":{"step":[["V"],["out","knows"]]}} gremlin> :bytecode translate g {"@type":"g:Bytecode","@value":{"step":[["V"],["out","knows"]]}} <span class="invisible">//</span><b class="conum">2</b> ==>g.V().out("knows") gremlin> m = GraphSONMapper.build().create() ==>org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper@69d6a7cd gremlin> :bc config m <span class="invisible">//</span><b class="conum">3</b> ==>Configured bytecode serializer gremlin> :bc from g.V().property('d',java.time.YearMonth.now()) <span class="invisible">//</span><b class="conum">4</b> Could not find a type identifier for the class : class java.time.Month. Make sure the value to serialize has a type identifier registered for its class. (through reference chain: java.time.YearMonth["month"]) Type ':help' or ':h' for help. Display stack trace? [yN]n gremlin> :bc reset <span class="invisible">//</span><b class="conum">5</b> ==>Bytecode serializer reset to GraphSON 3.0 with extensions and TinkerGraph serializers gremlin> :bc from g.V().property('d',java.time.YearMonth.now()) ==>{"@type":"g:Bytecode","@value":{"step":[["V"],["property","d",{"@type":"gx:YearMonth","@value":"2020-11"}]]}}</code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Generates a GraphSON 3.0 representation of the traversal as bytecode.</p> </li> <li> <p>Converts bytecode in GraphSON 3.0 format to a traversal string.</p> </li> <li> <p>Configure a custom <code>GraphSONMapper</code> for the <code>:bytecode</code> command to use which can be helpful when working with custom classes from different graph providers. The <code>config</code> option can take a <code>GraphSONMapper</code> argument as shown or one or more <code>IoRegistry</code> or <code>SimpleModule</code> implementations that will plug into the default <code>GraphSONMapper</code> constructed by the <code>:bytecode</code> command. The default will configure for GraphSON 3.0 with the extensions module and, if present, the <code>TinkerIoRegistry</code> from TinkerGraph.</p> </li> <li> <p>Note that the <code>YearMonth</code> will not serialize because <code>m</code> did not configure the extensions module.</p> </li> <li> <p>After <code>reset</code> it works properly once more.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The Console does expose the <code>:record</code> command which is inherited from the Groovy Shell. This command works well with local commands, but may record session outputs differently for <code>:remote</code> commands. If there is a need to use <code>:record</code> it may be best to manually create a <code>Cluster</code> object and issue commands that way so that they evaluate locally in the shell. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_interrupting_evaluations">Interrupting Evaluations</h3> <div class="paragraph"> <p>If there is some input that is taking too long to evaluate or to iterate through, use <code>ctrl+c</code> to attempt to interrupt that process. It is an "attempt" in the sense that the long running process is only informed of the interruption by the user and must respond to it (as with any call to <code>interrupt()</code> on a <code>Thread</code>). A <code>Traversal</code> will typically respond to such requests as do most commands, including <code>:remote</code> operations.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> java.util.stream.IntStream.range(0, 1000).iterator() ==>0 ==>1 ==>2 ==>3 ==>4 ... ==>348 ==>349 ==>350 ==>351 ==>352 Execution interrupted by ctrl+c gremlin></code></pre> </div> </div> </div> <div class="sect2"> <h3 id="console-preferences">Console Preferences</h3> <div class="paragraph"> <p>Preferences are set with <code>:set name value</code>. Values can contain spaces when quoted. All preferences are reset by <code>:purge preferences</code></p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 13.3333%;"> <col style="width: 66.6667%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Preference</th> <th class="tableblock halign-center valign-top">Type</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">max-iteration</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">int</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Controls the maximum number of results that the Console will display. Default: 100 results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bool</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enable ANSI color rendering. Default: true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">warnings</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bool</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enable display of remote execution warnings. Default: true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of the ASCII art gremlin on startup.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">info.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of "info" type messages.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">error.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of "error" type messages.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">vertex.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of vertices results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">edge.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of edges in results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">string.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Colors of strings in results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">number.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of numbers in results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">T.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of Tokens in results.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">input.prompt.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of the input prompt.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">result.prompt.color</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">colors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Color of the result prompt.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">input.prompt</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Text of the input prompt.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">result.prompt</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Text of the result prompt.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">result.indicator.null</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Text of the void/no results indicator - setting to empty string (i.e. "" at the command line) will print no result line in these cases.</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Colors can contain a comma-separated combination of 1 each of foreground, background, and attribute.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 13.3333%;"> <col style="width: 66.6667%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Foreground</th> <th class="tableblock halign-center valign-top">Background</th> <th class="tableblock halign-left valign-top">Attributes</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">black</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_black</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">bold</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">blue</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_blue</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">faint</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">cyan</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_cyan</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">underline</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">green</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_green</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">magenta</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_magenta</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">red</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_red</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">white</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_white</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">yellow</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">bg_yellow</p></td> <td class="tableblock halign-left valign-top"></td> </tr> </tbody> </table> <div class="paragraph"> <p>Example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">:set gremlin.color bg_black,green,bold</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_dependencies_and_plugin_usage">Dependencies and Plugin Usage</h3> <div class="paragraph"> <p>The Gremlin Console can dynamically load external code libraries and make them available to the user. Furthermore, those dependencies may contain Gremlin plugins which can expand the language, provide useful functions, etc. These important console features are managed by the <code>:install</code> and <code>:plugin</code> commands.</p> </div> <div class="paragraph"> <p>The following Gremlin Console session demonstrates the basics of these features:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :plugin list <span class="invisible">//</span><b class="conum">1</b> ==>tinkerpop.server[active] ==>tinkerpop.gephi ==>tinkerpop.utilities[active] ==>tinkerpop.sugar ==>tinkerpop.tinkergraph[active] gremlin> :plugin use tinkerpop.sugar <span class="invisible">//</span><b class="conum">2</b> ==>tinkerpop.sugar activated gremlin> :install org.apache.tinkerpop neo4j-gremlin 3.7.3 <span class="invisible">//</span><b class="conum">3</b> ==><span class="key">loaded</span>: [org.apache.tinkerpop, neo4j-gremlin, 3.7.3] gremlin> :plugin list <span class="invisible">//</span><b class="conum">4</b> ==>tinkerpop.server[active] ==>tinkerpop.gephi ==>tinkerpop.utilities[active] ==>tinkerpop.sugar ==>tinkerpop.tinkergraph[active] ==>tinkerpop.neo4j gremlin> :plugin use tinkerpop.neo4j <span class="invisible">//</span><b class="conum">5</b> ==>tinkerpop.neo4j activated gremlin> :plugin list <span class="invisible">//</span><b class="conum">6</b> ==>tinkerpop.server[active] ==>tinkerpop.gephi ==>tinkerpop.sugar[active] ==>tinkerpop.utilities[active] ==>tinkerpop.neo4j[active] ==>tinkerpop.tinkergraph[active]</code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Show a list of "available" plugins. The list of "available" plugins is determined by the classes available on the Console classpath. Plugins need to be "active" for their features to be available.</p> </li> <li> <p>To make a plugin "active" execute the <code>:plugin use</code> command and specify the name of the plugin to enable.</p> </li> <li> <p>Sometimes there are external dependencies that would be useful within the Console. To bring those in, execute <code>:install</code> and specify the Maven coordinates for the dependency.</p> </li> <li> <p>Note that there is a "tinkerpop.neo4j" plugin available, but it is not yet "active".</p> </li> <li> <p>Again, to use the "tinkerpop.neo4j" plugin, it must be made "active" with <code>:plugin use</code>.</p> </li> <li> <p>Now when the plugin list is displayed, the "tinkerpop.neo4j" plugin is displayed as "active".</p> </li> </ol> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Plugins must be compatible with the version of the Gremlin Console (or Gremlin Server) being used. Attempts to use incompatible versions cannot be guaranteed to work. Moreover, be prepared for dependency conflicts in third-party plugins that may only be resolved via manual jar removal from the <code>ext/{plugin}</code> directory. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> It is possible to manage plugin activation and deactivation by manually editing the <code>ext/plugins.txt</code> file which contains the class names of the "active" plugins. It is also possible to clear dependencies added by <code>:install</code> by deleting them from the <code>ext</code> directory. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="execution-mode">Execution Mode</h3> <div class="paragraph"> <p>For automated tasks and batch executions of Gremlin, it can be useful to execute Gremlin scripts in "execution" mode from the command line. Consider the following file named <code>gremlin.groovy</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">graph = TinkerFactory.createModern() g = traversal().withEmbedded(graph) g.V().each { println <span class="local-variable">it</span> }</code></pre> </div> </div> <div class="paragraph"> <p>This script creates the toy graph and then iterates through all its vertices printing each to the system out. To execute this script from the command line, <code>gremlin.sh</code> has the <code>-e</code> option used as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ bin/gremlin.sh -e gremlin.groovy v[1] v[2] v[3] v[4] v[5] v[6]</code></pre> </div> </div> <div class="paragraph"> <p>It is also possible to pass arguments to scripts. Any parameters following the file name specification are treated as arguments to the script. They are collected into a list and passed in as a variable called "args". The following Gremlin script is exactly like the previous one, but it makes use of the "args" option to filter the vertices printed to system out:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">graph = TinkerFactory.createModern() g = traversal().withEmbedded(graph) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,args[<span class="integer">0</span>]).each { println <span class="local-variable">it</span> }</code></pre> </div> </div> <div class="paragraph"> <p>When executed from the command line a parameter can be supplied:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ bin/gremlin.sh -e gremlin.groovy marko v[1] $ bin/gremlin.sh -e gremlin.groovy vadas v[2]</code></pre> </div> </div> <div class="paragraph"> <p>It is also possible to pass multiple scripts by specifying multiple <code>-e</code> options. The scripts will execute in the order in which they are specified. Note that only the arguments from the last script executed will be preserved in the console. Finally, if the arguments conflict with the reserved flags to which <code>gremlin.sh</code> responds, double quotes can be used to wrap all the arguments to the option:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ bin/gremlin.sh -e "gremlin.groovy -e -i --color"</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="interactive-mode">Interactive Mode</h3> <div class="paragraph"> <p>The Gremlin Console can be started in an "interactive" mode. Interactive mode is like <a href="#execution-mode">execution mode</a> but the console will not exit at the completion of the script, even if the script completes unsuccessfully. In such a case, it will simply stop processing on the line of the script that failed. In this way, the state of the console is such that a user could examine the state of things up to the point of failure, which might make the script easier to debug.</p> </div> <div class="paragraph"> <p>In addition to debugging, interactive mode is a helpful way for users to initialize their console environment to avoid otherwise repetitive typing. For example, a user who spends a lot of time working with the TinkerPop "modern" graph might create a script called <code>init.groovy</code> like:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">graph = TinkerFactory.createModern() g = traversal().withEmbedded(graph)</code></pre> </div> </div> <div class="paragraph"> <p>and then start Gremlin Console as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin.sh -i init.groovy \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph gremlin> g.V() ==>v[1] ==>v[2] ==>v[3] ==>v[4] ==>v[5] ==>v[6]</code></pre> </div> </div> <div class="paragraph"> <p>Note that the user can now reference <code>g</code> (and <code>graph</code> for that matter) at startup without having to directly type that variable initialization code into the console.</p> </div> <div class="paragraph"> <p>As in execution mode, it is also possible to pass multiple scripts by specifying multiple <code>-i</code> options. See the <a href="#execution-mode">Execution Mode Section</a> for more information on the specifics of that capability.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-console-docker-image">Docker Image</h3> <div class="paragraph"> <p>The Gremlin Console can also be started as a <a href="https://hub.docker.com/r/tinkerpop/gremlin-console/">Docker image</a>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ docker run -it tinkerpop/gremlin-console:3.7.3 Feb 25, 2018 3:47:24 PM java.util.prefs.FileSystemPreferences$1 run INFO: Created user preferences directory. \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph gremlin></code></pre> </div> </div> <div class="paragraph"> <p>The Docker image offers the same options as the standalone Console. It can be used for example to execute scripts:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ docker run -it tinkerpop/gremlin-console:3.7.3 -e gremlin.groovy v[1] v[2] v[3] v[4] v[5] v[6]</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-server">Gremlin Server</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-server.png" alt="gremlin server" width="400"></span> Gremlin Server provides a way to remotely execute Gremlin against one or more <code>Graph</code> instances hosted within it. The benefits of using Gremlin Server include:</p> </div> <div class="ulist"> <ul> <li> <p>Allows any Gremlin Structure-enabled graph (i.e. implements the <code>Graph</code> API on the JVM) to exist as a standalone server, which in turn enables the ability for multiple clients to communicate with the same graph database.</p> </li> <li> <p>Enables execution of ad hoc queries through remotely submitted Gremlin.</p> </li> <li> <p>Provides a method for non-JVM languages which may not have a Gremlin Traversal Machine (e.g. Python, Javascript, Go, etc.) to communicate with the TinkerPop stack on the JVM.</p> </li> <li> <p>Exposes numerous methods for extension and customization to include serialization options, remote commands, etc.</p> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Gremlin Server is the replacement for <a href="https://github.com/tinkerpop/rexster">Rexster</a>. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Please see the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/">Provider Documentation</a> for information on how to develop a driver for Gremlin Server. </td> </tr> </table> </div> <div class="paragraph"> <p>By default, communication with Gremlin Server occurs over <a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a> and exposes a custom sub-protocol for interacting with the server.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Gremlin Server allows for the execution of remotely submitted "scripts" (i.e. arbitrary code sent by a client to the server). Developers should consider the security implications involved in running Gremlin Server without the appropriate precautions. Please review the <a href="#security">Security Section</a> and more specifically, the <a href="#script-execution">Script Execution Section</a> for more information. </td> </tr> </table> </div> <div class="sect2"> <h3 id="starting-gremlin-server">Starting Gremlin Server</h3> <div class="paragraph"> <p>Gremlin Server comes packaged with a script called <code>bin/gremlin-server.sh</code> to get it started (use <code>gremlin-server.bat</code> on Windows):</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin-server.sh conf/gremlin-server-modern.yaml [INFO] GremlinServer \,,,/ (o o) -----oOOo-(3)-oOOo----- [INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-modern.yaml [INFO] MetricManager - Configured Metrics Slf4jReporter configured with interval=180000ms and loggerName=org.apache.tinkerpop.gremlin.server.Settings$Slf4jReporterMetrics [INFO] DefaultGraphManager - Graph [graph] was successfully configured via [conf/tinkergraph-empty.properties]. [INFO] ServerGremlinExecutor - Initialized Gremlin thread pool. Threads in pool named with pattern gremlin-* [INFO] ServerGremlinExecutor - Initialized GremlinExecutor and preparing GremlinScriptEngines instances. [INFO] ServerGremlinExecutor - Initialized gremlin-groovy GremlinScriptEngine and registered metrics [INFO] ServerGremlinExecutor - A GraphTraversalSource is now bound to [g] with graphtraversalsource[tinkergraph[vertices:0 edges:0], standard] [INFO] OpLoader - Adding the standard OpProcessor. [INFO] OpLoader - Adding the session OpProcessor. [INFO] OpLoader - Adding the traversal OpProcessor. [INFO] GremlinServer - Executing start up LifeCycleHook [INFO] Logger$info - Loading 'modern' graph data. [INFO] GremlinServer - idleConnectionTimeout was set to 0 which resolves to 0 seconds when configuring this value - this feature will be disabled [INFO] GremlinServer - keepAliveInterval was set to 0 which resolves to 0 seconds when configuring this value - this feature will be disabled [INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+json with org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 [INFO] AbstractChannelizer - Configured application/json with org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 [INFO] AbstractChannelizer - Configured application/vnd.graphbinary-v1.0 with org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 [INFO] AbstractChannelizer - Configured application/vnd.graphbinary-v1.0-stringd with org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 [INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 4 and boss thread pool of 1. [INFO] GremlinServer$1 - Channel started at port 8182.</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin Server is configured by the provided <a href="http://www.yaml.org/">YAML</a> file <code>conf/gremlin-server-modern.yaml</code>. That file tells Gremlin Server many things such as:</p> </div> <div class="ulist"> <ul> <li> <p>The host and port to serve on</p> </li> <li> <p>Thread pool sizes</p> </li> <li> <p>Where to report metrics gathered by the server</p> </li> <li> <p>The serializers to make available</p> </li> <li> <p>The Gremlin <code>ScriptEngine</code> instances to expose and external dependencies to inject into them</p> </li> <li> <p><code>Graph</code> instances to expose</p> </li> </ul> </div> <div class="paragraph"> <p>The log messages that printed above show a number of things, but most importantly, there is a <code>Graph</code> instance named <code>graph</code> that is exposed in Gremlin Server. This graph is an in-memory TinkerGraph and was empty at the start of the server. An initialization script at <code>scripts/generate-modern.groovy</code> was executed during startup. Its contents are as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"> <span class="comment">// an init script that returns a Map allows explicit setting of global bindings.</span> <span class="keyword">def</span> globals = [:] <span class="comment">// Generates the modern graph into an "empty" TinkerGraph via LifeCycleHook.</span> <span class="comment">// Note that the name of the key in the "global" map is unimportant.</span> globals << [hook : [ <span class="key">onStartUp</span>: { ctx -> ctx.logger.info(<span class="string"><span class="delimiter">"</span><span class="content">Loading 'modern' graph data.</span><span class="delimiter">"</span></span>) org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory.generateModern(graph) } ] <span class="keyword">as</span> LifeCycleHook] <span class="comment">// define the default TraversalSource to bind queries to - this one will be named "g".</span> globals << [g : traversal().withEmbedded(graph)]</code></pre> </div> </div> <div class="paragraph"> <p>The script above initializes a <code>Map</code> and assigns two key/values to it. The first, assigned to "hook", defines a <code>LifeCycleHook</code> for Gremlin Server. The "hook" provides a way to tie script code into the Gremlin Server startup and shutdown sequences. The <code>LifeCycleHook</code> has two methods that can be implemented: <code>onStartUp</code> and <code>onShutDown</code>. These events are called once at Gremlin Server start and once at Gremlin Server stop. This is an important point because code outside of the "hook" is executed for each <code>ScriptEngine</code> creation (multiple may be created when "sessions" are enabled) and therefore the <code>LifeCycleHook</code> provides a way to ensure that a script is only executed a single time. In this case, the startup hook loads the "modern" graph into the empty TinkerGraph instance, preparing it for use. The second key/value pair assigned to the <code>Map</code>, named "g", defines a <code>TraversalSource</code> from the <code>Graph</code> bound to the "graph" variable in the YAML configuration file. This variable <code>g</code>, as well as any other variable assigned to the <code>Map</code>, will be made available as variables for future remote script executions. In more general terms, any key/value pairs assigned to a <code>Map</code> returned from the initialization script will become variables that are global to all requests. In addition, any functions that are defined will be cached for future use.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Transactions on graphs in initialization scripts are not closed automatically after the script finishes executing. It is up to the script to properly commit or rollback transactions in the script itself. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="connecting-via-drivers">Connecting via Drivers</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/rexster-connect.png" alt="rexster connect" width="180"></span> TinkerPop offers client-side drivers for the Gremlin Server websocket sub-protocol in a variety of languages:</p> </div> <div class="ulist"> <ul> <li> <p><a href="#gremlin-dotnet">C#</a></p> </li> <li> <p><a href="#gremlin-go">Go</a></p> </li> <li> <p><a href="#gremlin-java">Java</a></p> </li> <li> <p><a href="#gremlin-javascript">Javascript</a></p> </li> <li> <p><a href="#gremlin-python">Python</a></p> </li> </ul> </div> <div class="paragraph"> <p>These drivers provide methods to send Gremlin based requests and get back traversal results as a response. The requests may be script-based or bytecode-based. As discussed earlier in the <a href="#connecting-gremlin-server">introduction</a> the recommendation is to use bytecode-based requests. The difference between sending scripts and sending bytecode are demonstrated below in some basic examples:</p> </div> <section class="tabs tabs-6"> <input id="tab-1729797129-5" type="radio" name="radio-set-1729797129-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-5" class="tab-label-1">java</label> <input id="tab-1729797129-6" type="radio" name="radio-set-1729797129-5" class="tab-selector-2" /> <label for="tab-1729797129-6" class="tab-label-2">groovy</label> <input id="tab-1729797129-7" type="radio" name="radio-set-1729797129-5" class="tab-selector-3" /> <label for="tab-1729797129-7" class="tab-label-3">csharp</label> <input id="tab-1729797129-8" type="radio" name="radio-set-1729797129-5" class="tab-selector-4" /> <label for="tab-1729797129-8" class="tab-label-4">javascript</label> <input id="tab-1729797129-9" type="radio" name="radio-set-1729797129-5" class="tab-selector-5" /> <label for="tab-1729797129-9" class="tab-label-5">python</label> <input id="tab-1729797129-10" type="radio" name="radio-set-1729797129-5" class="tab-selector-6" /> <label for="tab-1729797129-10" class="tab-label-6">go</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// script</span> Cluster cluster = Cluster.open(); Client client = cluster.connect(); <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> params = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); params.put(<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="predefined-type">List</span><<span class="predefined-type">Result</span>> list = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().has('person','name',name).out('knows')</span><span class="delimiter">"</span></span>, params).all().get(); <span class="comment">// bytecode</span> GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(<span class="string"><span class="delimiter">"</span><span class="content">localhost</span><span class="delimiter">"</span></span>,<span class="integer">8182</span>,<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>)); <span class="predefined-type">List</span><Vertex> list = 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>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).toList();</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"><span class="comment">// script</span> <span class="keyword">def</span> cluster = Cluster.open() <span class="keyword">def</span> client = cluster.connect() <span class="keyword">def</span> list = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().has('person','name',name).out('knows')</span><span class="delimiter">"</span></span>, [<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>]).all().get(); <span class="comment">// bytecode</span> <span class="keyword">def</span> g = traversal().withRemote(DriverRemoteConnection.using(<span class="string"><span class="delimiter">"</span><span class="content">localhost</span><span class="delimiter">"</span></span>,<span class="integer">8182</span>,<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>)) <span class="keyword">def</span> list = 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">// script var gremlinServer = new GremlinServer("localhost", 8182); using (var gremlinClient = new GremlinClient(gremlinServer)) { var bindings = new Dictionary<string, object> { {"name", "marko"} }; var response = await gremlinClient.SubmitWithSingleResultAsync<object>("g.V().has('person','name',name).out('knows')", bindings); } // bytecode using (var gremlinClient = new GremlinClient(new GremlinServer("localhost", 8182))) { var g = Traversal().WithRemote(new DriverRemoteConnection(gremlinClient)); var list = g.V().Has("person", "name", "marko").Out("knows").ToList(); }</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-4"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript"><span class="comment">// script</span> const client = <span class="keyword">new</span> Client(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:45940/gremlin</span><span class="delimiter">'</span></span>, { <span class="key">traversalSource</span>: <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span> }); const conn = client.open(); const list = conn.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().has('person','name',name).out('knows')</span><span class="delimiter">"</span></span>,{<span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>}).then(<span class="keyword">function</span> (response) { ... }); <span class="comment">// bytecode</span> const g = gtraversal().withRemote(<span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>)); const list = 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>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).toList();</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-5"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="comment"># script</span> client = Client(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>) list = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().has('person','name',name).out('knows')</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>}).all() <span class="comment"># bytecode</span> g = traversal().withRemote(DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>)) list = 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>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).toList()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-6"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="comment">// script</span> client, err := NewClient(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) resultSet, err := client.SubmitWithOptions(<span class="string"><span class="delimiter">"</span><span class="content">g.V().has('person','name',name).out('knows')</span><span class="delimiter">"</span></span>, <span class="predefined">new</span>(RequestOptionsBuilder).AddBinding(<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>).Create()) result, err := resultSet.All() <span class="comment">// bytecode</span> remote, err := NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) g := Traversal_().WithRemote(remote) list, err := 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>).Out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).ToList()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The advantage of bytecode over scripts should be apparent from the above examples. Scripts are just strings that are embedded in code (in the above examples, the strings are Groovy-based) whereas bytecode based requests are themselves code written in the native language of use. Obviously, the advantage of the Gremlin being actual code is that there are checks (e.g. compile-time, auto-complete and other IDE support, language level checks, etc.) that help validate the Gremlin during the development process.</p> </div> <div class="paragraph"> <p>TinkerPop makes an effort to ensure a high-level of consistency among the drivers and their features, but there are differences in capabilities and features as they are each developed independently. The Java driver was the first and is therefore the most advanced. Please see the related documentation for the driver of interest for more information and details in the <a href="#gremlin-drivers-variants">Gremlin Drivers and Variants</a> Section of this documentation.</p> </div> </div> <div class="sect2"> <h3 id="connecting-via-console">Connecting via Console</h3> <div class="paragraph"> <p>With Gremlin Server running it is now possible to issue some scripts to it for processing. Start Gremlin Console as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- gremlin></code></pre> </div> </div> <div class="paragraph"> <p>The console has the notion of a "remote", which represents a place a script will be sent from the console to be evaluated elsewhere in some other context (e.g. Gremlin Server, Hadoop, etc.). To create a remote in the console, do the following:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-11" type="radio" name="radio-set-1729797129-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-12" type="radio" name="radio-set-1729797129-11" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote connect tinkerpop.server conf/remote.yaml ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</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">:remote connect tinkerpop.server conf/remote.yaml</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>:remote</code> command shown above displays the current status of the remote connection. This command can also be used to configure a new connection and change other related settings. To actually send a script to the server a different command is required:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-13" type="radio" name="radio-set-1729797129-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-14" type="radio" name="radio-set-1729797129-13" class="tab-selector-2" /> <label for="tab-1729797129-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> :> g.V().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> :> 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>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>lop gremlin> :> g.E().label().groupCount() ==>{created=<span class="integer">4</span>, knows=<span class="integer">2</span>} gremlin> result ==>result{object={created=<span class="integer">4</span>, knows=<span class="integer">2</span>} <span class="type">class</span>=<span class="class">java</span>.lang.String} gremlin> :remote close ==>Removed - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</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">'</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>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) :> g.E().label().groupCount() result :remote close</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The <code>:></code> command, which is a shorthand for <code>:submit</code>, sends the script to the server to execute there. Results are wrapped in an <code>Result</code> object which is a just a holder for each individual result. The <code>class</code> shows the data type for the containing value. Note that the last script sent was supposed to return a <code>Map</code>, but its <code>class</code> is <code>java.lang.String</code>. By default, the connection is configured to only return text results. In other words, Gremlin Server is using <code>toString</code> to serialize all results back to the console. This enables virtually any object on the server to be returned to the console, but it doesn’t allow the opportunity to work with this data in any way in the console itself. A different configuration of the <code>:remote</code> is required to get the results back as "objects":</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-15" type="radio" name="radio-set-1729797129-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-16" type="radio" name="radio-set-1729797129-15" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote connect tinkerpop.server conf/remote-objects.yaml <span class="comment">//</span>// <b class="conum">(1)</b> ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> :remote list <span class="comment">//</span>// <b class="conum">(2)</b> ==>*<span class="integer">0</span> - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>] gremlin> :> g.E().label().groupCount() <span class="comment">//</span>// <b class="conum">(3)</b> ==>[<span class="key">created</span>:<span class="integer">4</span>,<span class="key">knows</span>:<span class="integer">2</span>] gremlin> m = result[<span class="integer">0</span>].object <span class="comment">//</span>// <b class="conum">(4)</b> ==>created=<span class="integer">4</span> ==>knows=<span class="integer">2</span> gremlin> m.sort {<span class="local-variable">it</span>.value} ==>knows=<span class="integer">2</span> ==>created=<span class="integer">4</span> gremlin> script = <span class="string"><span class="delimiter">"""</span><span class="content"> g.V().hasLabel('person'). out('knows'). out('created'). group(). by('name') </span><span class="delimiter">"""</span></span> ==> g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). group(). by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) gremlin> :> <span class="annotation">@script</span> <span class="comment">//</span>// <b class="conum">(5)</b> ==>[<span class="key">ripple</span>:[v[<span class="integer">5</span>]],<span class="key">lop</span>:[v[<span class="integer">3</span>]]] gremlin> :remote close ==>Removed - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</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">:remote connect tinkerpop.server conf/remote-objects.yaml <span class="comment">//</span>// <b class="conum">(1)</b> :remote list <span class="comment">//</span>// <b class="conum">(2)</b> :> g.E().label().groupCount() <span class="comment">//</span>// <b class="conum">(3)</b> m = result[<span class="integer">0</span>].object <span class="comment">//</span>// <b class="conum">(4)</b> m.sort {<span class="local-variable">it</span>.value} script = <span class="string"><span class="delimiter">"""</span><span class="content"> g.V().hasLabel('person'). out('knows'). out('created'). group(). by('name') </span><span class="delimiter">"""</span></span> :> <span class="annotation">@script</span> <span class="comment">//</span>// <b class="conum">(5)</b> :remote close</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>This configuration file specifies that results should be deserialized back into an <code>Object</code> in the console with the caveat being that the server and console both know how to serialize and deserialize the result to be returned.</p> </li> <li> <p>There are now two configured remote connections. The one marked by an asterisk is the one that was just created and denotes the current one that <code>:submit</code> will react to.</p> </li> <li> <p>When the script is executed again, the <code>class</code> is no longer shown to be a <code>java.lang.String</code>. It is instead a <code>java.util.HashMap</code>.</p> </li> <li> <p>The last result of a remote script is always stored in the reserved variable <code>result</code>, which allows access to the <code>Result</code> and by virtue of that, the <code>Map</code> itself.</p> </li> <li> <p>If the submission requires multiple-lines to express, then a multi-line string can be created. The <code>:></code> command realizes that the user is referencing a variable via <code>@</code> and submits the string script.</p> </li> </ol> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> In Groovy, <code>""" text """</code> is a convenient way to create a multi-line string and works well in concert with <code>:> @variable</code>. Note that this model of submitting a string variable works for all <code>:></code> based plugins, not just Gremlin Server. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Not all values that can be returned from a Gremlin script end up being serializable. For example, submitting <code>:> graph</code> will return a <code>Graph</code> instance and in most cases those are not serializable by Gremlin Server and will return a serialization error. It should be noted that <code>TinkerGraph</code>, as a convenience for shipping around small sub-graphs, is serializable from Gremlin Server. </td> </tr> </table> </div> <div class="paragraph"> <p>The alternative syntax to connecting allows for the <code>Cluster</code> to be user constructed directly in the console as opposed to simply providing a static YAML file.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-17" type="radio" name="radio-set-1729797129-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-18" type="radio" name="radio-set-1729797129-17" class="tab-selector-2" /> <label for="tab-1729797129-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> cluster = Cluster.open() ==>localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> :remote connect tinkerpop.server cluster ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</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">cluster = Cluster.open() :remote connect tinkerpop.server cluster</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The Gremlin Server <code>:remote config</code> command for the driver has the following configuration options:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 23.0769%;"> <col style="width: 76.9231%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Command</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">alias</p></td> <td class="tableblock halign-left valign-top"><div class="content"><table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 23.0769%;"> <col style="width: 76.9231%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Option</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><em>pairs</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A set of key/value alias/binding pairs to apply to requests.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>reset</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Clears any aliases that were supplied in previous configurations of the remote.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>show</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Shows the current set of aliases which is returned as a <code>Map</code></p></td> </tr> </tbody> </table></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">timeout</p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>Specifies the length of time in milliseconds the Console will wait for a response from the server. Specify "none" to have no timeout. By default, this setting uses "none".</p> </div></div></td> </tr> </tbody> </table> <div class="sect3"> <h4 id="console-aliases">Aliases</h4> <div class="paragraph"> <p>The <code>alias</code> configuration command for the Gremlin Server <code>:remote</code> can be useful in situations where there are multiple <code>Graph</code> or <code>TraversalSource</code> instances on the server, as it becomes possible to rename them from the client for purposes of execution within the context of a script. Therefore, it becomes possible to submit commands this way:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-19" type="radio" name="radio-set-1729797129-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-20" type="radio" name="radio-set-1729797129-19" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote connect tinkerpop.server conf/remote-objects.yaml ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> :remote config alias x g ==>x=g gremlin> :> x.E().label().groupCount() ==>[<span class="key">created</span>:<span class="integer">4</span>,<span class="key">knows</span>:<span class="integer">2</span>] gremlin> :remote close ==>Removed - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</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">:remote connect tinkerpop.server conf/remote-objects.yaml :remote config alias x g :> x.E().label().groupCount() :remote close</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="console-sessions">Sessions</h4> <div class="paragraph"> <p>A <code>:remote</code> created in the following fashion will be "sessionless", meaning each script issued to the server with <code>:></code> will be encased in a transaction and no state will be maintained from one request to the next.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :remote connect tinkerpop.server conf/remote-objects.yaml ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span></code></pre> </div> </div> <div class="paragraph"> <p>In other words, the transaction will be automatically committed (or rolledback on error) and any variables declared in that script will be forgotten for the next request. See the section on <a href="#sessions">"Considering Sessions"</a> for more information on that topic.</p> </div> <div class="paragraph"> <p>To enable the remote to connect with a session the <code>connect</code> argument takes another argument as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-21" type="radio" name="radio-set-1729797129-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-22" type="radio" name="radio-set-1729797129-21" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote connect tinkerpop.server conf/remote.yaml session ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>-[<span class="octal">07</span>c88e7e-<span class="integer">8178</span>-<span class="integer">4885</span>-<span class="float">9f</span>b2-<span class="float">010f</span><span class="integer">92</span>efb2c2] gremlin> :> x = <span class="integer">1</span> ==><span class="integer">1</span> gremlin> :> y = <span class="integer">2</span> ==><span class="integer">2</span> gremlin> :> x + y ==><span class="integer">3</span> gremlin> :remote close ==>Removed - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>]-[<span class="octal">07</span>c88e7e-<span class="integer">8178</span>-<span class="integer">4885</span>-<span class="float">9f</span>b2-<span class="float">010f</span><span class="integer">92</span>efb2c2]</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">:remote connect tinkerpop.server conf/remote.yaml session :> x = <span class="integer">1</span> :> y = <span class="integer">2</span> :> x + y :remote close</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>With the above command a session gets created with a random UUID for a session identifier. It is also possible to assign a custom session identifier by adding it as the last argument to <code>:remote</code> command above. There is also the option to replace "session" with "session-managed" to create a session that will auto-manage transactions (i.e. each request will occur within the bounds of a transaction). In this way, the state of bound variables between requests are maintained, but the need to manually managed the transactional scope of the graph is no longer required.</p> </div> </div> <div class="sect3"> <h4 id="console-remote-console">Remote Console</h4> <div class="paragraph"> <p>Previous examples have shown usage of the <code>:></code> command to send scripts to Gremlin Server. The Gremlin Console also supports an additional method for doing this which can be more convenient when the intention is to exclusively work with a remote connection to the server.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-23" type="radio" name="radio-set-1729797129-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-24" type="radio" name="radio-set-1729797129-23" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote connect tinkerpop.server conf/remote.yaml session ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>-[bdc8b7dd-bc2a-<span class="integer">4820</span>-<span class="integer">83</span>eb-aefcd8819a1e] gremlin> :remote console ==>All scripts will now be sent to Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>]-[bdc8b7dd-bc2a-<span class="integer">4820</span>-<span class="integer">83</span>eb-aefcd8819a1e] - type <span class="string"><span class="delimiter">'</span><span class="content">:remote console</span><span class="delimiter">'</span></span> to <span class="keyword">return</span> to local mode gremlin> x = <span class="integer">1</span> ==><span class="integer">1</span> gremlin> y = <span class="integer">2</span> ==><span class="integer">2</span> gremlin> x + y ==><span class="integer">3</span> gremlin> :remote console ==>All scripts will now be evaluated locally - type <span class="string"><span class="delimiter">'</span><span class="content">:remote console</span><span class="delimiter">'</span></span> to <span class="keyword">return</span> to remote mode <span class="keyword">for</span> Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>]-[bdc8b7dd-bc2a-<span class="integer">4820</span>-<span class="integer">83</span>eb-aefcd8819a1e] gremlin> :remote close ==>Removed - Gremlin Server - [localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span>]-[bdc8b7dd-bc2a-<span class="integer">4820</span>-<span class="integer">83</span>eb-aefcd8819a1e]</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">:remote connect tinkerpop.server conf/remote.yaml session :remote console x = <span class="integer">1</span> y = <span class="integer">2</span> x + y :remote console :remote close</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>In the above example, the <code>:remote console</code> command is executed. It places the console in a state where the <code>:></code> is no longer required. Each script line is actually automatically submitted to Gremlin Server for evaluation. The variables <code>x</code> and <code>y</code> that were defined actually don’t exist locally - they only exist on the server! In this sense, putting the console in this mode is basically like creating a window to a session on Gremlin Server.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When using <code>:remote console</code> there is not much point to using a configuration that uses a serializer that returns actual data. In other words, using a configuration like the one inside of <code>conf/remote-objects.yaml</code> isn’t typically useful as in this mode the result will only ever be displayed but not used. Using a serializer configuration like the one in <code>conf/remote.yaml</code> should perform better. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Console commands, those that begin with a colon (e.g. <code>:x</code>, <code>:remote</code>) do not execute remotely when in this mode. They are all still evaluated locally. </td> </tr> </table> </div> </div> </div> <div class="sect2"> <h3 id="connecting-via-http">Connecting via HTTP</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-rexster.png" alt="gremlin rexster" width="225"></span> While the default behavior for Gremlin Server is to provide a WebSocket-based connection, it can also be configured to support plain HTTP web service. The HTTP endpoint provides for a communication protocol familiar to most developers, with a wide support of programming languages, tools and libraries for accessing it. As a result, HTTP provides a fast way to get started with Gremlin Server. It also may represent an easier upgrade path from <a href="https://github.com/tinkerpop/rexster">Rexster</a> as the API for the endpoint is very similar to Rexster’s <a href="https://github.com/tinkerpop/rexster/wiki/Gremlin-Extension">Gremlin Extension</a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> TinkerPop provides and supports this HTTP endpoint as a convenience and for legacy reasons, but users should prefer the recommended approach of bytcode based requests as described in <a href="#connecting-gremlin">Connecting Gremlin</a> section. </td> </tr> </table> </div> <div class="paragraph"> <p>Gremlin Server provides for a single HTTP endpoint - a Gremlin evaluator - which allows the submission of a Gremlin script as a request. For each request, it returns a response containing the serialized results of that script. To enable this endpoint, Gremlin Server needs to be configured with the <code>HttpChannelizer</code>, which replaces the default. The <code>WsAndHttpChannelizer</code> may also be configured to enable both WebSockets and the REST endpoint in the configuration file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">channelizer</span>: <span class="string"><span class="content">org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer</span></span></code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">channelizer</span>: <span class="string"><span class="content">org.apache.tinkerpop.gremlin.server.channel.WsAndHttpChannelizer</span></span></code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>UnifiedChannelizer</code> introduced in 3.5.0 can also be used to support HTTP requests as its functionality is similar to <code>WsAndHttpChannelizer</code>. Please see the Gremlin Server UnifiedChannelizer Section of the Upgrade Documentation for 3.5.0 for more <a href="https://tinkerpop.apache.org/docs/3.7.3/upgrade/#_tinkerpop_3_5_0">details</a>. </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>HttpChannelizer</code> is already configured in the <code>gremlin-server-rest-modern.yaml</code> file that is packaged with the Gremlin Server distribution. To utilize it, start Gremlin Server as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/gremlin-server.sh conf/gremlin-server-rest-modern.yaml</code></pre> </div> </div> <div class="paragraph"> <p>Once the server has started, issue a request. Here’s an example with <a href="http://curl.haxx.se/">cURL</a>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ curl "http://localhost:8182?gremlin=100-1"</code></pre> </div> </div> <div class="paragraph"> <p>which returns:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="js">{ <span class="key"><span class="delimiter">"</span><span class="content">result</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">data</span><span class="delimiter">"</span></span>:<span class="integer">99</span>,<span class="key"><span class="delimiter">"</span><span class="content">meta</span><span class="delimiter">"</span></span>:{}}, <span class="key"><span class="delimiter">"</span><span class="content">requestId</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">0581cdba-b152-45c4-80fa-3d36a6eecf1c</span><span class="delimiter">"</span></span>, <span class="key"><span class="delimiter">"</span><span class="content">status</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">code</span><span class="delimiter">"</span></span>:<span class="integer">200</span>,<span class="key"><span class="delimiter">"</span><span class="content">attributes</span><span class="delimiter">"</span></span>:{},<span class="key"><span class="delimiter">"</span><span class="content">message</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="delimiter">"</span></span>} }</code></pre> </div> </div> <div class="paragraph"> <p>The above example showed a <code>GET</code> operation, but the preferred method for this endpoint is <code>POST</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">curl -X POST -d "{\"gremlin\":\"100-1\"}" "http://localhost:8182"</code></pre> </div> </div> <div class="paragraph"> <p>which returns:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="js">{ <span class="key"><span class="delimiter">"</span><span class="content">result</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">data</span><span class="delimiter">"</span></span>:<span class="integer">99</span>,<span class="key"><span class="delimiter">"</span><span class="content">meta</span><span class="delimiter">"</span></span>:{}}, <span class="key"><span class="delimiter">"</span><span class="content">requestId</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">ef2fe16c-441d-4e13-9ddb-3c7b5dfb10ba</span><span class="delimiter">"</span></span>, <span class="key"><span class="delimiter">"</span><span class="content">status</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">code</span><span class="delimiter">"</span></span>:<span class="integer">200</span>,<span class="key"><span class="delimiter">"</span><span class="content">attributes</span><span class="delimiter">"</span></span>:{},<span class="key"><span class="delimiter">"</span><span class="content">message</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="delimiter">"</span></span>} }</code></pre> </div> </div> <div class="paragraph"> <p>It is also preferred that Gremlin scripts be parameterized when possible via <code>bindings</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">curl -X POST -d "{\"gremlin\":\"100-x\", \"bindings\":{\"x\":1}}" "http://localhost:8182"</code></pre> </div> </div> <div class="paragraph"> <p>The <code>bindings</code> argument is a <code>Map</code> of variables where the keys become available as variables in the Gremlin script. Note that parameterization of requests is critical to performance, as repeated script compilation can be avoided on each request.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> It is possible to pass bindings via <code>GET</code> based requests. Query string arguments prefixed with "bindings." will be treated as parameters, where that prefix will be removed and the value following the period will become the parameter name. In other words, <code>bindings.x</code> will create a parameter named "x" that can be referenced in the submitted Gremlin script. The caveat is that these arguments will always be treated as <code>String</code> values. To ensure that data types are preserved or to pass complex objects such as lists or maps, use <code>POST</code> which will at least support the allowed JSON data types. </td> </tr> </table> </div> <div class="paragraph"> <p>Passing the <code>Accept</code> header with a valid MIME type will trigger the server to return the result in a particular format. Note that in addition to the formats available given the server’s <code>serializers</code> configuration, there is also a basic <code>text/plain</code> format which produces a text representation of results similar to the Gremlin Console:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://localhost:8182" ==>v[1] ==>v[2] ==>v[3] ==>v[4] ==>v[5] ==>v[6]</code></pre> </div> </div> <div class="paragraph"> <p>Finally, as Gremlin Server can host multiple <code>ScriptEngine</code> instances (e.g. <code>gremlin-groovy</code>, <code>nashorn</code>), it is possible to define the language to utilize to process the request:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">curl -X POST -d "{\"gremlin\":\"100-x\", \"language\":\"gremlin-groovy\", \"bindings\":{\"x\":1}}" "http://localhost:8182"</code></pre> </div> </div> <div class="paragraph"> <p>By default this value is set to <code>gremlin-groovy</code>. If using a <code>GET</code> operation, this value can be set as a query string argument with by setting the <code>language</code> key.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Consider the size of the result of a submitted script being returned from the HTTP endpoint. A script that iterates thousands of results will serialize each of those in memory into a single JSON result set. It is quite possible that such a script will generate <code>OutOfMemoryError</code> exceptions on the server. Consider the default WebSocket configuration, which supports streaming, if that type of use case is required. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_configuring_2">Configuring</h3> <div class="paragraph"> <p>The <code>gremlin-server.sh</code> file serves multiple purposes. It can be used to "install" dependencies to the Gremlin Server path. For example, to be able to configure and use other <code>Graph</code> implementations, the dependencies must be made available to Gremlin Server. To do this, use the <code>install</code> switch and supply the Maven coordinates for the dependency to "install". For example, to use Neo4j in Gremlin Server:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/gremlin-server.sh install org.apache.tinkerpop neo4j-gremlin 3.7.3</code></pre> </div> </div> <div class="paragraph"> <p>This command will "grab" the appropriate dependencies and copy them to the <code>ext</code> directory of Gremlin Server, which will then allow them to be "used" the next time the server is started. To uninstall dependencies, simply delete them from the <code>ext</code> directory.</p> </div> <div class="paragraph"> <p><code>bin/gremlin-server.sh</code> has several other options.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 23.0769%;"> <col style="width: 76.9231%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Parameter</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">start</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Start the server in the background.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">stop</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Shutdown the server.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">restart</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Shutdown a running server then start it again.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">status</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Check if the server is running.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">console</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Start the server in the foreground. Use ^C to kill it.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">install <group> <artifact> <version></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Install dependencies into the server. "-i" exists for backwards compatibility but is deprecated.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><conf file></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Start the server in the foreground using the provided YAML config file.</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>The <code>bin/gremlin-server.sh</code> script can be customized with environment variables in <code>bin/gremlin-server.conf</code>.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 23.0769%;"> <col style="width: 76.9231%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Variable</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">DEBUG</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enable debugging of the startup script</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">GREMLIN_HOME</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The Gremlin Server install directory. Use this if the script has trouble finding itself.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">GREMLIN_YAML</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The default server YAML file (conf/gremlin-server.yaml)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">LOG_DIR</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Location of gremlin.log where stdout/stderr are captured (logs/)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">PID_DIR</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Location of gremlin.pid</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">RUNAS</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">User to run the server as</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">JAVA_HOME</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Java install location. Will use $JAVA_HOME/bin/java</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">JAVA_OPTIONS</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Options passed to the JVM</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>As mentioned earlier, Gremlin Server is configured though a YAML file. By default, Gremlin Server will look for a file called <code>conf/gremlin-server.yaml</code> to configure itself on startup. To override this default, set GREMLIN_YAML in <code>bin/gremlin-server.conf</code> or supply the file to use to <code>bin/gremlin-server.sh</code> as in:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/gremlin-server.sh conf/gremlin-server-min.yaml</code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> On Windows, gremlin-server.bat will always start in the foreground. When no parameter is provided, it will start with the default <code>conf/gremlin-server.yaml</code> file. </td> </tr> </table> </div> <div class="paragraph"> <p>The following table describes the various YAML configuration options that Gremlin Server expects:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">authentication.authenticator</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of an <code>Authenticator</code> implementation to use. If this setting is not present, then authentication is effectively disabled.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>AllowAllAuthenticator</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">authentication.authenticationHandler</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of an <code>AbstractAuthenticationHandler</code> implementation to use. If this setting is not present, but the <code>authentication.authenticator</code> is, it will use that authenticator with the default <code>AbstractAuthenticationHandler</code> implementation for the specified <code>Channelizer</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">authentication.config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of configuration settings to be passed to the <code>Authenticator</code> when it is constructed. The settings available are dependent on the implementation.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">authorization.authorizer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of an <code>Authorizer</code> implementation to use.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">authorization.config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of configuration settings to be passed to the <code>Authorizer</code> when it is constructed. The settings available are dependent on the implementation.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">channelizer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of the <code>Channelizer</code> implementation to use. A <code>Channelizer</code> is a "channel initializer" which Gremlin Server uses to define the type of processing pipeline to use. By allowing different <code>Channelizer</code> implementations, Gremlin Server can support different communication protocols (e.g. WebSocket).</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>WebSocketChannelizer</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enableAuditLog</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The <code>AuthenticationHandler</code>, <code>AuthorizationHandler</code> and processors can issue audit logging messages with the authenticated user, remote socket address and requests with a gremlin query. For privacy reasons, the default value of this setting is false. The audit logging messages are logged at the INFO level via the <code>audit.org.apache.tinkerpop.gremlin.server</code> logger, which can be configured using the <code>logback.xml</code> file.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>false</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">graphManager</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of the <code>GraphManager</code> implementation to use. A <code>GraphManager</code> is a class that adheres to the TinkerPop <code>GraphManager</code> interface, allowing custom implementations for storing and managing graph references, as well as defining custom methods to open and close graphs instantiations. To prevent Gremlin Server from starting when all graphs fails, the <code>CheckedGraphManager</code> can be used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>DefaultGraphManager</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">graphs</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of <code>Graph</code> configuration files where the key of the <code>Map</code> becomes the name to which the <code>Graph</code> will be bound and the value is the file name of a <code>Graph</code> configuration file.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlinPool</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The number of "Gremlin" threads available to execute actual scripts in a <code>ScriptEngine</code>. This pool represents the workers available to handle blocking operations in Gremlin Server. When set to <code>0</code>, Gremlin Server will use the value provided by <code>Runtime.availableProcessors()</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">host</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The name of the host to bind the server to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">idleConnectionTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds that the server will allow a channel to not receive requests from a client before it automatically closes. If enabled, the value provided should typically exceed the amount of time given to <code>keepAliveInterval</code>. Note that while this value is to be provided as milliseconds it will resolve to second precision. Set this value to <code>0</code> to disable this feature.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">keepAliveInterval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds that the server will allow a channel to not send responses to a client before it sends a "ping" to see if it is still present. If it is present, the client should respond with a "pong" which will thus reset the <code>idleConnectionTimeout</code> and keep the channel open. If enabled, this number should be smaller than the value provided to the <code>idleConnectionTimeout</code>. Note that while this value is to be provided as milliseconds it will resolve to second precision. Set this value to <code>0</code> to disable this feature.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxAccumulationBufferComponents</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of request components that can be aggregated for a message.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1024</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxChunkSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum length of the content or each chunk. If the content length exceeds this value, the transfer encoding of the decoded request will be converted to 'chunked' and the content will be split into multiple <code>HttpContent</code> objects. If the transfer encoding of the HTTP request is 'chunked' already, each chunk will be split into smaller chunks if the length of the chunk exceeds this value.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8192</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxContentLength</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum length of the aggregated content for a message. Works in concert with <code>maxChunkSize</code> where chunked requests are accumulated back into a single message. A request exceeding this size will return a <code>413 - Request Entity Too Large</code> status code. A response exceeding this size will raise an internal exception.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">65536</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxHeaderSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum length of all headers.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8192</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxInitialLineLength</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum length of the initial line (e.g. "GET / HTTP/1.0") processed in a request, which essentially controls the maximum length of the submitted URI.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4096</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxParameters</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of parameters that can be passed on a request. Larger numbers may impact performance for scripts. This configuration only applies to the <code>UnifiedChannelizer</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">16</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxSessionTaskQueueSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum size that an individual session can queue requests before starting to reject them. This configuration only applies to the <code>UnifiedChannelizer</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4096</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxWorkQueueSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum size the general processing queue can grow before the <code>gremlinPool</code> starts to reject requests.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8192</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.consoleReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on console reporting of metrics.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.consoleReporter.interval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds between reports of metrics to console.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.csvReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on CSV reporting of metrics.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.csvReporter.fileName</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The file to write metrics to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.csvReporter.interval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds between reports of metrics to file.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.gangliaReporter.addressingMode</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Set to <code>MULTICAST</code> or <code>UNICAST</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.gangliaReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on Ganglia reporting of metrics. Additional <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#metrics">setup</a> is required.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.gangliaReporter.host</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Define the Ganglia host to report Metrics to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.gangliaReporter.interval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds between reports of metrics for Ganglia.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.gangliaReporter.port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Define the Ganglia port to report Metrics to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8649</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.graphiteReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on Graphite reporting of metrics. Additional <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#metrics">setup</a> is required.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.graphiteReporter.host</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Define the Graphite host to report Metrics to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.graphiteReporter.interval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds between reports of metrics for Graphite.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.graphiteReporter.port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Define the Graphite port to report Metrics to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">2003</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.graphiteReporter.prefix</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Define a "prefix" to append to metrics keys reported to Graphite.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.jmxReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on JMX reporting of metrics.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.slf4jReporter.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Turns on SLF4j reporting of metrics.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">metrics.slf4jReporter.interval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds between reports of metrics to SLF4j.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The port to bind the server to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8182</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">processors</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>List</code> of <code>Map</code> settings, where each <code>Map</code> represents a <code>OpProcessor</code> implementation to use along with its configuration.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">processors[X].className</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The full class name of the <code>OpProcessor</code> implementation.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">processors[X].config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> containing <code>OpProcessor</code> specific configurations.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">resultIterationBatchSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Defines the size in which the result of a request is "batched" back to the client. In other words, if set to <code>1</code>, then a result that had ten items in it would get each result sent back individually. If set to <code>2</code> the same ten results would come back in five batches of two each.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">64</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">scriptEngines</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of <code>ScriptEngine</code> implementations to expose through Gremlin Server, where the key is the name given by the <code>ScriptEngine</code> implementation. The key must match the name exactly for the <code>ScriptEngine</code> to be constructed. The value paired with this key is itself a <code>Map</code> of configuration for that <code>ScriptEngine</code>. If this value is not set, it will default to "gremlin-groovy".</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>gremlin-groovy</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">scriptEngines.<name>.imports</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A comma separated list of classes/packages to make available to the <code>ScriptEngine</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">scriptEngines.<name>.staticImports</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A comma separated list of "static" imports to make available to the <code>ScriptEngine</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">scriptEngines.<name>.scripts</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A comma separated list of script files to execute on <code>ScriptEngine</code> initialization. <code>Graph</code> and <code>TraversalSource</code> instance references produced from scripts will be stored globally in Gremlin Server, therefore it is possible to use initialization scripts to add Traversal Strategies or create entirely new <code>Graph</code> instances all together. Instantiating a <code>LifeCycleHook</code> in a script provides a way to execute scripts when Gremlin Server starts and stops.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">scriptEngines.<name>.config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of configuration settings for the <code>ScriptEngine</code>. These settings are dependent on the <code>ScriptEngine</code> implementation being used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">evaluationTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The amount of time in milliseconds before a request evaluation and iteration of result times out. This feature can be turned off by setting the value to <code>0</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">30000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">serializers</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>List</code> of <code>Map</code> settings, where each <code>Map</code> represents a <code>MessageSerializer</code> implementation to use along with its configuration. If this value is not set, then Gremlin Server will configure with GraphSON and GraphBinary but will not register any <code>ioRegistries</code> for configured graphs.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>empty</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">serializers[X].className</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The full class name of the <code>MessageSerializer</code> implementation.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">serializers[X].config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> containing <code>MessageSerializer</code> specific configurations.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">sessionLifetimeTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum time in milliseconds that a session can exist. This value cannot be extended beyond this value irrespective of the number of requests and their individual timeouts. The session life cannot be extended once started. This configuration only applies to the <code>UnifiedChannelizer</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">600000 (10 minutes)</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.enabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines if SSL is turned on or not.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.keyStore</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The private key in JKS or PKCS#12 format.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.keyStorePassword</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password of the <code>keyStore</code> if it is password-protected.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.keyStoreType</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>JKS</code> (Java 8 default) or <code>PKCS12</code> (Java 9+ default)</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.needClientAuth</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Optional. One of NONE, REQUIRE. Enables client certificate authentication at the enforcement level specified. Can be used in combination with Authenticator.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.sslCipherSuites</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The list of JSSE ciphers to support for SSL connections. If specified, only the ciphers that are listed and supported will be enabled. If not specified, the JVM default is used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.sslEnabledProtocols</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The list of SSL protocols to support for SSL connections. If specified, only the protocols that are listed and supported will be enabled. If not specified, the JVM default is used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.trustStore</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Required when needClientAuth is REQUIRE. Trusted certificates for verifying the remote endpoint’s certificate. If this value is not provided and SSL is enabled, the default <code>TrustManager</code> will be used, which will have a set of common public certificates installed to it.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ssl.trustStorePassword</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password of the <code>trustStore</code> if it is password-protected</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">strictTransactionManagement</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Set to <code>true</code> to require <code>aliases</code> to be submitted on every requests, where the <code>aliases</code> become the scope of transaction management.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">threadPoolBoss</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The number of threads available to Gremlin Server for accepting connections. Should always be set to <code>1</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">threadPoolWorker</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The number of threads available to Gremlin Server for processing non-blocking reads and writes.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">useCommonEngineForSessions</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Ensures that the same <code>ScriptEngine</code> is used to support sessions and sessionless requests which will lead to better performance. Do not change this setting from the default without a specific use case in mind. This configuration only applies to the <code>UnifiedChannelizer</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">useEpollEventLoop</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Try to use epoll event loops (works only on Linux os) instead of netty NIO.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">useGlobalFunctionCacheForSessions</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enable the global function cache for sessions when using the <code>UnifiedChannelizer</code>. When <code>true</code> it means that functions created in one request to a session remain available on the next request to that session. This setting is only relevant when <code>useGlobalFunctionCacheForSessions</code> is <code>false</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">writeBufferHighWaterMark</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">If the number of bytes in the network send buffer exceeds this value then the channel is no longer writeable, accepting no additional writes until buffer is drained and the <code>writeBufferLowWaterMark</code> is met.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">65536</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">writeBufferLowWaterMark</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Once the number of bytes queued in the network send buffer exceeds the <code>writeBufferHighWaterMark</code>, the channel will not become writeable again until the buffer is drained and it drops below this value.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">65536</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>See the <a href="#metrics">Metrics</a> section for more information on how to configure Ganglia and Graphite.</p> </div> <div class="sect3"> <h4 id="opprocessor-configurations">OpProcessor Configurations</h4> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>UnifiedChannelizer</code> does not rely on <code>OpProcessor</code> infrastructure. If using that channelizer, these configuration options can be ignored. </td> </tr> </table> </div> <div class="paragraph"> <p>An <code>OpProcessor</code> provides a way to plug-in handlers to Gremlin Server’s processing flow. Gremlin Server uses this plug-in system itself to expose the packaged functionality that it exposes. Configurations can be supplied to an <code>OpProcessor</code> through the <code>processors</code> key in the Gremlin Server configuration file. Each <code>OpProcessor</code> can take a <code>Map</code> of arguments which are specific to a particular implementation:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">processors</span>: - <span class="string"><span class="content">{ className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>The following sub-sections describe those configurations for each <code>OpProcessor</code> implementations supplied with Gremlin Server.</p> </div> <div class="sect4"> <h5 id="_sessionopprocessor">SessionOpProcessor</h5> <div class="paragraph"> <p>The <code>SessionOpProcessor</code> provides a way to interact with Gremlin Server over a <a href="#sessions">session</a>.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Name</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">globalFunctionCacheEnabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines if the script engine cache for global functions is enabled and behaves as an override to the plugin specific setting of the same name.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxParameters</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of parameters that can be passed on the request.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">16</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">perGraphCloseTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds to wait for each configured graph to close any open transactions when the session is killed.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">10000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">sessionTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Time in milliseconds before a session will time out.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">28800000</p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="_standardopprocessor">StandardOpProcessor</h5> <div class="paragraph"> <p>The <code>StandardOpProcessor</code> provides a way to interact with Gremlin Server without use of sessions and is the default method for processing script evaluation requests.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Name</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">maxParameters</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of parameters that can be passed on the request.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">16</p></td> </tr> </tbody> </table> </div> <div class="sect4"> <h5 id="traversalopprocessor">TraversalOpProcessor</h5> <div class="paragraph"> <p>The <code>TraversalOpProcessor</code> provides a way to accept traversals configured via <a href="#connecting-via-drivers">withRemote()</a>. It has no special configuration settings.</p> </div> </div> </div> <div class="sect3"> <h4 id="_serialization">Serialization</h4> <div class="paragraph"> <p>Gremlin Server can accept requests and return results using different serialization formats. Serializers implement the <code>MessageSerializer</code> interface. In doing so, they express the list of mime types they expect to support. When configuring multiple serializers it is possible for two or more serializers to support the same mime type. Such a situation may be common with a generic mime type such as <code>application/json</code>. Serializers are added in the order that they are encountered in the configuration file and the first one added for a specific mime type will not be overridden by other serializers that also support it.</p> </div> <div class="paragraph"> <p>The format of the serialization is configured by the <code>serializers</code> setting described in the table above. Note that some serializers have additional configuration options as defined by the <code>serializers[X].config</code> setting. The <code>config</code> setting is a <code>Map</code> where the keys and values get passed to the serializer at its initialization. The available and/or expected keys are dependent on the serializer being used. Gremlin Server comes packaged with two different serializers: GraphSON and GraphBinary.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Irrespective of the serialization format chosen, it is highly recommended that the serialization format is specified explicitly. For example, prefer <code>application/vnd.gremlin-v3.0+json</code> to <code>application/json</code>. Use of the drivers tend to take care of this issue internally, but for all other mechanisms it is best to ensure the <code>Accept</code> type is defined this way to avoid possible breaking changes or unexpected results, as defaults may vary from server to server. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> When connecting with drivers, never try to specify a serialization format that does not have embedded types. The drivers are designed to use that type information to properly produce results in the programming language’s type system and may not function correctly without it. Generally speaking, <code>GraphBinary</code> is always the best choice for the drivers. </td> </tr> </table> </div> <div class="sect4"> <h5 id="_graphson">GraphSON</h5> <div class="paragraph"> <p>The GraphSON serializer produces human-readable output in JSON format and is a good configuration choice for those trying to use TinkerPop from non-JVM languages. JSON obviously has wide support across virtually all major programming languages and can be consumed by a wide variety of tools. The format itself is described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/#graphson">IO Documentation</a>. The following table shows the available GraphSON serializers that can be configured:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 16.6666%;"> <col style="width: 33.3333%;"> <col style="width: 33.3335%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Version</th> <th class="tableblock halign-left valign-top">Embedded Types</th> <th class="tableblock halign-left valign-top">Mime Type</th> <th class="tableblock halign-left valign-top">Class</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">1.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v1.0+json</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONMessageSerializerGremlinV1</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">1.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v1.0+json;types=false</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONUntypedMessageSerializerV1</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">2.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v2.0+json</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONMessageSerializerV2</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">2.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v2.0+json;types=false</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONUntypedMessageSerializerV2</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">3.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">yes</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v3.0+json</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONMessageSerializerV3</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">3.0</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">no</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>application/vnd.gremlin-v3.0+json;types=false</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>GraphSONMessageSerializerV3</code></p></td> </tr> </tbody> </table> <div class="paragraph"> <p>The above serializer classes can be found in the <code>org.apache.tinkerpop.gremlin.util.ser</code> package of <code>gremlin-util</code>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Gremlin can produce results that cannot be serialized with untyped GraphSON as the result simply cannot fit the structure JSON inherently allows. A simple example would be <code>g.V().groupCount()</code> which returns a <code>Map</code>. A <code>Map</code> is no problem for JSON, but the key to this <code>Map</code> is a <code>Vertex</code>, which is a complex object, and cannot be a key in JSON which only allows <code>String</code> keys. Untyped GraphSON will simply convert the <code>Vertex</code> to a <code>String</code> for purpose of serialization and as a result that data and type is lost. If this information is needed, switch to a typed format or adjust the Gremlin query in some way to return it in a different form that fits JSON structure. </td> </tr> </table> </div> <div class="paragraph"> <p>Configuring GraphSON in the Gremlin Server configuration looks like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"> - <span class="string"><span class="content">{ className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 }</span></span></code></pre> </div> </div> <div class="paragraph"> <p>Gremlin Server is configured by default with GraphSON 3.0 as shown above. It has the following configuration option:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ioRegistries</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A list of <code>IoRegistry</code> implementations to be applied to the serializer.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> </tbody> </table> <div class="paragraph"> <p>It is worth noting that GraphSON 1.0 still has some appeal for some users as it can be configured to produce an untyped JSON format which is a bit easier to consume than its successors which embed data types into the output. This version of GraphSON tends to be the one that users like to utilize when <a href="#connecting-via-http">connecting via HTTP</a> and is still used by some <a href="#connecting-rgp">Remote Gremlin Providers</a> for this purpose.</p> </div> <div class="paragraph"> <p>To configure Gremlin Server this way, the <code>GraphSONMessageSerializerV1d0</code> must be included:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"> - <span class="string"><span class="content">{ className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1 }</span></span> - <span class="string"><span class="content">{ className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 }</span></span></code></pre> </div> </div> <div class="paragraph"> <p>In the above situation, both <code>GraphSONMessageSerializerV1d0</code> and <code>GraphSONMessageSerializerV3d0</code> each bind to the <code>application/json</code> mime type. When such conflicts arise, Gremlin Server will use the order of the serializers to determine priority such that the first serializer to bind to a type will be used and the others ignored. The following log message will indicate how the server is ultimately configured:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">[INFO] AbstractChannelizer - Configured application/json with org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1 [INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+json with org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 [INFO] AbstractChannelizer - application/json already has org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1 configured - it will not be replaced by org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, change order of serialization configuration if this is not desired.</code></pre> </div> </div> <div class="paragraph"> <p>Given the above, using GraphSON 3.0 under this configuration will require that the user specific the type:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ curl -X POST -d "{\"gremlin\":\"100-1\"}" "http://localhost:8182" {"requestId":"f8720ad9-2c8b-4eef-babe-21792a3e3157","status":{"message":"","code":200,"attributes":{}},"result":{"data":[99],"meta":{}}} $ curl -H "Accept:application/vnd.gremlin-v3.0+json" -X POST -d "{\"gremlin\":\"100-1\"}" "http://localhost:8182" {"requestId":"9fdf0892-d86c-41f2-94b5-092785c473eb","status":{"message":"","code":200,"attributes":{"@type":"g:Map","@value":[]}},"result":{"data":{"@type":"g:List","@value":[{"@type":"g:Int32","@value":99}]},"meta":{"@type":"g:Map","@value":[]}}</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="server-graphbinary">GraphBinary</h5> <div class="paragraph"> <p>GraphBinary is a binary serialization format suitable for object trees, designed to reduce serialization overhead on both the client and the server, as well as limiting the size of the payload that is transmitted over the wire. The format itself is described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/io/#graphbinary">IO Documentation</a>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"> - <span class="string"><span class="content">{ className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 }</span></span></code></pre> </div> </div> <div class="paragraph"> <p>It has the MIME type of <code>application/vnd.graphbinary-v1.0</code> and the following configuration options:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">custom</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A list of classes with custom kryo <code>Serializer</code> implementations related to them in the form of <code><class>;<serializer-class></code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ioRegistries</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A list of <code>IoRegistry</code> implementations to be applied to the serializer.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">builder</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Name of the <code>TypeSerializerRegistry.Builder</code> instance to be used to construct the <code>TypeSerializerRegistry</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> </tbody> </table> <div class="paragraph"> <p>As described above, there are multiple ways in which to register serializers for GraphBinary-based serialization. Note that the <code>ioRegistries</code> setting is applied first, followed by the <code>custom</code> setting.</p> </div> </div> </div> <div class="sect3"> <h4 id="metrics">Metrics</h4> <div class="paragraph"> <p>Gremlin Server produces metrics about its operations that can yield some insight into how it is performing. These metrics are exposed in a variety of ways:</p> </div> <div class="ulist"> <ul> <li> <p>Directly to the console where Gremlin Server is running</p> </li> <li> <p>CSV file</p> </li> <li> <p><a href="http://ganglia.info/">Ganglia</a></p> </li> <li> <p><a href="http://graphite.wikidot.com/">Graphite</a></p> </li> <li> <p><a href="http://www.slf4j.org/">SLF4j</a></p> </li> <li> <p><a href="https://en.wikipedia.org/wiki/Java_Management_Extensions">JMX</a></p> </li> </ul> </div> <div class="paragraph"> <p>The configuration of each of these outputs is described in the Gremlin Server <a href="#_configuring_2">Configuring</a> section. Note that Graphite and Ganglia are not included as part of the Gremlin Server distribution and must be installed to the server manually.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/gremlin-server.sh install com.codahale.metrics metrics-ganglia 3.0.2 bin/gremlin-server.sh install com.codahale.metrics metrics-graphite 3.0.2</code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Gremlin Server is built to work with Metrics 3.0.2. Usage of other versions may lead to unexpected problems. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Installing Ganglia will include <code>org.acplt:oncrpc</code>, which is an LGPL licensed dependency. </td> </tr> </table> </div> <div class="paragraph"> <p>Regardless of the output, the metrics gathered are the same. Each metric is prefixed with <code>org.apache.tinkerpop.gremlin.server.GremlinServer</code> and the following metrics are reported:</p> </div> <div class="ulist"> <ul> <li> <p><code>sessions</code> - The number of sessions open at the time the metric was last measured. For the <code>UnifiedChannelizer</code>, each request creates a "session", even a so-called "sessionless request", which is basically a session that will only execute within the context of that single request.</p> </li> <li> <p><code>errors</code> - The number of total errors, mean rate, as well as the 1, 5, and 15-minute error rates.</p> </li> <li> <p><code>op.eval</code> - The number of script evaluations, mean rate, 1, 5, and 15 minute rates, minimum, maximum, median, mean, and standard deviation evaluation times, as well as the 75th, 95th, 98th, 99th and 99.9th percentile evaluation times (note that these time apply to both sessionless and in-session requests).</p> </li> <li> <p><code>op.traversal</code> - The number of <code>Traversal</code> bytecode-based executions, mean rate, 1, 5, and 15 minute rates, minimum, maximum, median, mean, and standard deviation evaluation times, as well as the 75th, 95th, 98th, 99th and 99.9th percentile evaluation times.</p> </li> <li> <p><code>engine-name.session.session-id.*</code> - Metrics related to different <code>GremlinScriptEngine</code> instances configured for session-based requests where "engine-name" will be the actual name of the engine, such as "gremlin-groovy" and "session-id" will be the identifier for the session itself. This metric is not measured under the <code>UnifiedChannelizer</code>.</p> </li> <li> <p><code>engine-name.sessionless.*</code> - Metrics related to different <code>GremlinScriptEngine</code> instances configured for sessionless requests where "engine-name" will be the actual name of the engine, such as "gremlin-groovy". This metric is not measured under the <code>UnifiedChannelizer</code>.</p> </li> <li> <p><code>user-agent.*</code> - Counts the number of connection requests from clients providing a given user agent.</p> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Gremlin Server has a limit of 10000 unique user agents to be tracked by metrics. If this cap is exceeded any additional unique user agents will be counted as <code>user-agent.other</code>. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_as_a_service">As A Service</h4> <div class="paragraph"> <p>Gremlin server can be configured to run as a service.</p> </div> <div class="sect4"> <h5 id="_init_d_sysv">Init.d (SysV)</h5> <div class="paragraph"> <p>Link <code>bin/gremlin-server.sh</code> to <code>init.d</code> Be sure to set RUNAS to the service user in <code>bin/gremlin-server.conf</code></p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash"># Install ln -s /path/to/apache-tinkerpop-gremlin-server-3.7.3/bin/gremlin-server.sh /etc/init.d/gremlin-server # Systems with chkconfig/service. E.g. Fedora, Red Hat chkconfig --add gremlin-server # Start service gremlin-server start # Or call directly /etc/init.d/gremlin-server restart</code></pre> </div> </div> </div> <div class="sect4"> <h5 id="_systemd">Systemd</h5> <div class="paragraph"> <p>To install, copy the service template below to /etc/systemd/system/gremlin.service and update the paths <code>/path/to/apache-tinkerpop-gremlin-server</code> with the actual install path of Gremlin Server.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">[Unit] Description=Apache TinkerPop Gremlin Server daemon Documentation=https://tinkerpop.apache.org/ After=network.target [Service] Type=forking ExecStart=/path/to/apache-tinkerpop-gremlin-server/bin/gremlin-server.sh start ExecStop=/path/to/apache-tinkerpop-gremlin-server/bin/gremlin-server.sh stop PIDFile=/path/to/apache-tinkerpop-gremlin-server/run/gremlin.pid [Install] WantedBy=multi-user.target</code></pre> </div> </div> <div class="paragraph"> <p>Enable the service with <code>systemctl enable gremlin-server</code></p> </div> <div class="paragraph"> <p>Start the service with <code>systemctl start gremlin-server</code></p> </div> </div> </div> </div> <div class="sect2"> <h3 id="security">Security</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-server-secure.png" alt="gremlin server secure" width="175"></span> Gremlin Server provides for several features that aid in the security of the graphs that it exposes. In particular it supports SSL for transport layer security, authentication, authorization and protective measures against malicious script execution. Client SSL options are described in the <a href="#gremlin-drivers-variants">Gremlin Drivers and Variants"</a> sections with varying capability depending on the driver chosen. Script execution options are covered <a href="#script-execution">"at the end of this section"</a>. This section starts with authentication.</p> </div> <div class="paragraph"> <p>Gremlin Server supports a pluggable authentication framework using <a href="https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer">SASL</a> (Simple Authentication and Security Layer). Depending on the client used to connect to Gremlin Server, different authentication mechanisms are accessible, see the table below.</p> </div> <table class="tableblock frame-all grid-all" style="width: 70%;"> <colgroup> <col style="width: 27.2727%;"> <col style="width: 45.4545%;"> <col style="width: 27.2728%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Client</th> <th class="tableblock halign-left valign-top">Authentication mechanism</th> <th class="tableblock halign-left valign-top">Availability</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">HTTP</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">BASIC</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.0.0-incubating</p></td> </tr> <tr> <td class="tableblock halign-left valign-top" rowspan="3"><p class="tableblock">Gremlin-Java/ Gremlin-Console</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL (username/password)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.0.0-incubating</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Pluggable SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.0.0-incubating</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">GSSAPI SASL (Kerberos)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.3.0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Gremlin.NET</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.3.0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">Gremlin-Python</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.2.2</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">GSSAPI SASL (Kerberos)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.4.7</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Gremlin.Net</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.2.7</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Gremlin-Javascript</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.3.0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Gremlin-go</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PLAIN SASL</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">3.5.4</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>By default, Gremlin Server is configured to allow all requests to be processed (i.e. no authentication). To enable authentication, Gremlin Server must be configured with an <code>Authenticator</code> implementation in its YAML file. Gremlin Server comes packaged with two implementations called <code>SimpleAuthenticator</code> for plain text authentication using HTTP BASIC or PLAIN SASL and <code>Krb5Authenticator</code> for Kerberos authentication using GSSAPI SASL.</p> </div> <div class="sect3"> <h4 id="_plain_text_authentication">Plain text authentication</h4> <div class="paragraph"> <p>The <code>SimpleAuthenticator</code> implements the "PLAIN" SASL mechanism (i.e. plain text) to authenticate a request. It also supports handling basic authentication requests from http clients. It validates username/password pairs against a graph database, which must be provided to it as part of the configuration.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">authentication</span>: <span class="string"><span class="content">{</span><span class="content"> authenticator: org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator, config: { credentialsDb: conf/tinkergraph-credentials.properties}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>A quick way to get started with the <code>SimpleAuthenticator</code> is to use TinkerGraph for the "credentials graph" and the "sample" credential graph that is packaged with the server. To secure the transport for the credentials, SSL should be enabled. For this Quick Start, a self-signed certificate will be created but this should not be used in a production environment.</p> </div> <div class="paragraph"> <p>Generate the self-signed SSL certificate:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ keytool -genkey -alias localhost -keyalg RSA -keystore server.jks Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: localhost What is the name of your organizational unit? [Unknown]: What is the name of your organization? [Unknown]: What is the name of your City or Locality? [Unknown]: What is the name of your State or Province? [Unknown]: What is the two-letter country code for this unit? [Unknown]: Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? [no]: yes Enter key password for <localhost> (RETURN if same as keystore password):</code></pre> </div> </div> <div class="paragraph"> <p>Next, uncomment the <code>keyStore</code> and <code>keyStorePassword</code> lines in <code>conf/gremlin-server-secure.yaml</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">ssl</span>: <span class="string"><span class="content">{</span><span class="content"> enabled: true, sslEnabledProtocols: [TLSv1.2], keyStore: server.jks, keyStorePassword: changeit</span></span> }</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin-server.sh conf/gremlin-server-secure.yaml [INFO] GremlinServer - \,,,/ (o o) -----oOOo-(3)-oOOo----- [INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-secure.yaml ... [INFO] AbstractChannelizer - SSL enabled [INFO] SimpleAuthenticator - Initializing authentication with the org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator [INFO] SimpleAuthenticator - CredentialGraph initialized at CredentialGraph{graph=tinkergraph[vertices:1 edges:0]} [INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 8 and boss thread pool of 1. [INFO] GremlinServer$1 - Channel started at port 8182.</code></pre> </div> </div> <div class="paragraph"> <p>When SSL is enabled on the server, it must also be enabled on the client when connecting. To connect to Gremlin Server with the <a href="#gremlin-java"><code>gremlin-driver</code></a>, set the <code>credentials</code>, <code>enableSsl</code>, and <code>trustStore</code> when constructing the <code>Cluster</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.build().credentials(<span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">password</span><span class="delimiter">"</span></span>) .enableSsl(<span class="predefined-constant">true</span>).trustStore(<span class="string"><span class="delimiter">"</span><span class="content">server.jks</span><span class="delimiter">"</span></span>).create();</code></pre> </div> </div> <div class="paragraph"> <p>If connecting with Gremlin Console, which utilizes <code>gremlin-driver</code> for remote script execution, use the provided <code>conf/remote-secure.yaml</code> file when defining the remote. That file contains configuration for the username and password as well as enablement of SSL from the client side. Be sure to configure the trustStore if using self-signed certificates.</p> </div> <div class="paragraph"> <p>Similarly, Gremlin Server can be configured for REST and security. Follow the steps above for configuring the SSL certificate.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin-server.sh conf/gremlin-server-rest-secure.yaml [INFO] GremlinServer - \,,,/ (o o) -----oOOo-(3)-oOOo----- [INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-secure.yaml ... [INFO] AbstractChannelizer - SSL enabled [INFO] SimpleAuthenticator - Initializing authentication with the org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator [INFO] SimpleAuthenticator - CredentialGraph initialized at CredentialGraph{graph=tinkergraph[vertices:1 edges:0]} [INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 8 and boss thread pool of 1. [INFO] GremlinServer$1 - Channel started at port 8182.</code></pre> </div> </div> <div class="paragraph"> <p>Once the server has started, issue a request passing the credentials with an <code>Authentication</code> header, as described in <a href="http://tools.ietf.org/html/rfc2617#section-2">RFC2617</a>. Here’s a HTTP Basic authentication example with cURL:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">curl -X POST --insecure -u stephen:password -d "{\"gremlin\":\"100-1\"}" "https://localhost:8182"</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="credentials-dsl">Credentials Graph DSL</h4> <div class="paragraph"> <p>The "credentials graph", which has been mentioned in previous sections, is used by Gremlin Server to hold the list of users who can authenticate to the server. It is possible to use virtually any <code>Graph</code> instance for this task as long as it complies to a defined schema. The credentials graph stores users as vertices with the <code>label</code> of "user". Each "user" vertex has two properties: <code>username</code> and <code>password</code>. Naturally, these are both <code>String</code> values. The password must not be stored in plain text and should be hashed.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Be sure to define an index on the <code>username</code> property, as this will be used for lookups. If supported by the <code>Graph</code>, consider specifying a unique constraint as well. </td> </tr> </table> </div> <div class="paragraph"> <p>To aid with the management of a credentials graph, Gremlin Server provides a Gremlin Console plugin which can be used to add and remove users so as to ensure that the schema is adhered to, thus ensuring compatibility with Gremlin Server. In addition, as it is a plugin, it works naturally in the Gremlin Console as an extension of its capabilities (though one could use it programmatically, if desired). This plugin is distributed with the Gremlin Console so it does not have to be "installed". It does however need to be activated:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :plugin use tinkerpop.credentials ==>tinkerpop.credentials activated</code></pre> </div> </div> <div class="paragraph"> <p>Please see the example usage as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-25" type="radio" name="radio-set-1729797129-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-26" type="radio" name="radio-set-1729797129-25" class="tab-selector-2" /> <label for="tab-1729797129-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> graph.createIndex(<span class="string"><span class="delimiter">"</span><span class="content">username</span><span class="delimiter">"</span></span>,Vertex.class) ==><span class="predefined-constant">null</span> gremlin> credentials = traversal(CredentialTraversalSource.class).withEmbedded(graph) ==>credentialtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">password</span><span class="delimiter">"</span></span>) ==>v[<span class="integer">0</span>] gremlin> credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">daniel</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">better-password</span><span class="delimiter">"</span></span>) ==>v[<span class="integer">3</span>] gremlin> credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">rainbow-dash</span><span class="delimiter">"</span></span>) ==>v[<span class="integer">6</span>] gremlin> credentials.users(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>).elementMap() ==>[<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:user,<span class="key">password</span>:<span class="error">$</span><span class="integer">2</span>a<span class="error">$</span><span class="octal">04</span><span class="error">$</span>T2jQNrTHT6AF/EB2w9VzCe1WF34BXo2lADA0En47m9r4LD8JCLm..,<span class="key">username</span>:marko] gremlin> credentials.users().count() ==><span class="integer">3</span> gremlin> credentials.users(<span class="string"><span class="delimiter">"</span><span class="content">daniel</span><span class="delimiter">"</span></span>).drop() gremlin> credentials.users().count() ==><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">graph = TinkerGraph.open() graph.createIndex(<span class="string"><span class="delimiter">"</span><span class="content">username</span><span class="delimiter">"</span></span>,Vertex.class) credentials = traversal(CredentialTraversalSource.class).withEmbedded(graph) credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">password</span><span class="delimiter">"</span></span>) credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">daniel</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">better-password</span><span class="delimiter">"</span></span>) credentials.user(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">rainbow-dash</span><span class="delimiter">"</span></span>) credentials.users(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>).elementMap() credentials.users().count() credentials.users(<span class="string"><span class="delimiter">"</span><span class="content">daniel</span><span class="delimiter">"</span></span>).drop() credentials.users().count()</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 Credentials DSL is built using TinkerPop’s DSL Annotation Processor described <a href="#gremlin-java-dsl">here</a>. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> In the above example, an empty in-memory TinkerGraph was used for demonstrating the API of the DSL. Obviously, this data will not be retained and usable with Gremlin Server. It would be important to configure TinkerGraph to persist that data or to manually persist it (e.g. write the graph data to Gryo) once changes are complete. Alternatively, use a persistent graph to hold the credentials and configure Gremlin Server accordingly. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="krb5authenticator">Kerberos Authentication</h4> <div class="paragraph"> <p>The <code>Krb5Authenticator</code> implements the "GSSAPI" SASL mechanism (i.e. Kerberos) to authenticate a request from a Gremlin client. It can be applied in an existing Kerberos environment and validates whether a <a href="https://www.roguelynn.com/words/explain-like-im-5-kerberos/">valid authentication proof and service ticket are offered</a>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">authentication</span>: <span class="string"><span class="content">{</span><span class="content"> authenticator: org.apache.tinkerpop.gremlin.server.auth.Krb5Authenticator, config: { principal: gremlinserver/hostname.your.org@YOUR.REALM, keytab: /etc/security/keytabs/gremlinserver.service.keytab}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p><code>Krb5Authenticator</code> needs a Kerberos service principal and a keytab that holds the secret key for that principal. The keytab location and service name, e.g. gremlinserver, are free to be chosen. <code>Krb5Authenticator</code> finds the KDC’s hostname and port from the krb5.conf file with Kerberos configurations. This file can reside at either the <a href="https://web.mit.edu/kerberos/krb5-devel/doc/mitK5defaults.html">default location</a> or a location to be specified as a system property in the JAVA_OPTIONS environment variable of Gremlin Server:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">export JAVA_OPTIONS="${JAVA_OPTIONS} -Xms512m -Xmx4096m -Djava.security.krb5.conf=/etc/krb5.conf"</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin clients have to specify the service name as the <code>protocol</code> connection parameter. For Gremlin-Console the <code>protocol</code> is an entry in the remote.yaml file, for Gremlin-java the client builder has a <code>protocol()</code> method.</p> </div> <div class="paragraph"> <p>In addition to the <code>protocol</code>, the Gremlin client needs to specify a <code>jaasEntry</code>, an entry in the <a href="https://en.wikipedia.org/wiki/Java_Authentication_and_Authorization_Service">JAAS</a> configuration file. As a start one can define a conf/gremlin-jaas.conf file with a <code>GremlinConsole</code> jaasEntry:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="jaas">GremlinConsole { com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=true useTicketCache=true; };</code></pre> </div> </div> <div class="paragraph"> <p>This configuration tells Gremlin Console to pass authentication requests from Gremlin Server to the Krb5LoginModule, which is part of the java standard library. The Krb5LoginModule does not prompt the user for a username and password but uses the ticket cache that is normally refreshed when a user logs in to a host within the Kerberos realm.</p> </div> <div class="paragraph"> <p>The Gremlin client needs the location of the JAAS configuration file to be passed as a system property to the JVM. For Gremlin-Console the easiest way to do this is to pass it to the run script via the JAVA_OPTIONS environment property. If the krb5.conf Kerberos configuration file is not available from the <a href="https://web.mit.edu/kerberos/krb5-devel/doc/mitK5defaults.html">default location</a> it has to be provided as a system property as well:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">JAAS_OPTION="-Djava.security.auth.login.config=conf/gremlin-jaas.conf" KRB5_OPTION="-Djava.security.krb5.conf=/etc/krb5.conf" export JAVA_OPTIONS="${JAVA_OPTIONS} ${KRB5_OPTION} ${JAAS_OPTION}"</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="authorization">Authorization</h4> <div class="paragraph"> <p>While authentication determines which clients can connect to Gremlin Server, authorization regulates which elements of the exposed graphs a specific user is allowed to create, read, update or delete (CRUD). Authorization in Gremlin Server can take place at two instances. Before execution a user request can be allowed or denied based on the presence of operations such as:</p> </div> <div class="ulist"> <ul> <li> <p>reading from a GraphTraversalSource</p> </li> <li> <p>writing to a GraphTraversalSource</p> </li> <li> <p>presence of lambdas in bytecode</p> </li> <li> <p>script execution</p> </li> <li> <p><code>VertexProgram</code> execution (OLAP)</p> </li> <li> <p>removal or modification of <code>TraversalStrategy</code> instances</p> </li> </ul> </div> <div class="paragraph"> <p>During execution the applied traversal strategies influence the results and side-effects of a given query.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Authorization is a feature of Gremlin Server, but is not implemented as an element of the server protocol and therefore Remote Graph Providers may not have this feature or may not implement it in this particular way. Please consult the documentation of the graph you are using to determine what authorization features it supports. </td> </tr> </table> </div> <div class="sect4"> <h5 id="_mechanisms">Mechanisms</h5> <div class="paragraph"> <p>Gremlin Server supports three mechanisms to configure authorization:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>With the <code>ScriptFileGremlinPlugin</code> a groovy script is configured that instantiates the <code>GraphTraversalSources</code> that can be accessed by client requests. Using the <code>withStrategies()</code> gremlin <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#start-steps">start step</a>, one can apply so-called <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#traversalstrategy">TraversalStrategy instances</a> to these <code>GraphTraversalSource</code> instances, some of which can serve for authorization purposes (<code>ReadOnlyStrategy</code>, <code>LambdaRestrictionStrategy</code>, <code>VertexProgramRestrictionStrategy</code>, <code>SubgraphStrategy</code>, <code>PartitionStrategy</code>, <code>EdgeLabelVerificationStrategy</code>), provided that users are not allowed to remove or modify these <code>TraversalStrategy</code> instances afterwards. The <code>ScriptFileGremlinPlugin</code> is found in the yaml configuration file for Gremlin Server:</p> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">scriptEngines</span>: <span class="string"><span class="content">{</span><span class="content"> gremlin-groovy: { plugins: { org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}}</span></span></code></pre> </div> </div> </li> <li> <p>Administrators can configure an authorizer class, an implementation of the <code>Authorizer</code> interface. An authorizer receives a request before it is executed and it can decide to pass or deny the request, based on the information it has available on the requesting user or can seek externally.</p> </li> <li> <p>Apart from passing or denying requests, an <code>Authorizer</code> implementation can actively modify the request, in particular add the <code>TraversalStrategy</code> instances mentioned in item 1.</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> This section is written with gremlin bytecode requests in mind. Realizing authorization for script requests is hardly feasible, because such requests get full access to Gremlin Server’s execution environment. Although the section <a href="#script-execution">Protecting Script Execution</a> explains how the client access to this environment can be restricted, it is not possible to deny execution of <code>GraphFactory.open()</code> or <code>GraphTraversalSource.getGraph()</code> methods without resorting to TinkerPop implementation details (that is, internal API’s that can change without notice). </td> </tr> </table> </div> <div class="paragraph"> <p>The three mechanisms for authorization each have their merits in terms of simplicity and flexibility. The table below gives an overview.</p> </div> <table class="tableblock frame-all grid-all" style="width: 95%;"> <colgroup> <col style="width: 38.4615%;"> <col style="width: 15.3846%;"> <col style="width: 15.3846%;"> <col style="width: 30.7693%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Type (mechanism)</th> <th class="tableblock halign-left valign-top">GraphTraversalSources</th> <th class="tableblock halign-left valign-top">Groups</th> <th class="tableblock halign-left valign-top">Bytecode analysis</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Implicit (init script)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">all accessible</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">one</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>withStrategies()</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Passive (pass/deny)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">selected access</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">few</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">hybrid</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Active (inject)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">selected access</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">many</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">hybrid</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>With implicit authorization (only adding restricting <code>TraversalStrategy</code> instances in the initialization script of Gremlin Server) all authenticated users can access all hosted <code>GraphTraversalSources</code> and all face the same restrictions. One would need separate Gremlin Server instances for each authorization policy and apply an authenticator that restricts access to a group of users (that is, supports in authorization).</p> </div> <div class="paragraph"> <p>The other extreme is the active authorization solution that injects the restricting <code>Strategies</code> into the user request, following a policy that takes into account both the authenticated user and the original request. While this solution is the most flexible and can support an almost unlimited number of authorization policies, it is somewhat complex to implement. In particular, applying the <code>SubgraphStrategy</code> requires knowledge about the schema of the graph.</p> </div> <div class="paragraph"> <p>The passive authorization solution perhaps provides a middle ground to start implementing authorization. This solution assumes that the <code>SubgraphStrategy</code> is applied in the Gremlin Server initialization script, because compliance with a subgraph restriction can only be determined during the actual execution of the gremlin traversal. Note that the same graph can be reused with different <code>SubgraphStrategies</code>. Now, authorization policies can be defined in terms of accessible <code>GraphTraversalSources</code> and the authorizer can simply match the requested access to a <code>GraphTraversalSource</code> against the policies applicable to the authenticated user. Like for the active authorization solution, other restrictions such as read only access can be either applied at authorization time as policy in the authorizer itself or at request execution time as a result of an applied <code>Strategy</code> (denoted as 'hybrid' bytecode analysis in the table). A code example pursuing the former option is provided in the <a href="#authz-code-example">next section</a>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Both the passive and active authorization solutions need to analyze the gremlin bytecode of the original request for unwanted removal of restricting Strategies. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Gremlin Server is not shipped with <code>Authorizer</code> implementations, because these would heavily depend on the external systems to integrate with, e.g. <a href="https://ldap.com/directory-servers/">LDAP systems</a> or <a href="https://ranger.apache.org/">Apache Ranger </a>. However, third-party implementations can be offered as <a href="#gremlin-plugins">gremlin plugins</a>. </td> </tr> </table> </div> </div> <div class="sect4"> <h5 id="authz-code-example">Code example</h5> <div class="paragraph"> <p>The two java classes below provide an example implementation of the <code>Authorizer</code> interface; they originate from <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/authz">Gremlin Server’s test package</a>. If you copy the files into a project, build them into a jar and add the jar to Gremlin Server’s CLASSPATH, you can use them by adding the following to Gremlin Server’s yaml configuration file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">authentication</span>: <span class="string"><span class="content">{</span><span class="content"> authenticator: org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator, config: { credentialsDb: conf/tinkergraph-credentials.properties}}</span></span> <span class="key">authorization</span>: <span class="string"><span class="content">{</span><span class="content"> authorizer: org.yourpackage.AllowListAuthorizer, config: { authorizationAllowList: your/path/allow-list.yaml}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>The <code>AllowListAuthorizer</code> supports granting groups of users access to statically configured <code>GraphTraversalSource</code> instances and to the "sandbox", where sandbox means that the group is allowed anything unless restricted by Gremlin Server’s <a href="#script-execution">sandbox</a>. For denying mutating steps and OLAP operations in bytecode requests, the <code>AllowListAuthorizer</code> relies on the <code>ReadOnlyStrategy</code> and <code>VertexProgramRestrictionStrategy</code> being present in the <code>GraphTraversalSource</code>. However, it always denies the use of lambdas in bytecode requests unless the user has the "sandbox" grant. It uses the <code>BytecodeHelper.getLambdaLanguage()</code> method to detect these.</p> </div> <div class="paragraph"> <p>The grants to groups of users can be configured in a simple yaml file. In addition to the special value "sandbox" for a grant for string based requests and lambdas, the special value "anonymous" can be used to denote any user.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">org.yourpackage</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.util.message.RequestMessage</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verification.VertexProgramRestrictionStrategy</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.Bytecode</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.TraversalSource</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.server.Settings.AuthorizationSettings</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser</span>; <span class="keyword">import</span> <span class="include">java.util</span>.*; <span class="comment">/** * Authorizes a user per request, based on a list that grants access to {@link TraversalSource} instances for * bytecode requests and to gremlin server's sandbox for string requests and lambdas. The {@link * AuthorizationSettings}.config must have an authorizationAllowList entry that contains the name of a YAML file. * This authorizer is for demonstration purposes only. It does not scale well in the number of users regarding * memory usage and administrative burden. */</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">AllowListAuthorizer</span> <span class="directive">implements</span> Authorizer { <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> SANDBOX = <span class="string"><span class="delimiter">"</span><span class="content">sandbox</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_BYTECODE = <span class="string"><span class="delimiter">"</span><span class="content">User not authorized for bytecode requests on %s</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_LAMBDA = <span class="string"><span class="delimiter">"</span><span class="content">lambdas</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_MUTATE = <span class="string"><span class="delimiter">"</span><span class="content">the ReadOnlyStrategy</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_OLAP = <span class="string"><span class="delimiter">"</span><span class="content">the VertexProgramRestrictionStrategy</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_SUBGRAPH = <span class="string"><span class="delimiter">"</span><span class="content">the SubgraphStrategy</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> REJECT_STRING = <span class="string"><span class="delimiter">"</span><span class="content">User not authorized for string-based requests.</span><span class="delimiter">"</span></span>; <span class="directive">public</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> KEY_AUTHORIZATION_ALLOWLIST = <span class="string"><span class="delimiter">"</span><span class="content">authorizationAllowList</span><span class="delimiter">"</span></span>; <span class="comment">// Collections derived from the list with allowed users for fast lookups</span> <span class="directive">private</span> <span class="directive">final</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">List</span><<span class="predefined-type">String</span>>> usernamesByTraversalSource = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); <span class="directive">private</span> <span class="directive">final</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>> usernamesSandbox = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><>(); <span class="comment">/** * This method is called once upon system startup to initialize the {@code AllowListAuthorizer}. */</span> <span class="annotation">@Override</span> <span class="directive">public</span> <span class="type">void</span> setup(<span class="directive">final</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> config) { AllowList allowList; <span class="directive">final</span> <span class="predefined-type">String</span> file = (<span class="predefined-type">String</span>) config.get(KEY_AUTHORIZATION_ALLOWLIST); <span class="keyword">try</span> { allowList = AllowList.read(file); } <span class="keyword">catch</span> (<span class="exception">Exception</span> e) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">IllegalArgumentException</span>(<span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">"</span><span class="content">Failed to read list with allowed users from %s</span><span class="delimiter">"</span></span>, file)); } <span class="keyword">for</span> (<span class="predefined-type">Map</span>.Entry<<span class="predefined-type">String</span>, <span class="predefined-type">List</span><<span class="predefined-type">String</span>>> entry : allowList.grants.entrySet()) { <span class="keyword">if</span> (!entry.getKey().equals(SANDBOX)) { usernamesByTraversalSource.put(entry.getKey(), <span class="keyword">new</span> <span class="predefined-type">ArrayList</span><>()); } <span class="keyword">for</span> (<span class="directive">final</span> <span class="predefined-type">String</span> group : entry.getValue()) { <span class="keyword">if</span> (allowList.groups.get(group) == <span class="predefined-constant">null</span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">RuntimeException</span>(<span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">"</span><span class="content">Group '%s' not defined in file with allowed users.</span><span class="delimiter">"</span></span>, group)); } <span class="keyword">if</span> (entry.getKey().equals(SANDBOX)) { usernamesSandbox.addAll(allowList.groups.get(group)); } <span class="keyword">else</span> { usernamesByTraversalSource.get(entry.getKey()).addAll(allowList.groups.get(group)); } } } } <span class="comment">/** * Checks whether a user is authorized to have a gremlin bytecode request from a client answered and raises an * {@link AuthorizationException} if this is not the case. For a request to be authorized, the user must either * have a grant for the requested {@link TraversalSource}, without using lambdas, mutating steps or OLAP, or have a * sandbox grant. * * @param user {@link AuthenticatedUser} that needs authorization. * @param bytecode The gremlin {@link Bytecode} request to authorize the user for. * @param aliases A {@link Map} with a single key/value pair that maps the name of the {@link TraversalSource} in the * {@link Bytecode} request to name of one configured in Gremlin Server. * @return The original or modified {@link Bytecode} to be used for further processing. */</span> <span class="annotation">@Override</span> <span class="directive">public</span> Bytecode authorize(<span class="directive">final</span> AuthenticatedUser user, <span class="directive">final</span> Bytecode bytecode, <span class="directive">final</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">String</span>> aliases) <span class="directive">throws</span> AuthorizationException { <span class="directive">final</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>> usernames = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><>(); <span class="keyword">for</span> (<span class="directive">final</span> <span class="predefined-type">String</span> resource: aliases.values()) { usernames.addAll(usernamesByTraversalSource.get(resource)); } <span class="directive">final</span> <span class="type">boolean</span> userHasTraversalSourceGrant = usernames.contains(user.getName()) || usernames.contains(AuthenticatedUser.ANONYMOUS_USERNAME); <span class="directive">final</span> <span class="type">boolean</span> userHasSandboxGrant = usernamesSandbox.contains(user.getName()) || usernamesSandbox.contains(AuthenticatedUser.ANONYMOUS_USERNAME); <span class="directive">final</span> <span class="type">boolean</span> runsLambda = BytecodeHelper.getLambdaLanguage(bytecode).isPresent(); <span class="directive">final</span> <span class="type">boolean</span> touchesReadOnlyStrategy = bytecode.toString().contains(ReadOnlyStrategy.class.getSimpleName()); <span class="directive">final</span> <span class="type">boolean</span> touchesOLAPRestriction = bytecode.toString().contains(VertexProgramRestrictionStrategy.class.getSimpleName()); <span class="comment">// This element becomes obsolete after resolving TINKERPOP-2473 for allowing only a single instance of each traversal strategy.</span> <span class="directive">final</span> <span class="type">boolean</span> touchesSubgraphStrategy = bytecode.toString().contains(SubgraphStrategy.class.getSimpleName()); <span class="directive">final</span> <span class="predefined-type">List</span><<span class="predefined-type">String</span>> rejections = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span><>(); <span class="keyword">if</span> (runsLambda) { rejections.add(REJECT_LAMBDA); } <span class="keyword">if</span> (touchesReadOnlyStrategy) { rejections.add(REJECT_MUTATE); } <span class="keyword">if</span> (touchesOLAPRestriction) { rejections.add(REJECT_OLAP); } <span class="keyword">if</span> (touchesSubgraphStrategy) { rejections.add(REJECT_SUBGRAPH); } <span class="predefined-type">String</span> rejectMessage = REJECT_BYTECODE; <span class="keyword">if</span> (rejections.size() > <span class="integer">0</span>) { rejectMessage += <span class="string"><span class="delimiter">"</span><span class="content"> using </span><span class="delimiter">"</span></span> + <span class="predefined-type">String</span>.join(<span class="string"><span class="delimiter">"</span><span class="content">, </span><span class="delimiter">"</span></span>, rejections); } rejectMessage += <span class="string"><span class="delimiter">"</span><span class="content">.</span><span class="delimiter">"</span></span>; <span class="keyword">if</span> ( (!userHasTraversalSourceGrant || runsLambda || touchesOLAPRestriction || touchesReadOnlyStrategy || touchesSubgraphStrategy) && !userHasSandboxGrant) { <span class="keyword">throw</span> <span class="keyword">new</span> AuthorizationException(<span class="predefined-type">String</span>.format(rejectMessage, aliases.values())); } <span class="keyword">return</span> bytecode; } <span class="comment">/** * Checks whether a user is authorized to have a script request from a gremlin client answered and raises an * {@link AuthorizationException} if this is not the case. * * @param user {@link AuthenticatedUser} that needs authorization. * @param msg {@link RequestMessage} in which the {@link org.apache.tinkerpop.gremlin.util.Tokens}.ARGS_GREMLIN argument can contain an arbitrary succession of script statements. */</span> <span class="directive">public</span> <span class="type">void</span> authorize(<span class="directive">final</span> AuthenticatedUser user, <span class="directive">final</span> RequestMessage msg) <span class="directive">throws</span> AuthorizationException { <span class="keyword">if</span> (!usernamesSandbox.contains(user.getName())) { <span class="keyword">throw</span> <span class="keyword">new</span> AuthorizationException(REJECT_STRING); } } }</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">org.yourpackage</span>; <span class="keyword">import</span> <span class="include">org.yaml.snakeyaml.TypeDescription</span>; <span class="keyword">import</span> <span class="include">org.yaml.snakeyaml.Yaml</span>; <span class="keyword">import</span> <span class="include">org.yaml.snakeyaml.constructor.Constructor</span>; <span class="keyword">import</span> <span class="include">java.io.File</span>; <span class="keyword">import</span> <span class="include">java.io.FileInputStream</span>; <span class="keyword">import</span> <span class="include">java.io.InputStream</span>; <span class="keyword">import</span> <span class="include">java.util.List</span>; <span class="keyword">import</span> <span class="include">java.util.Map</span>; <span class="keyword">import</span> <span class="include">java.util.Optional</span>; <span class="comment">/** * AllowList for the AllowListAuthorizer as configured by a YAML file. */</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">AllowList</span> { <span class="comment">/** * Holds lists of groups by grant. A grant is either a TraversalSource name or the "sandbox" value. With the * sandbox grant users can access all TraversalSource instances and execute groovy scripts as string based * requests or as lambda functions, only limited by Gremlin Server's sandbox definition. */</span> <span class="directive">public</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">List</span><<span class="predefined-type">String</span>>> grants; <span class="comment">/** * Holds lists of user names by groupname. The "anonymous" user name can be used to denote any user. */</span> <span class="directive">public</span> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">List</span><<span class="predefined-type">String</span>>> groups; <span class="comment">/** * Read a configuration from a YAML file into an {@link AllowList} object. * * @param file the location of a AllowList YAML configuration file * @return An {@link Optional} object wrapping the created {@link AllowList} */</span> <span class="directive">public</span> <span class="directive">static</span> AllowList read(<span class="directive">final</span> <span class="predefined-type">String</span> file) <span class="directive">throws</span> <span class="exception">Exception</span> { <span class="directive">final</span> <span class="predefined-type">InputStream</span> stream = <span class="keyword">new</span> <span class="predefined-type">FileInputStream</span>(<span class="keyword">new</span> <span class="predefined-type">File</span>(file)); <span class="directive">final</span> <span class="predefined-type">Constructor</span> constructor = <span class="keyword">new</span> <span class="predefined-type">Constructor</span>(AllowList.class); <span class="directive">final</span> TypeDescription allowListDescription = <span class="keyword">new</span> TypeDescription(AllowList.class); allowListDescription.putMapPropertyType(<span class="string"><span class="delimiter">"</span><span class="content">grants</span><span class="delimiter">"</span></span>, <span class="predefined-type">String</span>.class, <span class="predefined-type">Object</span>.class); allowListDescription.putMapPropertyType(<span class="string"><span class="delimiter">"</span><span class="content">groups</span><span class="delimiter">"</span></span>, <span class="predefined-type">String</span>.class, <span class="predefined-type">Object</span>.class); constructor.addTypeDescription(allowListDescription); <span class="directive">final</span> Yaml yaml = <span class="keyword">new</span> Yaml(constructor); <span class="keyword">return</span> yaml.loadAs(stream, AllowList.class); } }</code></pre> </div> </div> <div class="paragraph"> <p>allow-list.yaml:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">grants</span>: <span class="string"><span class="content">{</span></span> <span class="key">gclassic</span>: <span class="string"><span class="content">[groupclassic],</span></span> <span class="key">gmodern</span>: <span class="string"><span class="content">[groupmodern],</span></span> <span class="key">gcrew</span>: <span class="string"><span class="content">[groupclassic, groupmodern],</span></span> <span class="key">ggrateful</span>: <span class="string"><span class="content">[groupgrateful],</span></span> <span class="key">sandbox</span>: <span class="string"><span class="content">[groupsandbox]</span></span> } <span class="key">groups</span>: <span class="string"><span class="content">{</span></span> <span class="key">groupclassic</span>: <span class="string"><span class="content">[userclassic],</span></span> <span class="key">groupmodern</span>: <span class="string"><span class="content">[usermodern, stephen],</span></span> <span class="key">groupsink</span>: <span class="string"><span class="content">[usersink],</span></span> <span class="key">groupgrateful</span>: <span class="string"><span class="content">[anonymous],</span></span> <span class="key">groupsandbox</span>: <span class="string"><span class="content">[usersandbox, marko]</span></span> }</code></pre> </div> </div> </div> </div> <div class="sect3"> <h4 id="script-execution">Protecting Script Execution</h4> <div class="paragraph"> <p>It is important to remember that Gremlin Server exposes <code>GremlinScriptEngine</code> instances that allows for remote execution of arbitrary code on the server. Obviously, this situation can represent a security risk or, more minimally, provide ways for "bad" scripts to be inadvertently executed. A simple example of a "valid" Gremlin script that would cause some problems would be, <code>while(true) {}</code>, which would consume a thread in the Gremlin pool indefinitely, thus preventing it from serving other requests. Sending enough of these kinds of scripts would eventually consume all available threads and Gremlin Server would stop responding.</p> </div> <div class="paragraph"> <p>Scripts have access to the full power of their language and the JVM on which they are running. This means that they can access certain APIs that have nothing to do with Gremlin itself, such as <code>java.lang.System</code> or the <code>java.io</code> and <code>java.net</code> packages. Scripts offer developers a lot of flexibility, but having that flexibility comes at the cost of safety. A Gremlin Server instance that is not secured appropriately provides for a big security risk.</p> </div> <div class="paragraph"> <p>The previous sections discussed methods for securing Gremlin Server through authentication and encryption, which is a good first step in protection. Another layer of protection comes in the form of specific configurations for the <code>GremlinGroovyScriptEngine</code>. A user can configure the script engine with a <code>GroovyCompilerGremlinPlugin</code> implementation. Consider the basic configuration from the Gremlin Server YAML file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">scriptEngines</span>: <span class="string"><span class="content">{</span><span class="content"> gremlin-groovy: { plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {}, org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>This configuration can be expanded to include a the <code>GroovyCompilerGremlinPlugin</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">scriptEngines</span>: <span class="string"><span class="content">{</span><span class="content"> gremlin-groovy: { plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {} org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample-secure.groovy]}, org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin: {enableThreadInterrupt: true}}}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>This configuration sets up the script engine with to ensure that loops (like <code>while</code>) will respect interrupt requests. With this configuration in place, a remote execution as follows, now times out rather than consuming the thread continuously:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :remote connect tinkerpop.server conf/remote.yaml ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> :> <span class="keyword">while</span>(<span class="predefined-constant">true</span>) { } ==>Evaluation exceeded the configured <span class="string"><span class="delimiter">'</span><span class="content">evaluationTimeout</span><span class="delimiter">'</span></span> threshold of <span class="integer">30000</span> ms or evaluation was otherwise cancelled directly <span class="keyword">for</span> request [<span class="keyword">while</span>(<span class="predefined-constant">true</span>) {}]</code></pre> </div> </div> <div class="paragraph"> <p>The <code>GroovyCompilerGremlinPlugin</code> has a number of configuration options:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 23.0769%;"> <col style="width: 76.9231%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Customizer</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>compilation</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>Allows for three configurations: <code>COMPILE_STATIC</code>, <code>TYPE_CHECKED</code> or <code>NONE</code> (default). When configured with <code>COMPILE_STATIC</code> or <code>TYPE_CHECKED</code> it applies <code>CompileStatic</code> or <code>TypeChecked</code> annotations (respectively) to incoming scripts thus removing dynamic dispatch. More information about static compilation can be found <a href="http://docs.groovy-lang.org/latest/html/documentation/#_static_compilation">here</a> and additional information on <code>TypeChecked</code> usage can be found <a href="http://docs.groovy-lang.org/latest/html/documentation/#_the_code_typechecked_code_annotation">here</a>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>compilerConfigurationOptions</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>Allows configuration of the Groovy <code>CompilerConfiguration</code> object by taking a <code>Map</code> of key/value pairs where the "key" is a property to set on the <code>CompilerConfiguration</code>.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>enableThreadInterrupt</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>Injects checks for thread interruption, thus allowing the script to potentially respect calls to <code>Thread.interrupt()</code></p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>expectedCompilationTime</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>The amount of time in milliseconds a script is allowed to compile before a warning message is sent to the logs.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>globalFunctionCacheEnabled</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>Determines if the global function cache is enabled. By default, this value is <code>true</code> - described in more detail in the <a href="#gremlin-server-cache">Cache Management</a> Section.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>classMapCacheSpecification</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>The cache specification for the <code>GremlinGroovyScriptEngine</code> class map cache - described in more detail in the <a href="#gremlin-server-cache">Cache Management</a> Section.</p> </div></div></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>extensions</code></p></td> <td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph"> <p>This setting is for use when <code>compilation</code> is configured with <code>COMPILE_STATIC</code> or <code>TYPE_CHECKED</code> and accepts a comma separated list of <a href="http://docs.groovy-lang.org/latest/html/documentation/#Typecheckingextensions-Workingwithextensions">type checking extensions</a> that can have the effect of securing calls to various methods.</p> </div></div></td> </tr> </tbody> </table> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Consult the latest <a href="http://docs.groovy-lang.org/latest/html/documentation/#_typing">Groovy Documentation</a> for information on the differences on the various compilation options. It is important to understand the impact that these configuration will have on submitted scripts before enabling this feature. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> TinkerPop does not offer an end-to-end out-of-the-box solution to perfectly protect against bad actors submitting nefarious scripts. The configurations to follow which discuss the <code>SimpleSandboxExtension</code> and <code>FileSandboxExtension</code> are meant to represent example implementations that users and providers can gain some inspiration from in developing their own solutions. Please consult the documentation of your TinkerPop implementation to determine how scripts are "secured" as many providers have taken their own approaches to solving this problem. </td> </tr> </table> </div> <div class="paragraph"> <p>Securing scripts (i.e. preventing access to certain methods) is a bit more complicated of a story. As an example, TinkerPop implemented some basic "sandbox" implementations as described in this <a href="https://melix.github.io/blog/2015/03/sandboxing.html">blog post</a> to try to demonstrate a method by which script security could be achieved. Consider the following configuration of the <code>GroovyCompilerGremlinPlugin</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">scriptEngines</span>: <span class="string"><span class="content">{</span><span class="content"> gremlin-groovy: { plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {} org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin: {enableThreadInterrupt: true, compilation: COMPILE_STATIC, extensions: org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.SimpleSandboxExtension}, org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample-secure.groovy]}}}}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>This configuration uses the <code>SimpleSandboxExtension</code>, which blocks calls to methods on the <code>System</code> class, thereby preventing someone from remotely killing the server:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :> <span class="predefined-type">System</span>.exit(<span class="integer">0</span>) Script8.groovy: <span class="integer">1</span>: [Static type checking] - Not authorized to call <span class="local-variable">this</span> <span class="key">method</span>: java.lang.System<span class="error">#</span>exit(<span class="type">int</span>) <span class="error">@</span> line <span class="integer">1</span>, column <span class="integer">1</span>. System.exit(<span class="integer">0</span>) ^ <span class="integer">1</span> error</code></pre> </div> </div> <div class="paragraph"> <p>The <code>SimpleSandboxExtension</code> is by no means a "complete" implementation protecting against all manner of nefarious scripts, but it does provide an example for how such a capability might be implemented. A slightly more advanced example is offered in the <code>FileSandboxExtension</code> which uses a configuration file to allow certain classes and methods. The configuration file is YAML-based and an example is presented as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">autoTypeUnknown</span>: <span class="string"><span class="content">true</span></span> <span class="key">methodWhiteList</span>: - <span class="string"><span class="content">java\.lang\.Boolean.*</span></span> - <span class="string"><span class="content">java\.lang\.Byte.*</span></span> - <span class="string"><span class="content">java\.lang\.Character.*</span></span> - <span class="string"><span class="content">java\.lang\.Double.*</span></span> - <span class="string"><span class="content">java\.lang\.Enum.*</span></span> - <span class="string"><span class="content">java\.lang\.Float.*</span></span> - <span class="string"><span class="content">java\.lang\.Integer.*</span></span> - <span class="string"><span class="content">java\.lang\.Long.*</span></span> - <span class="string"><span class="content">java\.lang\.Math.*</span></span> - <span class="string"><span class="content">java\.lang\.Number.*</span></span> - <span class="string"><span class="content">java\.lang\.Object.*</span></span> - <span class="string"><span class="content">java\.lang\.Short.*</span></span> - <span class="string"><span class="content">java\.lang\.String.*</span></span> - <span class="string"><span class="content">java\.lang\.StringBuffer.*</span></span> - <span class="string"><span class="content">java\.lang\.System#currentTimeMillis\(\)</span></span> - <span class="string"><span class="content">java\.lang\.System#nanoTime\(\)</span></span> - <span class="string"><span class="content">java\.lang\.Throwable.*</span></span> - <span class="string"><span class="content">java\.lang\.Void.*</span></span> - <span class="string"><span class="content">java\.util\..*</span></span> - <span class="string"><span class="content">org\.codehaus\.groovy\.runtime\.DefaultGroovyMethods.*</span></span> - <span class="string"><span class="content">org\.codehaus\.groovy\.runtime\.InvokerHelper#runScript\(java\.lang\.Class,java\.lang\.String\[\]\)</span></span> - <span class="string"><span class="content">org\.codehaus\.groovy\.runtime\.StringGroovyMethods.*</span></span> - <span class="string"><span class="content">groovy\.lang\.Script#<init>\(groovy.lang.Binding\)</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.structure\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.computer\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.computer\.bulkloading\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.computer\.clustering\.peerpressure\.*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.computer\.ranking\.pagerank\.*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.computer\.traversal\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.traversal\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.traversal\.dsl\.graph\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.process\.traversal\.engine\..*</span></span> - <span class="string"><span class="content">org\.apache\.tinkerpop\.gremlin\.server\.util\.LifeCycleHook.*</span></span> <span class="key">staticVariableTypes</span>: <span class="key">graph</span>: <span class="string"><span class="content">org.apache.tinkerpop.gremlin.structure.Graph</span></span> <span class="key">g</span>: <span class="string"><span class="content">org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource</span></span></code></pre> </div> </div> <div class="paragraph"> <p>There are three keys in this configuration file that control different aspects of the sandbox:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>autoTypeUnknown</code> - When set to <code>true</code>, unresolved variables are typed as <code>Object</code>.</p> </li> <li> <p><code>methodWhiteList</code> - A white list of classes and methods that follow a regex pattern which can then be matched against method descriptors to determine if they can be executed. The method descriptor is the fully-qualified class name of the method, its name and parameters. For example, <code>Math.ceil</code> would have a descriptor of <code>java.lang.Math#ceil(double)</code>.</p> </li> <li> <p><code>staticVariableTypes</code> - A list of variables that will be used in the <code>ScriptEngine</code> for which the types are always known. In the above example, the variable "graph" will always be bound to a <code>Graph</code> instance.</p> </li> </ol> </div> <div class="paragraph"> <p>At Gremlin Server startup, the <code>FileSandboxExtension</code> looks in the root of Gremlin Server installation directory for a file called <code>sandbox.yaml</code> and configures itself. To use a file in a different location set the <code>gremlinServerSandbox</code> system property to the location of the file (e.g. <code>-DgremlinServerSandbox=conf/my-sandbox.yaml</code>).</p> </div> <div class="paragraph"> <p>A final thought on the topic of <code>GroovyCompilerGremlinPlugin</code> implementation is that it is not just for "security" (though it is demonstrated in that capacity here). It can be used for a variety of features that can fine tune the Groovy compilation process. Read more about compilation customization in the <a href="http://docs.groovy-lang.org/latest/html/documentation/#compilation-customizers">Groovy Documentation</a>.</p> </div> </div> </div> <div class="sect2"> <h3 id="_best_practices">Best Practices</h3> <div class="paragraph"> <p>The following sections define best practices for working with Gremlin Server.</p> </div> <div class="sect3"> <h4 id="_tuning">Tuning</h4> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-handdrawn.png" alt="gremlin handdrawn" width="120"></span> Tuning Gremlin Server for a particular environment may require some simple trial-and-error, but the following represent some basic guidelines that might be useful:</p> </div> <div class="ulist"> <ul> <li> <p>Gremlin Server defaults to a very modest maximum heap size. Consider increasing this value for non-trivial uses. Maximum heap size (<code>-Xmx</code>) is defined with the <code>JAVA_OPTIONS</code> setting in <code>gremlin-server.conf</code>.</p> </li> <li> <p>TinkerPop tends to discourage the use of <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/#long-traversals">long traversals</a> as they can introduce performance problems in some cases and in others simply fail with a <code>StackOverflowError</code>. Aside from restructuring the traversal into multiple commands or stream based inserts, it may sometimes make sense to simply increase the stack size of the JVM for Gremlin Server by configuring an <code>-Xss</code> setting in <code>JAVA_OPTIONS</code> of <code>gremlin-server.conf</code>.</p> </li> <li> <p>If Gremlin Server is processing scripts or lambdas in bytecode requests, consider fine tuning the JVM’s handling of the metaspace size. Consider modifying the <code>-XX:MetaspaceSize</code>,<code>-XX:MaxMetaspaceSize</code>, and related settings given the expected workload. More discussion on this topic can be found in the <a href="#parameterized-scripts">Parameterized Scripts</a> Section below.</p> </li> <li> <p>When configuring the size of <code>threadPoolWorker</code> start with the default of <code>1</code> and increment by one as needed to a maximum of <code>2*number of cores</code>.</p> </li> <li> <p>The "right" size of the <code>gremlinPool</code> setting is somewhat dependent on the type of requests that will be processed by Gremlin Server. As requests arrive to Gremlin Server they are decoded and queued to be processed by threads in this pool. When this pool is exhausted of threads, Gremlin Server will continue to accept incoming requests, but the queue will continue to grow. If left to grow too large, the server will begin to slow. When tuning around this setting, consider whether the bulk of the scripts being processed will be "fast" or "slow", where "fast" generally means being measured in the low hundreds of milliseconds and "slow" means anything longer than that.</p> </li> <li> <p>Requests that are "slow" can really hurt Gremlin Server if they are not properly accounted for. Since these requests block a thread until the job is complete or successfully interrupted, lots of long-run requests will eventually consume the <code>gremlinPool</code> preventing other requests from getting processed from the queue.</p> <div class="ulist"> <ul> <li> <p>To limit the impact of this problem, consider properly setting the <code>evaluationTimeout</code> to something "sane". In other words, test the traversals being sent to Gremlin Server and determine the maximum time they take to evaluate and iterate over results, then set the timeout value accordingly. Also, consider setting a shorter global timeout for requests and then use longer per-request timeouts for those specific ones that might execute at a longer rate.</p> </li> <li> <p>Note that <code>evaluationTimeout</code> can only attempt to interrupt the evaluation on timeout. It allows Gremlin Server to "ignore" the result of that evaluation, which means the thread in the <code>gremlinPool</code> that did the evaluation may still be consumed after the timeout if interruption does not succeed on the thread.</p> </li> </ul> </div> </li> <li> <p>When using sessions, there are different options to consider depending on the <code>Channelizer</code> implementation being used:</p> <div class="ulist"> <ul> <li> <p><code>WebSocketChannelizer</code> and <code>WsAndHttpChannelizer</code> - Both of these channelizers use the <code>gremlinPool</code> only for sessionless requests and construct a single threaded pool for each session created. In this way, these channelizers tend to optimize sessions to be long-lived. For short-lived sessions, which may be typical when using bytecode based remote transactions, quickly creating and destroying these sessions can be expensive. It is likely that there will be increased garbage collection times and frequency as well as a general increase in overall server processing.</p> </li> <li> <p><code>UnifiedChannelizer</code> - The threads of the <code>gremlinPool</code> are used to service both sessions and sessionless requests. With a common thread pool, this channelizer is a better choice when using lots of short-lived sessions as compared to <code>WebSocketChannelizer</code> and <code>WsAndHttpChannelizer</code>, because there is less cost in starting and stopping sessions. It is important though to understand the expected workload for the server and plan the size accordingly to ensure that the server does not need to wait for an extended period of time for a thread to be available to process the queue of incoming requests.</p> </li> </ul> </div> </li> <li> <p>Graph element serialization for <code>Vertex</code> and <code>Edge</code> can be expensive, as their data structures are complex given the possible existence of multi-properties and meta-properties. When returning data from Gremlin Server only return the data that is required. For example, if only two properties of a <code>Vertex</code> are needed then simply return the two rather than returning the entire <code>Vertex</code> object itself. Even with an entire <code>Vertex</code>, it is typically much faster to issue the query as <code>g.V(1).elementMap()</code> than <code>g.V(1)</code>, as the former returns a <code>Map</code> of the same data as a <code>Vertex</code>, but without all the associated structure which can slow the response.</p> </li> </ul> </div> </div> <div class="sect3"> <h4 id="parameterized-scripts">Parameterized Scripts</h4> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-parameterized.png" alt="gremlin parameterized" width="150"></span> If using the standard <code>GremlinGroovyScriptEngine</code> in Gremlin Server, it is imperative to use script parameterization. Period. There are at least two good reasons for doing so: script caching and protection from "Gremlin injection" (conceptually the same as the notion of SQL injection).</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> It is possible to use the <code>GremlinLangScriptEngine</code> in Gremlin Server as opposed to the <code>GremlinGroovyScriptEngine</code>. The former makes use of <code>gremlin-language</code> and its ANTLR grammar for parsing Gremlin scripts. This processing is different from the processing performed by Groovy and therefore spares users from the concerns of this section. When considering parameterization, users should also consider the graph database they are using to determine if it has native mechanisms that preclude the need for parameterization. </td> </tr> </table> </div> <div class="paragraph"> <p>With respect to caching, Gremlin Server caches all scripts that are passed to it. The cache is keyed based on the a hash of the script. Therefore <code>g.V(1)</code> and <code>g.V(2)</code> will be recognized as two separate scripts in the cache. If that script is parameterized to <code>g.V(x)</code> where <code>x</code> is passed as a parameter from the client, there will be no additional compilation cost for future requests on that script. Compilation of a script should be considered "expensive" and avoided when possible.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The parameterized script of <code>g.V(x)</code> is keyed in the cache differently than <code>g.V(y)</code> or even <code>g.V( x )</code>. Scripts must be exact string matches for recompilation to be avoided. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); Client client = cluster.connect(); <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> params = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); params.put(<span class="string"><span class="delimiter">"</span><span class="content">x</span><span class="delimiter">"</span></span>,<span class="integer">4</span>); client.submit(<span class="string"><span class="delimiter">"</span><span class="content">[1,2,3,x]</span><span class="delimiter">"</span></span>, params);</code></pre> </div> </div> <div class="paragraph"> <p>The more parameters that are used in a script the more expensive the compilation step becomes. Gremlin Server has a <code>OpProcessor</code> setting called <code>maxParameters</code>, which is mentioned in the <a href="#opprocessor-configurations">OpProcessor Configuration</a> section. It controls the maximum number of parameters that can be passed to the server for script evaluation purposes. Use of this setting can prevent accidental long run compilations, which individually are not terribly oppressive to the server, but taken as a group under high concurrency would be considered detrimental.</p> </div> <div class="paragraph"> <p>On the topic of Gremlin injection, note that it is possible to take advantage of Gremlin scripts in the same fashion as SQL scripts that are submitted as strings. When using string building patterns for queries without proper input scrubbing, it would be quite simple to do:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">String</span> lbl = <span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span> <span class="predefined-type">String</span> nodeId = <span class="string"><span class="delimiter">"</span><span class="content">mary').next();g.V().drop().iterate();g.V().has('id', 'thomas</span><span class="delimiter">"</span></span>; <span class="predefined-type">String</span> query = <span class="string"><span class="delimiter">"</span><span class="content">g.addV('</span><span class="delimiter">"</span></span> + lbl + <span class="string"><span class="delimiter">"</span><span class="content">').property('identifier','</span><span class="delimiter">"</span></span> + nodeId + <span class="string"><span class="delimiter">"</span><span class="content">')</span><span class="delimiter">"</span></span>; client.submit(query);</code></pre> </div> </div> <div class="paragraph"> <p>The above case would <code>drop()</code> all vertices in the graph. By using script parameterization, there is a different outcome in that the <code>nodeId</code> string is not treated as something executable, but rather as a literal string that just becomes part of the "identifier" for the vertex on insertion:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">String</span> lbl = <span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span> <span class="predefined-type">String</span> nodeId = <span class="string"><span class="delimiter">"</span><span class="content">mary').next();g.V().drop().iterate();g.V().has('id', 'thomas</span><span class="delimiter">"</span></span>; <span class="predefined-type">String</span> query = <span class="string"><span class="delimiter">"</span><span class="content">g.addV(lbl).property('identifier',nodeId)</span><span class="delimiter">"</span></span>; <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> params = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); params.put(<span class="string"><span class="delimiter">"</span><span class="content">lbl</span><span class="delimiter">"</span></span>,lbl); params.put(<span class="string"><span class="delimiter">"</span><span class="content">nodeId</span><span class="delimiter">"</span></span>,nodeId); client.submit(query, params);</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin injection should not be possible with <code>Bytecode</code> based traversals - only scripts - because <code>Bytecode</code> traversals will treat all arguments as literal values. There is potential for concern if lambda based steps are utilized as they execute arbitrary code, which is string based, but configuring <code>TraversalSource</code> instances with <code>LambdaRestrictionStrategy</code>, which prevents lambdas all together, using a graph that does not allow lambdas at all, or configuring appropriate <a href="#script-execution">sandbox options</a> in Gremlin Server (or such options available to the graph database in use) should each help mitigate problems related to this issue.</p> </div> <div class="paragraph"> <p>Scripts create classes which get loaded to the JVM metaspace and to a <code>Class</code> cache. For those using script parameterization, a typical application should not generate an overabundance of pressure on these two components of Gremlin Server’s memory footprint. On the other hand, it’s not too hard to imagine a situation where problems might emerge:</p> </div> <div class="ulist"> <ul> <li> <p>An application use case makes parameterization impossible and therefore all scripts are unique.</p> </li> <li> <p>There is a bug in an applications parameterization code that is actually instead producing unique scripts.</p> </li> <li> <p>A long running Gremln Server takes lots of non-parameterized scripts from Gremlin Console or similar tools.</p> </li> </ul> </div> <div class="paragraph"> <p>In these sorts of cases, Gremlin Server’s performance can be affected adversely as without some additional configuration the metaspace will grow indefinitely (possibly along with the general heap) triggering longer and more frequent rounds of garbage collection (GC). Some tuning of JVM settings can help abate this issue.</p> </div> <div class="paragraph"> <p>As a first guard against this problem consider setting the <code>-XX:SoftRefLRUPolicyMSPerMB</code> to release soft references earlier. The <code>ScriptEngine</code> cache for created <code>Class</code> objects uses soft references and if the workload expectation is such that cache hits will be low there is little need to keep such references around.</p> </div> <div class="paragraph"> <p>Perhaps the more important guards are related to the JVM metaspace. Start by setting the initial size of this space with <code>-XX:MetaspaceSize</code>. When this value is exceeded it will trigger a GC round - it is essentially a threshold for GC. The grow of this value can be capped with <code>-XX:MaxMetaspaceSize</code> (this value is unlimited by default). In an ideal situation (i.e. parameterization), the <code>-XX:MetaspaceSize</code> should have a large enough setting so as to avoid early GC rounds for metaspace, but outside of an ideal world (i.e. non-parameterization) it may not be smart to make this number too large. Making the setting too large (and thus the <code>-XX:MaxMetaspaceSize</code> even larger) may trigger longer GC rounds when they inevitably arrive.</p> </div> <div class="paragraph"> <p>In addition to those two metaspace settings it may also be useful to consider the following additional options:</p> </div> <div class="ulist"> <ul> <li> <p><code>MinMetaspaceFreeRatio</code> - When the percentage for committed space available for class metadata is less than this value, then the threshold of metaspace GC will be raised, but only if the incremental size of the threshold meets the requirement set by <code>MinMetaspaceExpansion</code>. A larger number should make the metaspace grow more aggressively.</p> </li> <li> <p><code>MaxMetaspaceFreeRatio</code> - When the percentage for committed space available for class metadata is more than this value, then the threshold of metaspace GC will be lowered, but only if the incremental size of the threshold meets the requirement set by <code>MaxMetaspaceExpansion</code>. A larger number should reduce the chance of the metaspace shrinking.</p> </li> <li> <p><code>MinMetaspaceExpansion</code> - The minimum size by which the metaspace is expanded after a metaspace GC round.</p> </li> <li> <p><code>MaxMetaspaceExpansion`</code> - If the incremental size exceeds <code>MinMetaspaceExpansion</code> but less than <code>MaxMetaspaceExpansion</code>, then the incremental size is <code>MaxMetaspaceExpansion</code>. If the incremental size exceeds <code>MaxMetaspaceExpansion</code>, then the incremental size is <code>MinMetaspaceExpansion</code> plus the original incremental size.</p> </li> </ul> </div> <div class="paragraph"> <p>There really aren’t any general guidelines for how to initially set these values. Using profiling tools to examine GC trends is likely the best way to understand how a particular workload is affecting the metaspace and its relation to GC. Getting these settings "right" however will help ensure much more predictable Gremlin Server operations.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> A lambda used in a bytecode-based request will be treated as a script, so issues related to raw script-based requests apply equally well to lambda-bytecode requests. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_properties_of_elements">Properties of Elements</h4> <div class="paragraph"> <p>It was mentioned above at the start of this "Best Practices" section that serialization of graph elements (i.e. <code>Vertex</code>, <code>Edge</code>, and <code>VertexProperty</code>) can be expensive and that it is best to only return the data that is required by the requesting system. This point begs for further clarification as there are a number of ways to use and configure Gremlin Server which might influence its interpretation.</p> </div> <div class="paragraph"> <p>To begin to discuss these nuances, first consider the method of making requests to Gremlin Server: script or bytecode. For scripts, that will mean that users are sending string representation of Gremlin to the server directly through a driver over websockets or through the HTTP. For bytecode, users will be utilize a <a href="#gremlin-drivers-variants">Gremlin GLV</a> which will construct bytecode for them and submit the request to the server upon iteration of their traversal.</p> </div> <div class="paragraph"> <p>In either case, it is important to also consider the method of "detachment". Detachment refers to the manner in which a graph element is disconnected from the graph for purpose of serialization. Depending on the case and configuration, graph elements may be detached with or without properties. Cases where they include properties is generally referred to as "detached elements" and cases where properties are not included are "reference elements".</p> </div> <div class="paragraph"> <p>With the type of request and detachment model in mind, it is now possible to discuss how best to consider element properties in relation to them all in concert.</p> </div> <div class="paragraph"> <p>By default, Gremlin Server configuration returns all properties.</p> </div> <div class="paragraph"> <p>To manage properties for each request you can use the <a href="#configuration-steps-with">with()</a> configuration option <code>materializeProperties</code></p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.with(<span class="string"><span class="delimiter">'</span><span class="content">materializeProperties</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">tokens</span><span class="delimiter">'</span></span>).V()</code></pre> </div> </div> <div class="paragraph"> <p>The <code>tokens</code> value for the <code>materializeProperties</code> means that only <code>id</code> and <code>label</code> should be returned. Another option, <code>all</code>, can be used to indicate that all properties should be returned and is the default value.</p> </div> <div class="paragraph"> <p>In some cases it can be inconvenient to load Elements with properties due to large data size or for compatibility reasons. That can be solved by utilizing <code>ReferenceElementStrategy</code> when creating the out-of-the-box <code>GraphTraversalSource</code>. As the name suggests, this means that elements will be detached by reference and will therefore not have properties included. The relevant configuration from the Gremlin Server initialization script looks like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">globals << [g : traversal().withEmbedded(graph).withStrategies(ReferenceElementStrategy)]</code></pre> </div> </div> <div class="paragraph"> <p>This configuration is global to Gremlin Server and therefore all methods of connection will always return elements without properties. If this strategy is not included, then elements will be returned with properties.</p> </div> <div class="paragraph"> <p>Ultimately, the detachment model should have little impact to Gremlin usage if the best practice of specifying only the data required by the application is adhered to.</p> </div> <div class="paragraph"> <p>The best practice of requesting only the data the application needs:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); Client client = cluster.connect(); <span class="predefined-type">ResultSet</span> results = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().hasLabel('person').elementMap('name')</span><span class="delimiter">"</span></span>); GraphTraversalSource g = traversal().withRemote(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-graph.properties</span><span class="delimiter">'</span></span>); <span class="predefined-type">List</span><Vertex> results = g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).elementMap(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).toList();</code></pre> </div> </div> <div class="paragraph"> <p>Both of the above requests return a list of <code>Map</code> instances that contain the <code>id</code>, <code>label</code> and the "name" property.</p> </div> <div class="paragraph"> <p><strong>Compatibility</strong></p> </div> <div class="paragraph"> <p><strong>It is not recommended to use 3.6.x or below driver versions with 3.7.x or above Gremlin Server</strong>, as some older drivers do not construct graph elements with properties and thus are not designed to handle the returned properties by default; however, compatibility can be achieved by configuring <code>ReferenceElementStrategy</code> in the server such that properties are not returned. Per-request configuration option <code>materializeProperties</code> is not supported older driver versions.</p> </div> <div class="paragraph"> <p>Also note that older drivers of different language variants will handle incoming properties differently with different serializers used. Drivers using <code>GraphSON</code> serializers will remain compatible, but may encounter deserialization errors with <code>GraphBinary</code>. Below is a table documenting GLV behaviors using <code>GraphBinary</code> when properties are returned by the default 3.7.x server, as well as if <code>ReferenceElementStrategy</code> is configured (i.e. mimic the behavior of a 3.6.x server). This can be observed with the results of <code>g.V().next()</code>. Note that only <code>gremlin-driver</code> and <code>gremlin-javacript</code> have the <code>properties</code> attribute in the Element objects, all other GLVs only have <code>id</code> and <code>label</code>.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 33.3333%;"> <col style="width: 33.3333%;"> <col style="width: 33.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">3.6.x drivers with <code>GraphBinary</code></th> <th class="tableblock halign-left valign-top">Behavior with default 3.7.x Server</th> <th class="tableblock halign-left valign-top">Behavior with <code>ReferenceElementStrategy</code></th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>gremlin-driver</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Properties returned as empty iterator</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Properties returned as empty iterator</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>gremlin-dotnet</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Skips properties in Elements</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Skips properties in Elements</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>gremlin-javascript</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Deserialization error</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Properties returned as empty list</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>gremlin-python</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Deserialization error</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Skips properties in Elements</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>gremlin-go</code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Deserialization error</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Skips properties in Elements</p></td> </tr> </tbody> </table> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Consider utilizing <code>ReferenceElementStrategy</code> whenever creating a <code>GraphTraversalSource</code> in Java to ensure the most portable Gremlin. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> For those interested, please see <a href="https://lists.apache.org/thread.html/e959e85d4f8b3d46d281f2742a6e574c7d27c54bfc52f802f7c04af3%40%3Cdev.tinkerpop.apache.org%3E">this post</a> to the TinkerPop dev list which outlines the full history of this issue and related concerns. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="gremlin-server-cache">Cache Management</h4> <div class="paragraph"> <p>If Gremlin Server processes a large number of unique scripts, the global function cache will grow beyond the memory available to Gremlin Server and an <code>OutOfMemoryError</code> will loom. Script parameterization goes a long way to solving this problem and running out of memory should not be an issue for those cases. If it is a problem or if there is no script parameterization due to a given use case (perhaps using with use of <a href="#sessions">sessions</a>), it is possible to better control the nature of the global function cache from the client side, by issuing scripts with a parameter to help define how the garbage collector should treat the references.</p> </div> <div class="paragraph"> <p>The parameter is called <code>#jsr223.groovy.engine.keep.globals</code> and has four options:</p> </div> <div class="ulist"> <ul> <li> <p><code>hard</code> - available in the cache for the life of the JVM (default when not specified).</p> </li> <li> <p><code>soft</code> - retained until memory is "low" and should be reclaimed before an <code>OutOfMemoryError</code> is thrown.</p> </li> <li> <p><code>weak</code> - garbage collected even when memory is abundant.</p> </li> <li> <p><code>phantom</code> - removed immediately after being evaluated by the <code>ScriptEngine</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>By specifying an option other than <code>hard</code>, an <code>OutOfMemoryError</code> in Gremlin Server should be avoided. Of course, this approach will come with the downside that functions could be garbage collected and thus removed from the cache, forcing Gremlin Server to recompile later if that script is later encountered.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); Client client = cluster.connect(); <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> params = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); params.put(<span class="string"><span class="delimiter">"</span><span class="content">#jsr223.groovy.engine.keep.globals</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">soft</span><span class="delimiter">"</span></span>); client.submit(<span class="string"><span class="delimiter">"</span><span class="content">def addItUp(x,y){x+y}</span><span class="delimiter">"</span></span>, params);</code></pre> </div> </div> <div class="paragraph"> <p>In cases where maintaining the expense of the global function cache is unecessary this cache can be disabled with the <code>globalFunctionCacheEnabled</code> configuration on the <code>GroovyCompilerGremlinPlugin</code>.</p> </div> <div class="paragraph"> <p>Gremlin Server also has a "class map" cache which holds compiled scripts which helps avoid recompilation costs on future requests. This cache can be tuned in the Gremlin Server configuration with the <code>GroovyCompilerGremlinPlugin</code> in the following fashion:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">scriptEngines</span>: <span class="string"><span class="content">{</span><span class="content"> gremlin-groovy: { plugins: { ... org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin: {classMapCacheSpecification: "initialCapacity=1000,maximumSize=10000"}, ...}</span></span></code></pre> </div> </div> <div class="paragraph"> <p>The specifics for this comma delimited format can be found <a href="https://static.javadoc.io/com.github.ben-manes.caffeine/caffeine/2.6.2/com/github/benmanes/caffeine/cache/CaffeineSpec.html">here</a>. By default, the cache is set to <code>softValues</code> which means they are garbage collected in a globally least-recently-used manner as memory gets low. For production systems, it is likely that a more predictable strategy be taken as shown above with the use of the <code>maximumSize</code>.</p> </div> </div> <div class="sect3"> <h4 id="sessions">Considering Sessions</h4> <div class="paragraph"> <p>The preferred approach for issuing script-based requests to Gremlin Server is to do so in a sessionless manner. The concept of "sessionless" refers to a request that is completely encapsulated within a single transaction, such that the script in the request starts with a new transaction and ends with a closed transaction. Sessionless requests have automatic transaction management handled by Gremlin Server, thus automatically opening and closing transactions as previously described. The downside to the sessionless approach is that the entire script to be executed must be known at the time of submission so that it can all be executed at once. This requirement makes it difficult for some use cases where more control over the transaction is desired.</p> </div> <div class="paragraph"> <p>For such use cases, Gremlin Server supports sessions. With sessions, the user is in complete control of the start and end of the transaction. This feature comes with some additional expense to consider:</p> </div> <div class="ulist"> <ul> <li> <p>Initialization scripts will be executed for each session created so any expense related to them will be established each time a session is constructed.</p> </li> <li> <p>There will be one script cache per session, which obviously increases memory requirements. The cache is not shared, so as to ensure that a session has isolation from other session environments. As a result, if the same script is executed in each session the same compilation cost will be paid for each session it is executed in.</p> </li> <li> <p>Each session will require its own thread pool with a single thread in it - this ensures that transactional boundaries are managed properly from one request to the next.</p> </li> <li> <p>If there are multiple Gremlin Server instances, communication from the client to the server must be bound to the server that the session was initialized in. Gremlin Server does not share session state as the transactional context of a <code>Graph</code> is bound to the thread it was initialized in.</p> </li> </ul> </div> <div class="paragraph"> <p>To connect to a session with Java via the <code>gremlin-driver</code>, it is necessary to create a <code>SessionedClient</code> from the <code>Cluster</code> object:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); <span class="invisible">//</span><b class="conum">1</b> Client client = cluster.connect(<span class="string"><span class="delimiter">"</span><span class="content">sessionName</span><span class="delimiter">"</span></span>); <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Opens a reference to <code>localhost</code> as <a href="#gremlin-java">previously shown</a>.</p> </li> <li> <p>Creates a <code>SessionedClient</code> given the configuration options of the Cluster. The <code>connect()</code> method is given a <code>String</code> value that becomes the unique name of the session. It is often best to simply use a <code>UUID</code> to represent the session.</p> </li> </ol> </div> <div class="paragraph"> <p>It is also possible to have Gremlin Server manage the transactions as is done with sessionless requests. The user is in control of enabling this feature when creating the <code>SessionedClient</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); Client client = cluster.connect(<span class="string"><span class="delimiter">"</span><span class="content">sessionName</span><span class="delimiter">"</span></span>, <span class="predefined-constant">true</span>);</code></pre> </div> </div> <div class="paragraph"> <p>Specifying <code>true</code> to the <code>connect()</code> method signifies that the <code>client</code> should make each request as one encapsulated in a transaction. With this configuration of <code>client</code> there is no need to close a transaction manually.</p> </div> <div class="paragraph"> <p>When using this mode of the <code>SessionedClient</code> it is important to recognize that global variable state for the session is not rolled-back on failure depending on where the failure occurs. For example, sending the following script would create a variable "x" in global session scope that would be accessible on the next request:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">x = <span class="integer">1</span></code></pre> </div> </div> <div class="paragraph"> <p>However, sending this script which explicitly throws an exception:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">y = <span class="integer">2</span> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">RuntimeException</span>()</code></pre> </div> </div> <div class="paragraph"> <p>will result in an obvious failure during script evaluation and "y" will not be available to the next request. The complication arises where the script evaluates successfully, but fails during result iteration or serialization. For example, this script:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">a = <span class="integer">1</span> g.addV()</code></pre> </div> </div> <div class="paragraph"> <p>would successfully evaluate and return a <code>Traversal</code>. The variable "a" would be available on the next request. However, if there was a failure in transaction management on the call to <code>commit()</code>, "a" would still be available to the next request.</p> </div> <div class="paragraph"> <p>To avoid unexpected problems with state in relation to errors in sessions, it is best to follow these guidelines:</p> </div> <div class="ulist"> <ul> <li> <p>Do not re-use session identifiers. Simply use a new UUID for each session.</p> </li> <li> <p>On exception, be sure to call <code>close()</code> on the <code>Client</code> and create a new session.</p> </li> <li> <p>While you may submit parallel asynchronous requests to a session, it may not make sense to do so because they are simply executed serially as they arrive to the session. A failed asynchronous request could leave an invalid state in the session which may not allow later requests to succeed. Either use synchronous requests only or carefully consider error conditions with asynchronous requests.</p> </li> </ul> </div> <div class="paragraph"> <p>If using the <code>UnifiedChannelizer</code>, failures in evaluation will result in the session being closed and state being lost. Asynchronous requests that are queued on the server will be cancelled and additional requests, in-flight or otherwise will be rejected. Users should create a new session from the <code>Cluster</code> object in this case. The alternative, to match the old <code>OpProcessor</code> GremlinServer behavior, is to add the <code>maintainStateAfterException</code> session setting to <code>true</code> which will instead have similar behavior to that described in this section.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Client.SessionSettings settings = Client.SessionSettings.build().maintainStateAfterException(<span class="predefined-constant">true</span>).create(); Client session = cluster.connect(Client.Settings.build().useSession(settings).create());</code></pre> </div> </div> <div class="paragraph"> <p>A session is a "heavier" approach to the simple "request/response" approach of sessionless requests, but is sometimes necessary for a given use case.</p> </div> </div> <div class="sect3"> <h4 id="considering-transactions">Considering Transactions</h4> <div class="paragraph"> <p>Gremlin Server performs automated transaction handling for "sessionless" requests (i.e. no state between requests) and for "in-session" requests with that feature enabled. It will automatically commit or rollback transactions depending on the success or failure of the request.</p> </div> <div class="paragraph"> <p>Another aspect of Transaction Management that should be considered is the usage of the <code>strictTransactionManagement</code> setting. It is <code>false</code> by default, but when set to <code>true</code>, it forces the user to pass <code>aliases</code> for all requests. The aliases are then used to determine which graphs will have their transactions closed for that request. Running Gremlin Server in this configuration should be more efficient when there are multiple graphs being hosted as Gremlin Server will only close transactions on the graphs specified by the <code>aliases</code>. Keeping this setting <code>false</code>, will simply have Gremlin Server close transactions on all graphs for every request.</p> </div> </div> <div class="sect3"> <h4 id="considering-state">Considering State</h4> <div class="paragraph"> <p>With HTTP and any sessionless requests, there is no variable state maintained between requests. Therefore, when <a href="#connecting-via-console">connecting with the console</a>, for example, it is not possible to create a variable in one command and then expect to access it in the next:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :remote connect tinkerpop.server conf/remote.yaml ==>Configured localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> :> x = <span class="integer">2</span> ==><span class="integer">2</span> gremlin> :> <span class="integer">2</span> + x No such <span class="key">property</span>: x <span class="keyword">for</span> <span class="type">class</span>: <span class="class">Script4</span> Display stack trace? [yN] n</code></pre> </div> </div> <div class="paragraph"> <p>The same behavior would be seen with HTTP or when using sessionless requests through one of the Gremlin Server drivers. If having this behavior is desireable, then <a href="#sessions">consider sessions</a>.</p> </div> <div class="paragraph"> <p>There is an exception to this notion of state not existing between requests and that is globally defined functions. All functions created via scripts are global to the server.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :> <span class="keyword">def</span> <span class="function">subtractIt</span>(<span class="type">int</span> x, <span class="type">int</span> y) { x - y } ==><span class="predefined-constant">null</span> gremlin> :> subtractIt(<span class="integer">8</span>,<span class="integer">7</span>) ==><span class="integer">1</span></code></pre> </div> </div> <div class="paragraph"> <p>If this behavior is not desirable there are several options. A first option would be to consider using sessions. Each session gets its own <code>ScriptEngine</code>, which maintains its own isolated cache of global functions, whereas sessionless requests uses a single function cache. A second option would be to define functions as closures:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :> multiplyIt = { <span class="type">int</span> x, <span class="type">int</span> y -> x * y } ==>Script7<span class="error">$</span>_run_closure1<span class="error">@</span><span class="integer">6</span>b24f3ab gremlin> :> multiplyIt(<span class="integer">7</span>, <span class="integer">8</span>) No signature of <span class="key">method</span>: org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.multiplyIt() is applicable <span class="keyword">for</span> argument <span class="key">types</span>: (java.lang.Integer, java.lang.Integer) <span class="key">values</span>: [<span class="integer">7</span>, <span class="integer">8</span>] Display stack trace? [yN]</code></pre> </div> </div> <div class="paragraph"> <p>When the function is declared this way, the function is viewed by the <code>ScriptEngine</code> as a variable rather than a global function and since sessionless requests don’t maintain state, the function is forgotten for the next request. A final option would be to manage the <code>ScriptEngine</code> cache manually:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bourne">$ curl -X POST -d "{\"gremlin\":\"def divideIt(int x, int y){ x / y }\",\"bindings\":{\"#jsr223.groovy.engine.keep.globals\":\"phantom\"}}" "http://localhost:8182" {"requestId":"97fe1467-a943-45ea-8fd6-9e889a6c9381","status":{"message":"","code":200,"attributes":{}},"result":{"data":[null],"meta":{}}} $ curl -X POST -d "{\"gremlin\":\"divideIt(8, 2)\"}" "http://localhost:8182" {"message":"Error encountered evaluating script: divideIt(8, 2)"}</code></pre> </div> </div> <div class="paragraph"> <p>In the above HTTP-based requests, the bindings contain a special parameter that tells the <code>ScriptEngine</code> cache to immediately forget the script after execution. In this way, the function does not end up being globally available.</p> </div> </div> <div class="sect3"> <h4 id="request-retry">Request Retry</h4> <div class="paragraph"> <p>The server has the ability to instruct the client that an error condition is transient and that the client should simply retry the request later. In the event a client detects a <code>ResponseStatusCode</code> of <code>SERVER_ERROR_TEMPORARY</code>, which is error code <code>596</code>, the client may choose to retry that request. Note that drivers do not have the ability to automatically retry and that it is up to the application to provide such logic.</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-server-docker-image">Docker Image</h3> <div class="paragraph"> <p>The Gremlin Server can also be started as a <a href="https://hub.docker.com/r/tinkerpop/gremlin-server/">Docker image</a>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ docker run tinkerpop/gremlin-server:3.7.3 [INFO] GremlinServer - \,,,/ (o o) -----oOOo-(3)-oOOo----- [INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server.yaml ... [INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 4 and boss thread pool of 1. [INFO] GremlinServer$1 - Channel started at port 8182.</code></pre> </div> </div> <div class="paragraph"> <p>By default, Gremlin Server listens on port 8182. So that port needs to be exposed if it should be reachable on the host:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ docker run -p 8182:8182 tinkerpop/gremlin-server:3.7.3</code></pre> </div> </div> <div class="paragraph"> <p>Arguments provided with <code>docker run</code> are forwarded to the script that starts Gremlin Server. This allows for example to use an alternative config file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">$ docker run tinkerpop/gremlin-server:3.7.3 conf/gremlin-server-secure.yaml</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-plugins">Gremlin Plugins</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image"><img src="../images/gremlin-plugin.png" alt="gremlin plugin" width="125"></span></p> </div> <div class="paragraph"> <p>Plugins provide a way to expand the features of Gremlin Console and Gremlin Server. The following sections describe the plugins that are available directly from TinkerPop. Please see the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#gremlin-plugins">Provider Documentation</a> for information on how to develop custom plugins.</p> </div> <div class="sect2"> <h3 id="credentials-plugin">Credentials Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-server.png" alt="gremlin server" width="200"></span> <a href="#gremlin-server">Gremlin Server</a> supports an authentication model where user credentials are stored inside of a <code>Graph</code> instance. This database can be managed with the <a href="#credentials-dsl">Credentials DSL</a>, which can be installed in the console via the Credentials Plugin. This plugin is packaged with the console, but is not enabled by default.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :plugin use tinkerpop.credentials ==>tinkerpop.credentials activated</code></pre> </div> </div> <div class="paragraph"> <p>This plugin imports the appropriate classes for managing the credentials graph.</p> </div> </div> <div class="sect2"> <h3 id="gephi-plugin">Gephi Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gephi-logo.png" alt="gephi logo" width="200"></span> <a href="http://gephi.org/">Gephi</a> is an interactive visualization, exploration, and analysis platform for graphs. The <a href="https://gephi.org/plugins/#/plugin/graphstreaming">Graph Streaming</a> plugin for Gephi provides an API that can be leveraged to stream graph data to a running Gephi application. The Gephi plugin for Gremlin Console utilizes this API to allow for graph and traversal visualization.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> These instructions have been tested with Gephi 0.9.2 and Graph Streaming plugin 1.0.3. </td> </tr> </table> </div> <div class="paragraph"> <p>The following instructions assume that Gephi has been download and installed. It further assumes that the Graph Streaming plugin has been installed (<code>Tools > Plugins</code>). The following instructions explain how to visualize a <code>Graph</code> and <code>Traversal</code>.</p> </div> <div class="paragraph"> <p>In Gephi, create a new project with <code>File > New Project</code>. In the lower left view, click the "Streaming" tab, open the Master drop down, and right click <code>Master Server > Start</code> which starts the Graph Streaming server in Gephi and by default accepts requests at <code><a href="http://localhost:8080/workspace1" class="bare">http://localhost:8080/workspace1</a></code>:</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gephi-start-server.png" alt="gephi start server" width="800"> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The Gephi Streaming Plugin doesn’t detect port conflicts and will appear to start the plugin successfully even if there is something already active on that port it wants to connect to (which is 8080 by default). Be sure that there is nothing running on the port before Gephi will be using before starting the plugin. Failing to do this produce behavior where the console will appear to submit requests to Gephi successfully but nothing will render. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Do not skip the <code>File > New Project</code> step as it may prevent a newly started Gephi application from fully enabling the streaming tab. </td> </tr> </table> </div> <div class="paragraph"> <p>Start the <a href="#gremlin-console">Gremlin Console</a> and activate the Gephi plugin:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-27" type="radio" name="radio-set-1729797129-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-28" type="radio" name="radio-set-1729797129-27" class="tab-selector-2" /> <label for="tab-1729797129-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> :plugin use tinkerpop.gephi ==>tinkerpop.gephi activated gremlin> graph = TinkerFactory.createModern() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> :remote connect tinkerpop.gephi ==><span class="predefined-type">Connection</span> to Gephi - <span class="key">http</span>:<span class="comment">//localhost:8080/workspace1 with stepDelay:1000, startRGBColor:[0.0, 1.0, 0.5], colorToFade:g, colorFadeRate:0.7, startSize:10.0,sizeDecrementRate:0.33</span> gremlin> :> graph ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] ==><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">:plugin use tinkerpop.gephi graph = TinkerFactory.createModern() :remote connect tinkerpop.gephi :> graph</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>The above Gremlin session activates the Gephi plugin, creates the "modern" <code>TinkerGraph</code>, uses the <code>:remote</code> command to setup a connection to the Graph Streaming server in Gephi (with default parameters that will be explained below), and then uses <code>:submit</code> which sends the vertices and edges of the graph to the Gephi Streaming Server. The resulting graph appears in Gephi as displayed in the left image below.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gephi-graph-submit.png" alt="gephi graph submit" width="800"> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Issuing <code>:> graph</code> again will clear the Gephi workspace and then re-write the graph. To manually empty the workspace do <code>:> clear</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>Now that the graph is visualized in Gephi, it is possible to <a href="https://gephi.github.io/users/tutorial-layouts/">apply a layout algorithm</a>, change the size and/or color of vertices and edges, and display labels/properties of interest. Further information can be found in Gephi’s tutorial on <a href="https://gephi.github.io/users/tutorial-visualization/">Visualization</a>. After applying the Fruchterman Reingold layout, increasing the node size, decreasing the edge scale, and displaying the id, name, and weight attributes the graph looks as displayed in the right image above.</p> </div> <div class="paragraph"> <p>Visualization of a <code>Traversal</code> has a different approach as the visualization occurs as the <code>Traversal</code> is executing, thus showing a real-time view of its execution. A <code>Traversal</code> must be "configured" to operate in this format and for that it requires use of the <code>visualTraversal</code> option on the <code>config</code> function of the <code>:remote</code> command:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-29" type="radio" name="radio-set-1729797129-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-30" type="radio" name="radio-set-1729797129-29" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote config visualTraversal graph <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="predefined-type">Connection</span> to Gephi - <span class="key">http</span>:<span class="comment">//localhost:8080/workspace1 with stepDelay:1000, startRGBColor:[0.0, 1.0, 0.5], colorToFade:g, colorFadeRate:0.7, startSize:10.0,sizeDecrementRate:0.33</span> gremlin> traversal = vg.V(<span class="integer">2</span>).in().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">30</span>)).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> <span class="comment">//</span>// <b class="conum">(2)</b> gremlin> :> traversal <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">5</span>] ==><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">:remote config visualTraversal graph <span class="comment">//</span>// <b class="conum">(1)</b> traversal = vg.V(<span class="integer">2</span>).in().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">30</span>)).outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>). has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,gt(<span class="float">0.5d</span>)).inV();<span class="type">[]</span> <span class="comment">//</span>// <b class="conum">(2)</b> :> traversal <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Configure a "visual traversal" from your "graph" - this must be a <code>Graph</code> instance. This command will create a new <code>TraversalSource</code> called "vg" that must be used to visualize any spawned traversals in Gephi.</p> </li> <li> <p>Define the traversal to be visualized. Note that ending the line with <code>;[]</code> simply prevents iteration of the traversal before it is submitted.</p> </li> <li> <p>Submit the <code>Traversal</code> to visualize to Gephi.</p> </li> </ol> </div> <div class="paragraph"> <p>When the <code>:></code> line is called, each step of the <code>Traversal</code> that produces or filters vertices generates events to Gephi. The events update the color and size of the vertices at that step with <code>startRGBColor</code> and <code>startSize</code> respectively. After the first step visualization, it sleeps for the configured <code>stepDelay</code> in milliseconds. On the second step, it decays the configured <code>colorToFade</code> of all the previously visited vertices in prior steps, by multiplying the current <code>colorToFade</code> value for each vertex with the <code>colorFadeRate</code>. Setting the <code>colorFadeRate</code> value to <code>1.0</code> will prevent the color decay. The screenshots below show how the visualization evolves over the four steps:</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/gephi-traversal.png" alt="gephi traversal" width="1200"> </div> </div> <div class="paragraph"> <p>To get a sense of how the visualization configuration parameters affect the output, see the example below:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-31" type="radio" name="radio-set-1729797129-31" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-31" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-32" type="radio" name="radio-set-1729797129-31" class="tab-selector-2" /> <label for="tab-1729797129-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> :remote config startRGBColor [<span class="float">0.0</span>,<span class="float">0.3</span>,<span class="float">1.0</span>] ==><span class="predefined-type">Connection</span> to Gephi - <span class="key">http</span>:<span class="comment">//localhost:8080/workspace1 with stepDelay:1000, startRGBColor:[0.0, 0.3, 1.0], colorToFade:g, colorFadeRate:0.7, startSize:10.0,sizeDecrementRate:0.33</span> gremlin> :remote config colorToFade b ==><span class="predefined-type">Connection</span> to Gephi - <span class="key">http</span>:<span class="comment">//localhost:8080/workspace1 with stepDelay:1000, startRGBColor:[0.0, 0.3, 1.0], colorToFade:b, colorFadeRate:0.7, startSize:10.0,sizeDecrementRate:0.33</span> gremlin> :remote config colorFadeRate <span class="float">0.5</span> ==><span class="predefined-type">Connection</span> to Gephi - <span class="key">http</span>:<span class="comment">//localhost:8080/workspace1 with stepDelay:1000, startRGBColor:[0.0, 0.3, 1.0], colorToFade:b, colorFadeRate:0.5, startSize:10.0,sizeDecrementRate:0.33</span> gremlin> :> traversal ==><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">:remote config startRGBColor [<span class="float">0.0</span>,<span class="float">0.3</span>,<span class="float">1.0</span>] :remote config colorToFade b :remote config colorFadeRate <span class="float">0.5</span> :> traversal</code></pre> </div> </div> </div> </div> </section> <div class="imageblock"> <div class="content"> <img src="../images/gephi-traversal-config.png" alt="gephi traversal config" width="400"> </div> </div> <div class="paragraph"> <p>The visualization configuration above starts with a blue color now (most recently visited), fading the blue color (so that dark green remains on oldest visited), and fading the blue color more quickly so that the gradient from dark green to blue across steps has higher contrast. The following table provides a more detailed description of the Gephi plugin configuration parameters as accepted via the <code>:remote config</code> command:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Parameter</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">workspace</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The name of the workspace that your Graph Streaming server is started for.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">workspace1</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">host</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The host URL where the Graph Streaming server is configured for.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The port number of the URL that the Graph Streaming server is listening on.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8080</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">sizeDecrementRate</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The rate at which the size of an element decreases on each step of the visualization.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0.33</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">stepDelay</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The amount of time in milliseconds to pause between step visualizations.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">startRGBColor</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A size 3 float array of RGB color values which define the starting color to update most recently visited nodes with.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">[0.0,1.0,0.5]</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">startSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The size an element should be when it is most recently visited.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">20</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">colorToFade</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A single char from the set <code>{r,g,b,R,G,B}</code> determining which color to fade for vertices visited in prior steps</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">g</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">colorFadeRate</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A float value in the range <code>(0.0,1.0]</code> which is multiplied against the current <code>colorToFade</code> value for prior vertices; a <code>1.0</code> value effectively turns off the color fading of prior step visited vertices</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0.7</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">visualTraversal</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Creates a <code>TraversalSource</code> variable in the Console named <code>vg</code> which can be used for visualizing traversals. This configuration option takes two parameters. The first is required and is the name of the <code>Graph</code> instance variable that will generate the <code>TraversalSource</code>. The second parameter is the variable name that the <code>TraversalSource</code> should have when referenced in the Console. If left unspecified, this value defaults to <code>vg</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">vg</p></td> </tr> </tbody> </table> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> This plugin is typically only useful to the Gremlin Console and is enabled in the there by default. </td> </tr> </table> </div> <div class="paragraph"> <p>The instructions above assume that the <code>Graph</code> instance being visualized is local to the Gremlin Console. It makes that assumption because the Gephi plugin requires a locally held <code>Graph</code>. If the intent is to visualize a <code>Graph</code> instance hosted in Gremlin Server or a TinkerPop-enabled graph that can only be connected to in a "remote" fashion, then it is still possible to use the Gephi plugin, but the requirement for a locally held <code>Graph</code> remains the same. To use the Gephi plugin in these situations simply use <a href="#subgraph-step">subgraph()-step</a> to extract the portion of the remote graph that will be visualized. Use of that step will return a <code>TinkerGraph</code> instance to the Gremlin Console at which point it can be used locally with the Gephi plugin. The following example demonstrates the general steps:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> :remote connect tinkerpop.server conf/remote-objects.yaml <span class="invisible">//</span><b class="conum">1</b> ... gremlin> :> g.E().hasLabel('knows').subgraph('subGraph').cap('subGraph') <span class="invisible">//</span><b class="conum">2</b> ... gremlin> graph = result[0].object <span class="invisible">//</span><b class="conum">3</b> ...</code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Be sure to connect with a serializer configured to return objects and not their <code>toString()</code> representation which is discussed in more detail in the <a href="#connecting-via-console">Connecting Via Console</a> Section.</p> </li> <li> <p>Use the <code>:></code> command to subgraph the remote graph as needed.</p> </li> <li> <p>The <code>TinkerGraph</code> of that previous traversal can be found in the <code>result</code> object and now that the <code>Graph</code> is local to Gremlin Console it can be used with Gephi as shown in the prior instruction set.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="graph-plugins">Graph Plugins</h3> <div class="paragraph"> <p>This section does not refer to a specific Gremlin Plugin, but a class of them. Graph Plugins are typically created by graph providers to make it easy to integrate their graph systems into Gremlin Console and Gremlin Server. As TinkerPop provides two reference <code>Graph</code> implementations in <a href="#tinkergraph-gremlin">TinkerGraph</a> and <a href="#neo4j-gremlin">Neo4j</a>, there is also one Gremlin Plugin for each of them.</p> </div> <div class="paragraph"> <p>The TinkerGraph plugin is installed and activated in the Gremlin Console by default and the sample configurations that are supplied with the Gremlin Server distribution include the <code>TinkerGraphGremlinPlugin</code> as part of the default setup. If using Neo4j, however, the plugin must be installed manually. Instructions for doing so can be found in the <a href="#neo4j-gremlin">Neo4j</a> section.</p> </div> </div> <div class="sect2"> <h3 id="hadoop-plugin">Hadoop Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/hadoop-logo-notext.png" alt="hadoop logo notext" width="100"></span> The Hadoop Plugin installs as part of <code>hadoop-gremlin</code> and provides a number of imports and utility functions to the environment within which it is used. Those classes and functions provide the basis for supporting <a href="#graphcomputer">OLAP based traversals</a> with Gremlin. This plugin is defined in greater detail in the <a href="#hadoop-gremlin">Hadoop-Gremlin</a> section.</p> </div> </div> <div class="sect2"> <h3 id="server-plugin">Server Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-server.png" alt="gremlin server" width="200"></span> <a href="#gremlin-server">Gremlin Server</a> remotely executes Gremlin scripts that are submitted to it. The Server Plugin provides a way to submit scripts to Gremlin Server for remote processing. Read more about the plugin and how it works in the Gremlin Server section on <a href="#connecting-via-console">Connecting via Console</a>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> This plugin is typically only useful to the Gremlin Console and is enabled in the there by default. </td> </tr> </table> </div> <div class="paragraph"> <p>The Server Plugin for remoting with the Gremlin Console should not be confused with a plugin of similar name that is used by the server. <code>GremlinServerGremlinPlugin</code> is typically only configured in Gremlin Server and provides a number of imports that are required for writing <a href="#starting-gremlin-server">initialization scripts</a>.</p> </div> </div> <div class="sect2"> <h3 id="spark-plugin">Spark Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/spark-logo.png" alt="spark logo" width="175"></span> The Spark Plugin installs as part of <code>spark-gremlin</code> and provides a number of imports and utility functions to the environment within which it is used. Those classes and functions provide the basis for supporting <a href="#graphcomputer">OLAP based traversals</a> using <a href="http://spark.apache.org">Spark</a>. This plugin is defined in greater detail in the <a href="#sparkgraphcomputer">SparkGraphComputer</a> section and is typically installed in conjuction with the <a href="#hadoop-plugin">Hadoop-Plugin</a>.</p> </div> </div> <div class="sect2"> <h3 id="sugar-plugin">Sugar Plugin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-sugar.png" alt="gremlin sugar" width="120"></span> In previous versions of Gremlin-Groovy, there were numerous <a href="http://en.wikipedia.org/wiki/Syntactic_sugar">syntactic sugars</a> that users could rely on to make their traversals more succinct. Unfortunately, many of these conventions made use of <a href="http://docs.oracle.com/javase/tutorial/reflect/">Java reflection</a> and thus, were not performant. In TinkerPop, these conveniences have been removed in support of the standard Gremlin-Groovy syntax being both inline with Gremlin-Java syntax as well as always being the most performant representation. However, for those users that would like to use the previous syntactic sugars (as well as new ones), there is <code>SugarGremlinPlugin</code> (a.k.a Gremlin-Groovy-Sugar).</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> It is important that the sugar plugin is loaded in a Gremlin Console session prior to any manipulations of the respective TinkerPop objects as Groovy will cache unavailable methods and properties. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :plugin use tinkerpop.sugar ==>tinkerpop.sugar activated</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When using Sugar in a Groovy class file, add <code>static { SugarLoader.load() }</code> to the head of the file. Note that <code>SugarLoader.load()</code> will automatically call <code>GremlinLoader.load()</code>. </td> </tr> </table> </div> <div class="sect3"> <h4 id="_graph_traversal_methods">Graph Traversal Methods</h4> <div class="paragraph"> <p>If a <code>GraphTraversal</code> property is unknown and there is a corresponding method with said name off of <code>GraphTraversal</code> then the property is assumed to be a method call. This enables the user to omit <code>( )</code> from the method name. However, if the property does not reference a <code>GraphTraversal</code> method, then it is assumed to be a call to <code>values(property)</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-33" type="radio" name="radio-set-1729797129-33" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-33" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-34" type="radio" name="radio-set-1729797129-33" class="tab-selector-2" /> <label for="tab-1729797129-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> g.V <span class="comment">//</span>// <b class="conum">(1)</b> ==>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> g.V.name <span class="comment">//</span>// <b class="conum">(2)</b> ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V.outE.weight <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="float">0.4</span> ==><span class="float">0.5</span> ==><span class="float">1.0</span> ==><span class="float">1.0</span> ==><span class="float">0.4</span> ==><span class="float">0.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 <span class="comment">//</span>// <b class="conum">(1)</b> g.V.name <span class="comment">//</span>// <b class="conum">(2)</b> g.V.outE.weight <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>There is no need for the parentheses in <code>g.V()</code>.</p> </li> <li> <p>The traversal is interpreted as <code>g.V().values('name')</code>.</p> </li> <li> <p>A chain of zero-argument step calls with a property value call.</p> </li> </ol> </div> </div> <div class="sect3"> <h4 id="_range_queries">Range Queries</h4> <div class="paragraph"> <p>The <code>[x]</code> and <code>[x..y]</code> range operators in Groovy translate to <code>RangeStep</code> calls.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-35" type="radio" name="radio-set-1729797129-35" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-35" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-36" type="radio" name="radio-set-1729797129-35" class="tab-selector-2" /> <label for="tab-1729797129-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> g.V[<span class="integer">0</span>..<span class="integer">2</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] gremlin> g.V[<span class="integer">0</span>..<<span class="integer">2</span>] ==>v[<span class="integer">1</span>] gremlin> g.V[<span class="integer">2</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">0</span>..<span class="integer">2</span>] g.V[<span class="integer">0</span>..<<span class="integer">2</span>] g.V[<span class="integer">2</span>]</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_logical_operators">Logical Operators</h4> <div class="paragraph"> <p>The <code>&</code> and <code>|</code> operator are overloaded in <code>SugarGremlinPlugin</code>. When used, they introduce the <code>AndStep</code> and <code>OrStep</code> markers into the traversal. See <a href="#and-step"><code>and()</code></a> and <a href="#or-step"><code>or()</code></a> for more information.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-37" type="radio" name="radio-set-1729797129-37" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-37" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-38" type="radio" name="radio-set-1729797129-37" class="tab-selector-2" /> <label for="tab-1729797129-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> g.V.where(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) & outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).name <span class="comment">//</span>// <b class="conum">(1)</b> ==>marko gremlin> t = g.V.where(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) | inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).name; <span class="predefined-constant">null</span> <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="predefined-constant">null</span> gremlin> t.toString() ==>[GraphStep(vertex,<span class="type">[]</span>), TraversalFilterStep([VertexStep(OUT,[knows],edge), OrStep, VertexStep(IN,[created],edge)]), PropertiesStep([name],value)] gremlin> t ==>marko ==>lop ==>ripple gremlin> t.toString() ==>[TinkerGraphStep(vertex,<span class="type">[]</span>), OrStep([[VertexStep(OUT,[knows],edge)], [VertexStep(IN,[created],edge)]]), PropertiesStep([name],value)]</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(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) & outE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).name <span class="comment">//</span>// <b class="conum">(1)</b> t = g.V.where(outE(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>) | inE(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>)).name; <span class="predefined-constant">null</span> <span class="comment">//</span>// <b class="conum">(2)</b> t.toString() t t.toString()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Introducing the <code>AndStep</code> with the <code>&</code> operator.</p> </li> <li> <p>Introducing the <code>OrStep</code> with the <code>|</code> operator.</p> </li> </ol> </div> </div> <div class="sect3"> <h4 id="_traverser_methods">Traverser Methods</h4> <div class="paragraph"> <p>It is rare that a user will ever interact with a <code>Traverser</code> directly. However, if they do, some method redirects exist to make it easy.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-39" type="radio" name="radio-set-1729797129-39" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-39" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-40" type="radio" name="radio-set-1729797129-39" class="tab-selector-2" /> <label for="tab-1729797129-40" 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> g.V().map{<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)} <span class="comment">// conventional</span> ==>marko ==>vadas ==>lop ==>josh ==>ripple ==>peter gremlin> g.V.map{<span class="local-variable">it</span>.name} <span class="comment">// sugar</span> ==>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().map{<span class="local-variable">it</span>.get().value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)} <span class="comment">// conventional</span> g.V.map{<span class="local-variable">it</span>.name} <span class="comment">// sugar</span></code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect2"> <h3 id="utilities-plugin">Utilities Plugin</h3> <div class="paragraph"> <p>The Utilities Plugin provides various functions, helper methods and imports of external classes that are useful in the console.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The Utilities Plugin is enabled in the Gremlin Console by default. </td> </tr> </table> </div> <div class="sect3"> <h4 id="describe-graph">Describe Graph</h4> <div class="paragraph"> <p>A good implementation of the Gremlin APIs will validate their features against the <a href="../dev/provider/#validating-with-gremlin-test">Gremlin test suite</a>. To learn more about a specific implementation’s compliance with the test suite, use the <code>describeGraph</code> function. The following shows the output for <code>HadoopGraph</code>:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797129-41" type="radio" name="radio-set-1729797129-41" class="tab-selector-1" checked="checked" /> <label for="tab-1729797129-41" class="tab-label-1">console (groovy)</label> <input id="tab-1729797129-42" type="radio" name="radio-set-1729797129-41" class="tab-selector-2" /> <label for="tab-1729797129-42" 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> describeGraph(HadoopGraph) ==> IMPLEMENTATION - org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph TINKERPOP TEST SUITE - Compliant with (<span class="integer">5</span> of <span class="integer">4</span> suites) - Compliant with (<span class="integer">5</span> of <span class="integer">11</span> suites) > org.apache.tinkerpop.gremlin.structure.StructureStandardSuite > org.apache.tinkerpop.gremlin.process.ProcessStandardSuite > org.apache.tinkerpop.gremlin.process.ProcessComputerSuite > org.apache.tinkerpop.gremlin.process.ProcessLimitedStandardSuite > org.apache.tinkerpop.gremlin.process.ProcessLimitedComputerSuite - Opts out of <span class="integer">22</span> individual tests > org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_matchXa_hasXname_GarciaX__a_0writtenBy_b__a_0sungBy_bX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_matchXa_0sungBy_b__a_0sungBy_c__b_writtenBy_d__c_writtenBy_e__d_hasXname_George_HarisonX__e_hasXname_Bob_MarleyXX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_matchXa_0sungBy_b__a_0writtenBy_c__b_writtenBy_d__c_sungBy_d__d_hasXname_GarciaXX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_matchXa_0sungBy_b__a_0writtenBy_c__b_writtenBy_dX_whereXc_sungBy_dX_whereXd_hasXname_GarciaXX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_both_both_count <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_repeatXoutX_timesX3X_count <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_repeatXoutX_timesX8X_count <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest<span class="error">$</span>Traversals<span class="error">#</span>g_V_repeatXoutX_timesX5X_asXaX_outXwrittenByX_asXbX_selectXa_bX_count <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest<span class="error">$</span>Traversals<span class="error">#</span>grateful_V_out_out_profile <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest<span class="error">$</span>Traversals<span class="error">#</span>grateful_V_out_out_profileXmetricsX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest<span class="error">#</span>g_V_hasLabelXsongX_groupXaX_byXnameX_byXproperties_groupCount_byXlabelXX_out_capXaX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest<span class="error">#</span>g_V_outXfollowedByX_group_byXsongTypeX_byXbothE_group_byXlabelX_byXweight_sumXX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest<span class="error">#</span>g_V_repeatXbothXfollowedByXX_timesX2X_group_byXsongTypeX_byXcountX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest<span class="error">#</span>g_V_repeatXbothXfollowedByXX_timesX2X_groupXaX_byXsongTypeX_byXcountX_capXaX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.computer.GraphComputerTest<span class="error">#</span>shouldStartAndEndWorkersForVertexProgramAndMapReduce <span class="string"><span class="delimiter">"</span><span class="content">Spark executes map and combine in a lazy fashion and thus, fails the blocking aspect of this test</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest<span class="error">#</span>* <span class="string"><span class="delimiter">"</span><span class="content">The interruption model in the test can't guarantee interruption at the right time with HadoopGraph.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionComputerTest<span class="error">#</span>* <span class="string"><span class="delimiter">"</span><span class="content">This test makes use of a sideEffect to enforce when a thread interruption is triggered and thus isn't applicable to HadoopGraph</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest<span class="error">$</span>CountMatchTraversals<span class="error">#</span>g_V_matchXa_followedBy_count_isXgtX10XX_b__a_0followedBy_count_isXgtX10XX_bX_count <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin is OLAP-oriented and for OLTP operations, linear-scan joins are required. This particular tests takes many minutes to execute.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest<span class="error">$</span>Traversals<span class="error">#</span>g_io_readXxmlX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin does not support reads/writes with GraphML.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest<span class="error">$</span>Traversals<span class="error">#</span>g_io_read_withXreader_graphmlX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin does not support reads/writes with GraphML.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteTest<span class="error">$</span>Traversals<span class="error">#</span>g_io_writeXxmlX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin does not support reads/writes with GraphML.</span><span class="delimiter">"</span></span> > org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteTest<span class="error">$</span>Traversals<span class="error">#</span>g_io_write_withXwriter_graphmlX <span class="string"><span class="delimiter">"</span><span class="content">Hadoop-Gremlin does not support reads/writes with GraphML.</span><span class="delimiter">"</span></span> - NOTE - The describeGraph() function shows information about a Graph implementation. It uses information found <span class="keyword">in</span> Java Annotations on the implementation itself to determine <span class="local-variable">this</span> output and does not assess the actual code of the test cases of the implementation itself. Compliant implementations will faithfully and honestly supply these Annotations to provide the most accurate depiction of their support.</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">describeGraph(HadoopGraph)</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p><a id="gremlin-variants"></a></p> </div> </div> </div> </div> </div> <h1 id="gremlin-drivers-variants" class="sect0">Gremlin Drivers and Variants</h1> <div class="openblock partintro"> <div class="content"> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-house-of-mirrors.png" alt="gremlin house of mirrors" width="1024"> </div> </div> <div class="paragraph"> <p>At this point, readers should be well familiar with the <a href="#intro">Introduction</a> to this Reference Documentation and will likely be thinking about implementation details specific to the graph provider they have selected as well as the programming language they intend to use. The choice of programming language could have implications to the architecture and design of the application and the choice itself may have limits imposed upon it by the chosen graph provider. For example, a <a href="#connecting-rgp">Remote Gremlin Provider</a> will require the selection of a driver to interact with it. On the other hand, a graph system that is designed for embedded use, like TinkerGraph, needs the Java Virtual Machine (JVM) environment which is easily accessed with a JVM programming language. If however the programming language is not built for the JVM then it will require <a href="#connecting-gremlin-server">Gremlin Server</a> in the architecture as well.</p> </div> <div class="paragraph"> <p>TinkerPop provides an array of drivers in different programming languages as a way to connect to a remote Gremlin Server or Remote Gremlin Provider. Drivers allow the developer to make requests to that remote system and get back results from the TinkerPop-enabled graphs hosted within. A driver can submit Gremlin strings and Gremlin bytecode over this sub-protocol. Gremlin strings are written in the scripting language made available by the remote system that the driver is connecting to (typically, Groovy-based). This connection approach is quite similar to what developers are likely familiar with when using JDBC and SQL.</p> </div> <div class="paragraph"> <p>The preferred approach is to use bytecode-based requests, which essentially allows the ability to craft Gremlin directly in the programming language of choice. As Gremlin makes use of two fundamental programming constructs: <a href="https://en.wikipedia.org/wiki/Function_composition">function composition</a> and <a href="https://en.wikipedia.org/wiki/Nested_function">function nesting</a>, it is possible to embed the Gremlin language in any modern programming language. It is a far more natural way to program, because it enables IDE interaction, compile time checks, and language level checks that can help prevent errors prior to execution. The differences between these two approaches were outlined in the <a href="#connecting-via-drivers">Connecting Via Drivers</a> Section, which applies to Gremlin Server, but also to Remote Gremlin Providers.</p> </div> <div class="paragraph"> <p>In addition to the languages and drivers that TinkerPop supports, there are also third-party implementations, as well as extensions to the Gremlin language that might be specific to a particular graph provider. That listing can be found on the TinkerPop <a href="https://tinkerpop.apache.org/#graph-systems">home page</a>. Their description is beyond the scope of this documentation.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When possible, it is typically best to align the version of TinkerPop used on the client with the version supported on the server. While it is not impossible to have a different version between client and server, it may require additional configuration and/or a deeper knowledge of that changes introduced between versions. It’s simply safer to avoid the conflict, when allowed to do so. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Gremlin-Java is the canonical representation of Gremlin and any (proper) Gremlin language variant will emulate its structure as best as possible given the constructs of the host language. A strong correspondence between variants ensures that the general Gremlin reference documentation is applicable to all variants and that users moving between development languages can easily adopt the Gremlin variant for that language. </td> </tr> </table> </div> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-variant-architecture.png" alt="gremlin variant architecture" width="650"> </div> </div> <div class="paragraph"> <p>The following sections describe each language variant and driver that is officially TinkerPop a part of the project, providing more detailed information about usage, configuration and known limitations.</p> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-go">Gremlin-Go</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-go.png" alt="gremlin go" width="130"></span> Apache TinkerPop’s Gremlin-Go implements Gremlin within the <a href="https://go.dev/">Go</a> language and can therefore be used on different operating systems. Go’s syntax has the similar constructs as Java including "dot notation" for function chaining (<code>a.b.c</code>) and round bracket function arguments (<code>a(b,c)</code>). Something unlike Java is that Gremlin-Go requires a <code>gremlingo</code> prefix when using the namespace (<code>a(b())</code> vs <code>gremlingo.a(gremlingo.T__.b())</code>). Anyone familiar with Gremlin-Java will be able to work with Gremlin-Go with relative ease. Moreover, there are a few added constructs to Gremlin-Go that make traversals a bit more succinct.</p> </div> <div class="paragraph"> <p>To install the Gremlin-Go as a dependency for your project, run the following in the root directory of your project that contains your <code>go.mod</code> file:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">go get github.com/apache/tinkerpop/gremlin-go/v3[optionally append @<version>, such as @v3.5.3]</code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-go-connecting">Connecting</h3> <div class="paragraph"> <p>The pattern for connecting is described in <a href="#connecting-gremlin">Connecting Gremlin</a> and it basically distills down to creating a <code>GraphTraversalSource</code>. A <code>GraphTraversalSource</code> is created from the anonymous <code>Traversal_()</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">remote, err := gremlingo.NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) g := gremlingo.Traversal_().WithRemote(remote)</code></pre> </div> </div> <div class="paragraph"> <p>If you need to additional parameters to connection setup, you can pass in a configuration function.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">remote, err := gremlingo.NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>, <span class="keyword">func</span>(settings *DriverRemoteConnectionSettings) { settings.TraversalSource = <span class="string"><span class="delimiter">"</span><span class="content">gmodern</span><span class="delimiter">"</span></span> })</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin-go supports plain text authentication. It can be set in the connection function.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">remote, err := gremlingo.NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>, <span class="keyword">func</span>(settings *DriverRemoteConnectionSettings) { settings.TlsConfig = &tls.Config{InsecureSkipVerify: <span class="predefined-constant">true</span>} settings.AuthInfo = gremlingo.BasicAuthInfo(<span class="string"><span class="delimiter">"</span><span class="content">login</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">password</span><span class="delimiter">"</span></span>) })</code></pre> </div> </div> <div class="paragraph"> <p>If you authenticate to a remote <a href="#connecting-gremlin-server">Gremlin Server</a> or <a href="#connecting-rgp">Remote Gremlin Provider</a>, this server normally has SSL activated and the websockets url will start with 'wss://'.</p> </div> <div class="paragraph"> <p>Some connection options can also be set on individual requests made through the using <code>With()</code> step on the <code>TraversalSource</code>. For instance to set request timeout to 500 milliseconds:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">results, err := g.With(<span class="string"><span class="delimiter">"</span><span class="content">evaluationTimeout</span><span class="delimiter">"</span></span>, <span class="integer">500</span>).V().Out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).ToList()</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code> and <code>evaluationTimeout</code>.</p> </div> <div class="paragraph"> <p><a id="go-imports"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-go-imports">Common Imports</h3> <div class="paragraph"> <p>There are a number of classes, functions and tokens that are typically used with Gremlin. The following import provide most of the typical functionality required to use Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="keyword">import</span> ( <span class="string"><span class="delimiter">"</span><span class="content">github.com/apache/tinkerpop/gremlin-go/driver</span><span class="delimiter">"</span></span> )</code></pre> </div> </div> <div class="paragraph"> <p>These can be used analogously to how they are used in Gremlin-Java.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">results, err := 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">age</span><span class="delimiter">"</span></span>, gremlingo.T__.Is(gremlingo.P.Gt(<span class="integer">30</span>))).Order().By(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>, gremlingo.Desc).ToList() [v[<span class="integer">6</span>], v[<span class="integer">4</span>]]</code></pre> </div> </div> <div class="paragraph"> <p><a id="go-configuration"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-go-configuration">Configuration</h3> <div class="paragraph"> <p>The following table describes the various configuration options for the Gremlin-go Driver. They can be passed to the <code>NewClient</code> or <code>NewDriverRemoteConnection</code> functions as configuration function arguments:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TraversalSource</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Traversal source.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">"g"</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TransporterType</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Transporter type.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">Gorilla</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">LogVerbosity</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Log verbosity.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">gremlingo.INFO</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Logger</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Instance of logger.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">log</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Language</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Language used for logging messages.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">language.English</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">AuthInfo</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Authentification info, can be build with BasicAuthInfo() or HeaderAuthInfo().</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">empty</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TlsConfig</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">TLS configuration.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">empty</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">KeepAliveInterval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Keep connection alive interval.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">5 seconds</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">WriteDeadline</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Write deadline.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">3 seconds</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ConnectionTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Timeout for establishing connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">45 seconds</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">NewConnectionThreshold</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Minimum amount of concurrent active traversals on a connection to trigger creation of a new connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">MaximumConcurrentConnections</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of concurrent connections.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">number of runtime processors</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">EnableCompression</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Flag to enable compression.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ReadBufferSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Specify I/O buffer sizes in bytes. If a buffer size is zero, then a useful default size is used</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">WriteBufferSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Specify I/O buffer sizes in bytes. If a buffer size is zero, then a useful default size is used</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">0</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">Session</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Session ID.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">""</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">EnableUserAgentOnConnect</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables sending a user agent to the server during connection requests. More details can be found in provider docs <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_graph_driver_provider_requirements">here</a>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> </tbody> </table> </div> <div class="sect2"> <h3 id="gremlin-go-strategies">Traversal Strategies</h3> <div class="paragraph"> <p>In order to add and remove <a href="#traversalstrategy">traversal strategies</a> from a traversal source, Gremlin-Go has a <code>TraversalStrategy</code> interface along with a collection of functions that mirror the standard Gremlin-Java strategies.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">promise := g.WithStrategies(gremlingo.ReadOnlyStrategy()).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">foo</span><span class="delimiter">"</span></span>).Iterate()</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Many of the <code>TraversalStrategy</code> classes in Gremlin-Go are proxies to the respective strategy on Apache TinkerPop’s JVM-based Gremlin traversal machine. As such, their <code>apply(Traversal)</code> method does nothing. However, the strategy is encoded in the Gremlin-Go bytecode and transmitted to the Gremlin traversal machine for re-construction machine-side. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-go-transactions">Transactions</h3> <div class="paragraph"> <p>To get a full understanding of this section, it would be good to start by reading the <a href="#transactions">Transactions</a> section of this documentation, which discusses transactions in the general context of TinkerPop itself. This section builds on that content by demonstrating the transactional syntax for Go.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">remote, err := NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) g := gremlingo.Traversal_().WithRemote(remote) <span class="comment">// Create a Transaction.</span> tx := g.Tx() <span class="comment">// Spawn a new GraphTraversalSource, binding all traversals established from it to tx.</span> gtx, _ := tx.Begin() <span class="comment">// Execute a traversal within the transaction.</span> promise := 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">Lyndon</span><span class="delimiter">"</span></span>).Iterate() err := <-promise <span class="keyword">if</span> err != <span class="predefined-constant">nil</span> { <span class="comment">// Rollback the transaction if an error occurs.</span> tx.rollback() } <span class="keyword">else</span> { <span class="comment">// Commit the transaction. The transaction can no longer be used and cannot be re-used.</span> <span class="comment">// A new transaction can be spawned through g.Tx().</span> tx.Commit() }</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-go-lambda">The Lambda Solution</h3> <div class="paragraph"> <p>Supporting <a href="https://en.wikipedia.org/wiki/Anonymous_function">anonymous functions</a> across languages is difficult as most languages do not support lambda introspection and thus, code analysis. In Gremlin-Go, a Gremlin lambda should be represented as a zero-arg callable that returns a string representation of the lambda expected for use in the traversal. The lambda should be written as a <code>Gremlin-Groovy</code> string. When the lambda is represented in <code>Bytecode</code> its language is encoded such that the remote connection host can infer which translator and ultimate execution engine to use.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">r, err := g.V().Out().Map(&gremlingo.Lambda{Script: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>, Language: <span class="string"><span class="delimiter">"</span><span class="delimiter">"</span></span>}).Sum().ToList()</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When running into situations where Groovy cannot properly discern a method signature based on the <code>Lambda</code> instance created, it will help to fully define the closure in the lambda expression - so rather than <code>Script: "it.get().value('name')", Language: "gremlin-groovy"</code>, prefer <code>Script: "x → x.get().value('name')", Language: "gremlin-groovy"</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>Finally, Gremlin <code>Bytecode</code> that includes lambdas requires that the traversal be processed by the <code>ScriptEngine</code>. To avoid continued recompilation costs, it supports the encoding of bindings, which allow a remote engine to to cache traversals that will be reused over and over again save that some parameterization may change. Thus, instead of translating, compiling, and then executing each submitted bytecode, it is possible to simply execute.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">r, err := g.V((&gremlingo.Bindings{}).Of(<span class="string"><span class="delimiter">"</span><span class="content">x</span><span class="delimiter">"</span></span>, <span class="integer">1</span>)).Out(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).Map(&gremlingo.Lambda{Script: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>, Language: <span class="string"><span class="delimiter">"</span><span class="delimiter">"</span></span>}).Sum().ToList() <span class="comment">// 3</span> r, err := g.V((&gremlingo.Bindings{}).Of(<span class="string"><span class="delimiter">"</span><span class="content">x</span><span class="delimiter">"</span></span>, <span class="integer">4</span>)).Out(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).Map(&gremlingo.Lambda{Script: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>, Language: <span class="string"><span class="delimiter">"</span><span class="delimiter">"</span></span>}).Sum().ToList() <span class="comment">// 9</span></code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> As explained throughout the documentation, when possible <a href="#a-note-on-lambdas">avoid</a> lambdas. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-go-scripts">Submitting Scripts</h3> <div class="paragraph"> <p>The <code>Client</code> class implementation/interface is based on the Java Driver, with some restrictions. Most notably, Gremlin-go does not yet implement the <code>Cluster</code> class. Instead, <code>Client</code> is instantiated directly. Usage is as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="keyword">import</span> <span class="string"><span class="delimiter">"</span><span class="content">github.com/apache/tinkerpop/gremlin-go/v3/driver</span><span class="delimiter">"</span></span> <span class="invisible">//</span><b class="conum">1</b> client, err := gremlingo.NewClient(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Import the Gremlin-Go module.</p> </li> <li> <p>Opens a reference to <code>localhost</code> - note that there are various configuration options that can be passed to the <code>Client</code> object upon instantiation as keyword arguments.</p> </li> </ol> </div> <div class="paragraph"> <p>Once a <code>Client</code> instance is ready, it is possible to issue some Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">resultSet, err := client.Submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().count()</span><span class="delimiter">"</span></span>) <span class="invisible">//</span><b class="conum">1</b> result, err := resultSet.All() <span class="invisible">//</span><b class="conum">2</b> fmt.Println(result[<span class="integer">0</span>].GetString()) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Submit a script that simply returns a Count of vertexes.</p> </li> <li> <p>Get results from resultSet. Block until the script is evaluated and results are sent back by the server.</p> </li> <li> <p>Use the result.</p> </li> </ol> </div> <div class="sect3"> <h4 id="_per_request_settings">Per Request Settings</h4> <div class="paragraph"> <p>Both the <code>Client</code> and <code>DriverRemoteConnection</code> types have a <code>SubmitWithOptions(traversalString, requestOptions)</code> variant of the standard <code>Submit()</code> method. These methods allow a <code>RequestOptions</code> struct to be passed in which will augment the execution on the server. <code>RequestOptions</code> can be constructed using <code>RequestOptionsBuilder</code>. A good use-case for this feature is to set a per-request override to the <code>evaluationTimeout</code> so that it only applies to the current request.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">options := <span class="predefined">new</span>(RequestOptionsBuilder). SetEvaluationTimeout(<span class="integer">5000</span>). SetBatchSize(<span class="integer">32</span>). SetMaterializeProperties(<span class="string"><span class="delimiter">"</span><span class="content">tokens</span><span class="delimiter">"</span></span>). AddBinding(<span class="string"><span class="delimiter">"</span><span class="content">x</span><span class="delimiter">"</span></span>, <span class="integer">100</span>). Create() resultSet, err := client.SubmitWithOptions(<span class="string"><span class="delimiter">"</span><span class="content">g.V(x).count()</span><span class="delimiter">"</span></span>, options)</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code>, <code>evaluationTimeout</code> and <code>materializeProperties</code>. <code>RequestOptions</code> may also contain a map of variable <code>bindings</code> to be applied to the supplied traversal string.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try <code>g.with("evaluationTimeout", 500)</code> within a script. Scripts with multiple traversals and multiple timeouts will be interpreted as a sum of all timeouts identified in the script for that request. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go">resultSet, err := client.SubmitWithOptions(<span class="string"><span class="delimiter">"</span><span class="content">g.with('evaluationTimeout', 500).addV().iterate();</span><span class="delimiter">"</span></span>+ <span class="string"><span class="delimiter">"</span><span class="content">g.addV().iterate();</span><span class="delimiter">"</span></span>+ <span class="string"><span class="delimiter">"</span><span class="content">g.with('evaluationTimeout', 500).addV();</span><span class="delimiter">"</span></span>, <span class="predefined">new</span>(RequestOptionsBuilder).SetEvaluationTimeout(<span class="integer">500</span>).Create()) results, err := resultSet.All()</code></pre> </div> </div> <div class="paragraph"> <p>In the above example, defines a timeout of 500 milliseconds, but the script has three traversals with two internal settings for the timeout using <code>with()</code>. The request timeout used by the server will therefore be 1000 milliseconds (overriding the 500 which itself was an override for whatever configuration was on the server).</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-go-dsl">Domain Specific Languages</h3> <div class="paragraph"> <p>Writing a Gremlin <a href="#dsl">Domain Specific Language</a> (DSL) in Go requires embedding of several structs and interfaces:</p> </div> <div class="ulist"> <ul> <li> <p><code>GraphTraversal</code> - which exposes the various steps used in traversal writing</p> </li> <li> <p><code>GraphTraversalSource</code> - which spawns <code>GraphTraversal</code> instances</p> </li> <li> <p><code>AnonymousTraversal</code> - which spawns anonymous traversals from steps</p> </li> </ul> </div> <div class="paragraph"> <p>The Social DSL based on the <a href="https://tinkerpop.apache.org/docs/3.7.3/images/tinkerpop-modern.png">"modern" toy graph</a> might look like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="comment">// Optional syntactic sugar.</span> <span class="keyword">var</span> __ = gremlingo.T__ <span class="keyword">var</span> P = gremlingo.P <span class="keyword">var</span> gt = gremlingo.P.Gt <span class="comment">// Optional alias for import convenience.</span> <span class="keyword">type</span> GraphTraversal = gremlingo.GraphTraversal <span class="keyword">type</span> GraphTraversalSource = gremlingo.GraphTraversalSource <span class="keyword">type</span> AnonymousTraversal = gremlingo.AnonymousTraversal <span class="comment">// Embed Graph traversal inside custom traversal struct to add custom traversal functions.</span> <span class="comment">// In go, capitalizing the first letter exports (makes public) the struct/method to outside of package, for this example</span> <span class="comment">// we have defined everything package private. In actual usage, please see fit to your application.</span> <span class="keyword">type</span> socialTraversal <span class="keyword">struct</span> { *GraphTraversal } <span class="keyword">func</span> (s *socialTraversal) knows(personName <span class="predefined-type">string</span>) *socialTraversal { <span class="keyword">return</span> &socialTraversal{s.Out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).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>, personName)} } <span class="keyword">func</span> (s *socialTraversal) youngestFriendsAge() *socialTraversal { <span class="keyword">return</span> &socialTraversal{s.Out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).HasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).Values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).Min()} } <span class="keyword">func</span> (s *socialTraversal) createdAtLeast(number <span class="predefined-type">int</span>) *socialTraversal { <span class="keyword">return</span> &socialTraversal{s.OutE(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).Count().Is(gt(number))} } <span class="comment">// Add custom social traversal source to spaw custom traversals.</span> <span class="keyword">type</span> socialTraversalSource <span class="keyword">struct</span> { *GraphTraversalSource } <span class="comment">// Define the source step function by adding steps to the bytecode.</span> <span class="keyword">func</span> (sts *socialTraversalSource) persons(personNames ...<span class="keyword">interface</span>{}) *socialTraversal { t := sts.GetGraphTraversal() t.Bytecode.AddStep(<span class="string"><span class="delimiter">"</span><span class="content">V</span><span class="delimiter">"</span></span>) t.Bytecode.AddStep(<span class="string"><span class="delimiter">"</span><span class="content">hasLabel</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>) <span class="keyword">if</span> personNames != <span class="predefined-constant">nil</span> { t.Bytecode.AddStep(<span class="string"><span class="delimiter">"</span><span class="content">has</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>, P.Within(personNames...)) } <span class="keyword">return</span> &socialTraversal{t} } <span class="comment">// Create the social anonymous traversal interface to embed and extend the anonymous traversal functions.</span> <span class="keyword">type</span> iSocialAnonymousTraversal <span class="keyword">interface</span> { AnonymousTraversal knows(personName <span class="predefined-type">string</span>) *GraphTraversal youngestFriendsAge() *GraphTraversal createdAtLeast(number <span class="predefined-type">int</span>) *GraphTraversal } <span class="comment">// Add the struct to implement the iSocialAnonymousTraversal interface.</span> <span class="keyword">type</span> socialAnonymousTraversal <span class="keyword">struct</span> { AnonymousTraversal socialTraversal <span class="keyword">func</span>() *socialTraversal } <span class="comment">// Add the variable s__ to call anonymous traversal step functions in place of __.</span> <span class="keyword">var</span> s__ iSocialAnonymousTraversal = &socialAnonymousTraversal{ __, <span class="keyword">func</span>() *socialTraversal { <span class="keyword">return</span> &socialTraversal{gremlingo.NewGraphTraversal(<span class="predefined-constant">nil</span>, gremlingo.NewBytecode(<span class="predefined-constant">nil</span>), <span class="predefined-constant">nil</span>)} }, } <span class="comment">// Extended anonymous traversal functions need to return GraphTraversal for serialization purposes</span> <span class="keyword">func</span> (sat *socialAnonymousTraversal) knows(personName <span class="predefined-type">string</span>) *GraphTraversal { <span class="keyword">return</span> sat.socialTraversal().knows(personName).GraphTraversal } <span class="keyword">func</span> (sat *socialAnonymousTraversal) youngestFriendsAge() *GraphTraversal { <span class="keyword">return</span> sat.socialTraversal().youngestFriendsAge().GraphTraversal } <span class="keyword">func</span> (sat *socialAnonymousTraversal) createdAtLeast(number <span class="predefined-type">int</span>) *GraphTraversal { <span class="keyword">return</span> sat.socialTraversal().createdAtLeast(number).GraphTraversal }</code></pre> </div> </div> <div class="paragraph"> <p>Using the DSL requires a social traversal source to be created from the default traversal source:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"><span class="comment">// Creating the driver remote connection as regular.</span> driverRemoteConnection, _ := gremlingo.NewDriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>, <span class="keyword">func</span>(settings *gremlingo.DriverRemoteConnectionSettings) { settings.TraversalSource = <span class="string"><span class="delimiter">"</span><span class="content">gmodern</span><span class="delimiter">"</span></span> }) <span class="keyword">defer</span> driverRemoteConnection.Close() <span class="comment">// Create social traversal source from graph traversal source.</span> social := &socialTraversalSource{gremlingo.Traversal_().WithRemote(driverRemoteConnection)} <span class="comment">// We can now use the social traversal source as well as traversal steps</span> resBool, _ := social.persons(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">stephen</span><span class="delimiter">"</span></span>).knows(<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>).HasNext() fmt.Println(resBool) <span class="comment">// Using the createdAtLeast step.</span> resCreated, _ := social.persons().createdAtLeast(<span class="integer">1</span>).Next() fmt.Println(resCreated.GetString()) <span class="comment">// Using the social anonymous traversal.</span> resAnon, _ := social.persons().Filter(s__.createdAtLeast(<span class="integer">1</span>)).Count().Next() fmt.Println(resAnon.GetString()) <span class="comment">// Note that error handling has been omitted with _ from the above examples.</span></code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-go-differences">Differences</h3> <div class="paragraph"> <p>In situations where Go reserved words and global functions overlap with standard Gremlin steps and tokens, those bits of conflicting Gremlin get an underscore appended as a suffix. In addition, all function names start with a capital letter in order to be public:</p> </div> <div class="paragraph"> <p><strong>Steps</strong> - <a href="#and-step">And()</a>, <a href="#as-step">As()</a>, <a href="#filter-step">Filter()</a>, <a href="#from-step">From()</a>, <a href="#id-step">Id()</a>, <a href="#is-step">Is()</a>, <a href="#in-step">In()</a>, <a href="#max-step">Max()</a>, <a href="#min-step">Min()</a>, <a href="#not-step">Not()</a>, <a href="#or-step">Or()</a>, <a href="#range-step">Range()</a>, <a href="#sum-step">Sum()</a>, <a href="#with-step">With()</a></p> </div> <div class="paragraph"> <p><strong>Tokens</strong> - <a href="#a-note-on-scopes">Scope.Global</a>, <a href="#a-note-on-scopes">Scope.Local</a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-go-aliases">Aliases</h3> <div class="paragraph"> <p>To make the code more readable and close to the Gremlin query language), you can use aliases. These aliases can be named with capital letters to be consistent with non-aliased steps but will result in exported variables which could be problematic if not being used in a top-level program (i.e. not a redistributable package).</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"> <span class="keyword">var</span> __ = gremlingo.T__ <span class="keyword">var</span> gt = gremlingo.P.Gt <span class="keyword">var</span> order = gremlingo.Order results, err := 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">age</span><span class="delimiter">"</span></span>, __.Is(gt(<span class="integer">30</span>))).Order().By(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>, order.Desc).ToList()</code></pre> </div> </div> <div class="sect3"> <h4 id="_list_of_useful_aliases">List of useful aliases</h4> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="go"> <span class="comment">// common</span> <span class="keyword">var</span> __ = gremlingo.T__ <span class="keyword">var</span> TextP = gremlingo.TextP <span class="comment">// predicates</span> <span class="keyword">var</span> between = gremlingo.P.Between <span class="keyword">var</span> eq = gremlingo.P.Eq <span class="keyword">var</span> gt = gremlingo.P.Gt <span class="keyword">var</span> gte = gremlingo.P.Gte <span class="keyword">var</span> inside = gremlingo.P.Inside <span class="keyword">var</span> lt = gremlingo.P.Lt <span class="keyword">var</span> lte = gremlingo.P.Lte <span class="keyword">var</span> neq = gremlingo.P.Neq <span class="keyword">var</span> not = gremlingo.P.Not <span class="keyword">var</span> outside = gremlingo.P.Outside <span class="keyword">var</span> test = gremlingo.P.Test <span class="keyword">var</span> within = gremlingo.P.Within <span class="keyword">var</span> without = gremlingo.P.Without <span class="keyword">var</span> and = gremlingo.P.And <span class="keyword">var</span> or = gremlingo.P.Or <span class="comment">// sorting</span> <span class="keyword">var</span> order = gremlingo.Order</code></pre> </div> </div> <div class="paragraph"> <p>Finally, the enum construct for <code>Cardinality</code> cannot have functions attached to it the way it can be done in Java, therefore cardinality functions that take a value like <code>list()</code>, <code>set()</code>, and <code>single()</code> are referenced from a <code>CardinalityValue</code> class rather than <code>Cardinality</code> itself.</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-go-limitations">Limitations</h3> <div class="ulist"> <ul> <li> <p>There is no default <code>set</code> type in Go. Any set type code from server will be deserialized into slices with the list type implementation. To input a set into Gremlin-Go, a custom struct which implements the <code>gremlingo.Set</code> interface will be serialized as a set. <code>gremlingo.NewSimpleSet</code> is a basic implementation of a set that is provided by Gremlin-Go that can be used to fulfill the <code>gremlingo.Set</code> interface if desired.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="gremlin-go-examples">Application Examples</h3> <div class="paragraph"> <p>The TinkerPop source code contains some sample applications that demonstrate the basics of Gremlin-Go. They can be found in GitHub <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-go/examples/">here</a> and are designed to connect to a running <a href="#gremlin-server">Gremlin Server</a> configured with the <code>conf/gremlin-server.yaml</code> and <code>conf/gremlin-server-modern.yaml</code> files as included with the standard release packaging.</p> </div> <div class="paragraph"> <p>To run the examples, first download an image of Gremlin Server from Docker Hub:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker pull tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The remote connection and basic Gremlin examples can be run on a clean server, which uses the default configuration file <code>conf/gremlin-server.yaml</code>. To start a clean server, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The traversal examples should be run on a server configured to start with the Modern toy graph, using <code>conf/gremlin-server-modern.yaml</code>. To start a server with the Modern graph preloaded, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server conf/gremlin-server-modern.yaml</code></pre> </div> </div> <div class="paragraph"> <p>Each example can now be run with the following commands:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">go run connections.go go run basic_gremlin.go go run modern_traversals.go</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-groovy">Gremlin-Groovy</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-groovy-drawing.png" alt="gremlin groovy drawing" width="130"></span> Apache TinkerPop’s Gremlin-Groovy implements Gremlin within the <a href="http://groovy.apache.org">Apache Groovy</a> language. As a JVM-based language variant, Gremlin-Groovy is backed by <a href="#gremlin-java">Gremlin-Java</a> constructs. Moreover, given its scripting nature, Gremlin-Groovy serves as the language of <a href="#gremlin-console">Gremlin Console</a> and <a href="#gremlin-server">Gremlin Server</a>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">compile <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">org.apache.tinkerpop</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">gremlin-core</span><span class="delimiter">'</span></span>, <span class="key">version</span>: <span class="string"><span class="delimiter">'</span><span class="content">3.7.3</span><span class="delimiter">'</span></span> compile <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">org.apache.tinkerpop</span><span class="delimiter">'</span></span>, <span class="key">name</span>: <span class="string"><span class="delimiter">'</span><span class="content">gremlin-driver</span><span class="delimiter">'</span></span>, <span class="key">version</span>: <span class="string"><span class="delimiter">'</span><span class="content">3.7.3</span><span class="delimiter">'</span></span></code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-groovy-differences">Differences</h3> <div class="paragraph"> <p>In Groovy, <code>as</code>, <code>in</code>, and <code>not</code> are reserved words. Gremlin-Groovy does not allow these steps to be called statically from the anonymous traversal <code>__</code> and therefore, must always be prefixed with <code>__.</code> For instance: <code>g.V().as('a').in().as('b').where(__.not(__.as('a').out().as('b')))</code></p> </div> <div class="paragraph"> <p>Care needs to be taken when using the <code>any(P)</code> step as you may accidentally invoke Groovy’s <code>any(Closure)</code> method. This typically happens when calling <code>any()</code> without arguments. You can tell if Groovy’s <code>any</code> has been called if the return value is a boolean.</p> </div> <div class="paragraph"> <p>Since Groovy has access to the full JVM as Java does, it is possible to construct <code>Date</code>-like objects directly, but the Gremlin language does offer a <code>datetime()</code> function that is exposed in the Gremlin Console and as a function for Gremlin scripts sent to Gremlin Server. The function accepts the following forms of dates and times using a default time zone offset of UTC(+00:00):</p> </div> <div class="ulist"> <ul> <li> <p><code>2018-03-22</code></p> </li> <li> <p><code>2018-03-22T00:35:44</code></p> </li> <li> <p><code>2018-03-22T00:35:44Z</code></p> </li> <li> <p><code>2018-03-22T00:35:44.741</code></p> </li> <li> <p><code>2018-03-22T00:35:44.741Z</code></p> </li> <li> <p><code>2018-03-22T00:35:44.741+1600</code></p> </li> </ul> </div> <div class="paragraph"> <p><a id="connecting-via-remotegraph"></a> <a id="connecting-via-java"></a></p> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-java">Gremlin-Java</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-java-drawing.png" alt="gremlin java drawing" width="130"></span> Apache TinkerPop’s Gremlin-Java implements Gremlin within the Java language and can be used by any Java Virtual Machine. Gremlin-Java is considered the canonical, reference implementation of Gremlin and serves as the foundation by which all other Gremlin language variants should emulate. As the Gremlin Traversal Machine that processes Gremlin queries is also written in Java, it can be used in all three connection methods described in the <a href="#connecting-gremlin">Connecting Gremlin</a> Section.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>gremlin-core<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span> <span class="comment"><!-- when using Gremlin Server or Remote Gremlin Provider a driver is required --></span> <span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>gremlin-driver<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span> <span class="comment"><!-- alternatively the driver is packaged as an uberjar with shaded non-optional dependencies including gremlin-core and tinkergraph-gremlin which are not shaded. --></span> <span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>gremlin-driver<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"><classifier></span>shaded<span class="tag"></classifier></span> <span class="comment"><!-- The shaded JAR uses the original POM, therefore conflicts may still need resolution --></span> <span class="tag"><exclusions></span> <span class="tag"><exclusion></span> <span class="tag"><groupId></span>io.netty<span class="tag"></groupId></span> <span class="tag"><artifactId></span>*<span class="tag"></artifactId></span> <span class="tag"></exclusion></span> <span class="tag"></exclusions></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-java-connecting">Connecting</h3> <div class="paragraph"> <p>The pattern for connecting is described in <a href="#connecting-gremlin">Connecting Gremlin</a> and it basically distills down to creating a <code>GraphTraversalSource</code>. For <a href="#connecting-embedded">embedded</a> mode, this involves first creating a <code>Graph</code> and then spawning the <code>GraphTraversalSource</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Graph graph = ...; GraphTraversalSource g = traversal().withEmbedded(graph);</code></pre> </div> </div> <div class="paragraph"> <p>Using "g" it is then possible to start writing Gremlin. The "g" allows for the setting of many configuration options which affect traversal execution. The <a href="#traversal">Traversal</a> Section describes some of these options and some are only suitable with <a href="#connecting-embedded">embedded</a> style usage. For remote options however there are some added configurations to consider and this section looks to address those.</p> </div> <div class="paragraph"> <p>When connecting to <a href="#connecting-gremlin-server">Gremlin Server</a> or <a href="#connecting-rgp">Remote Gremlin Providers</a> it is possible to configure the <code>DriverRemoteConnection</code> manually as shown in earlier examples where the host and port are provided as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(<span class="string"><span class="delimiter">"</span><span class="content">localhost</span><span class="delimiter">"</span></span>,<span class="integer">8182</span>,<span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>));</code></pre> </div> </div> <div class="paragraph"> <p>It is also possible to create it from a configuration. The most basic way to do so involves the following line of code:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withRemote(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-graph.properties</span><span class="delimiter">'</span></span>);</code></pre> </div> </div> <div class="paragraph"> <p>The <code>remote-graph.properties</code> file simply provides connection information to the <code>GraphTraversalSource</code> which is used to configure a <code>RemoteConnection</code>. That file looks like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin.remote.remoteConnectionClass=org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection gremlin.remote.driver.clusterFile=conf/remote-objects.yaml gremlin.remote.driver.sourceName=g</code></pre> </div> </div> <div class="paragraph"> <p>The <code>RemoteConnection</code> is an interface that provides the transport mechanism for "g" and makes it possible to for that mechanism to be altered (typically by graph providers who have their own protocols). TinkerPop provides one such implementation called the <code>DriverRemoteConnection</code> which enables transport over Gremlin Server protocols using the TinkerPop driver. The driver is configured by the specified <code>gremlin.remote.driver.clusterFile</code> and the local "g" is bound to the <code>GraphTraversalSource</code> on the remote end with <code>gremlin.remote.driver.sourceName</code> which in this case is also "g".</p> </div> <div class="paragraph"> <p>There are other ways to configure the traversal using <code>withRemote()</code> as it has other overloads. It can take an Apache Commons <code>Configuration</code> object which would have keys similar to those shown in the properties file and it can also take a <code>RemoteConnection</code> instance directly. The latter is interesting in that it means it is possible to programmatically construct all aspects of the <code>RemoteConnection</code>. For TinkerPop usage, that might mean directly constructing the <code>DriverRemoteConnection</code> and the driver instance that supplies the transport mechanism. For example, the command shown above could be re-written using programmatic construction as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>));</code></pre> </div> </div> <div class="paragraph"> <p>Please consider the following example:</p> </div> <section class="tabs tabs-3"> <input id="tab-1729797163-1" type="radio" name="radio-set-1729797163-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797163-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797163-2" type="radio" name="radio-set-1729797163-1" class="tab-selector-2" /> <label for="tab-1729797163-2" class="tab-label-2">groovy</label> <input id="tab-1729797163-3" type="radio" name="radio-set-1729797163-1" class="tab-selector-3" /> <label for="tab-1729797163-3" class="tab-label-3">java</label> <div class="tabcontent"> <div class="tabcontent-1"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> g = traversal().withRemote(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-graph.properties</span><span class="delimiter">'</span></span>) ==>graphtraversalsource[emptygraph[empty], standard] gremlin> g.V().elementMap() ==>[<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">29</span>] ==>[<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">27</span>] ==>[<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] ==>[<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">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<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">35</span>] gremlin> g.close() ==><span class="predefined-constant">null</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().withRemote(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-graph.properties</span><span class="delimiter">'</span></span>) g.V().elementMap() g.close()</code></pre> </div> </div> </div> </div> <div class="tabcontent"> <div class="tabcontent-3"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withRemote(<span class="string"><span class="delimiter">"</span><span class="content">conf/remote-graph.properties</span><span class="delimiter">"</span></span>); <span class="predefined-type">List</span><<span class="predefined-type">Map</span>> list = g.V().elementMap(); g.close();</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Note the call to <code>close()</code> above. The call to <code>withRemote()</code> internally instantiates a connection via the driver that can only be released by "closing" the <code>GraphTraversalSource</code>. It is important to take that step to release network resources associated with <code>g</code>.</p> </div> <div class="paragraph"> <p>If working with multiple remote <code>TraversalSource</code> instances it is more efficient to construct <code>Cluster</code> and <code>Client</code> objects and then re-use them.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797163-4" type="radio" name="radio-set-1729797163-4" class="tab-selector-1" checked="checked" /> <label for="tab-1729797163-4" class="tab-label-1">console (groovy)</label> <input id="tab-1729797163-5" type="radio" name="radio-set-1729797163-4" class="tab-selector-2" /> <label for="tab-1729797163-5" 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> cluster = Cluster.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-objects.yaml</span><span class="delimiter">'</span></span>) ==>localhost/<span class="float">127.0</span><span class="float">.0</span><span class="float">.1</span>:<span class="integer">8182</span> gremlin> client = cluster.connect() ==>org.apache.tinkerpop.gremlin.driver.Client<span class="error">$</span>ClusteredClient<span class="error">@</span><span class="integer">8</span>ae7e03 gremlin> g = traversal().withRemote(DriverRemoteConnection.using(client, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>)) ==>graphtraversalsource[emptygraph[empty], standard] gremlin> g.V().elementMap() ==>[<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">29</span>] ==>[<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">27</span>] ==>[<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] ==>[<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">32</span>] ==>[<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software,<span class="key">name</span>:ripple,<span class="key">lang</span>:java] ==>[<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">35</span>] gremlin> g.close() ==><span class="predefined-constant">null</span> gremlin> client.close() ==><span class="predefined-constant">null</span> gremlin> cluster.close() ==><span class="predefined-constant">null</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">cluster = Cluster.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/remote-objects.yaml</span><span class="delimiter">'</span></span>) client = cluster.connect() g = traversal().withRemote(DriverRemoteConnection.using(client, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>)) g.V().elementMap() g.close() client.close() cluster.close()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>If the <code>Client</code> instance is supplied externally, as is shown above, then it is not closed implicitly by the close of "g". Closing "g" will have no effect on "client" or "cluster". When supplying them externally, the <code>Client</code> and <code>Cluster</code> objects must also be closed explicitly. It’s worth noting that the close of a <code>Cluster</code> will close all <code>Client</code> instances spawned by the <code>Cluster</code>.</p> </div> <div class="paragraph"> <p>Some connection options can also be set on individual requests made through the Java driver using <code>with()</code> step on the <code>TraversalSource</code>. For instance to set request timeout to 500 milliseconds:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">GraphTraversalSource g = traversal().withRemote(conf); <span class="predefined-type">List</span><Vertex> vertices = g.with(Tokens.ARGS_EVAL_TIMEOUT, <span class="integer">500L</span>).V().out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).toList()</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated). Use of <code>Tokens</code> to reference these options is preferred.</p> </div> <div class="paragraph"> <p><a id="java-imports"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-imports">Common Imports</h3> <div class="paragraph"> <p>There are a number of classes, functions and tokens that are typically used with Gremlin. The following imports provide most of the common functionality required to use Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource</span>; <span class="keyword">import</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.IO</span>; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal</span>; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.Operator</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.Order</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.P</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.Pop</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.SackFunctions</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.Scope</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.TextP</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.structure.Column</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.structure.Direction</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.structure.T</span>.*; <span class="keyword">import</span> <span class="include">static</span> <span class="include">org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__</span>.*;</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-java-configuration">Configuration</h3> <div class="paragraph"> <p>The following table describes the various configuration options for the Gremlin Driver:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.channelizer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified classname of the client <code>Channelizer</code> that defines how to connect to the server.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>Channelizer.WebSocketChannelizer</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.enableSsl</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines if SSL should be enabled or not. If enabled on the server then it must be enabled on the client.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.keepAliveInterval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Length of time in milliseconds to wait on an idle connection before sending a keep-alive request. Set to zero to disable this feature.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">180000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.keyStore</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The private key in JKS or PKCS#12 format.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.keyStorePassword</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password of the <code>keyStore</code> if it is password-protected.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.keyStoreType</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>JKS</code> (Java 8 default) or <code>PKCS12</code> (Java 9+ default)</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxContentLength</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum length in bytes that a message can be sent to the server. This number can be no greater than the setting of the same name in the server configuration.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">65536</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxInProcessPerConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of in-flight requests that can occur on a connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxSimultaneousUsagePerConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of times that a connection can be borrowed from the pool simultaneously.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">16</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum size of a connection pool for a host.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxWaitForConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The amount of time in milliseconds to wait for a new connection before timing out.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">3000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.maxWaitForClose</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The amount of time in milliseconds to wait for pending messages to be returned from the server before closing the connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">3000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.minInProcessPerConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The minimum number of in-flight requests that can occur on a connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.minSimultaneousUsagePerConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of times that a connection can be borrowed from the pool simultaneously.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.minSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The minimum size of a connection pool for a host.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">2</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.reconnectInterval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The amount of time in milliseconds to wait before trying to reconnect to a dead host.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.resultIterationBatchSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The override value for the size of the result batches to be returned from the server.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">64</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.sslCipherSuites</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The list of JSSE ciphers to support for SSL connections. If specified, only the ciphers that are listed and supported will be enabled. If not specified, the JVM default is used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.sslEnabledProtocols</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The list of SSL protocols to support for SSL connections. If specified, only the protocols that are listed and supported will be enabled. If not specified, the JVM default is used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.sslSkipCertValidation</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Configures the <code>TrustManager</code> to trust all certs without any validation. Should not be used in production.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.trustStore</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">File location for a SSL Certificate Chain to use when SSL is enabled. If this value is not provided and SSL is enabled, the default <code>TrustManager</code> will be used.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.trustStorePassword</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password of the <code>trustStore</code> if it is password-protected</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.validationRequest</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A script that is used to test server connectivity. A good script to use is one that evaluates quickly and returns no data. The default simply returns an empty string, but if a graph is required by a particular provider, a good traversal might be <code>g.inject()</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>''</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">connectionPool.connectionSetupTimeoutMillis</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Duration of time in milliseconds provided for connection setup to complete which includes WebSocket protocol handshake and SSL handshake.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">15000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enableCompression</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables permessage-deflate compression. Note that use of compression may increase vulnerability to attacks such as CRIME/BREACH.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enableUserAgentOnConnect</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables sending a user agent to the server during connection requests. More details can be found in provider docs <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_graph_driver_provider_requirements">here</a>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">hosts</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The list of hosts that the driver will connect to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">jaasEntry</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the <code>AuthProperties.Property.JAAS_ENTRY</code> properties for authentication to Gremlin Server.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">nioPoolSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Size of the pool for handling request/response operations.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">available processors</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">password</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">path</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The URL path to the Gremlin Server.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>/gremlin</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The port of the Gremlin Server to connect to. The same port will be applied for all hosts.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8192</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">protocol</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Sets the <code>AuthProperties.Property.PROTOCOL</code> properties for authentication to Gremlin Server.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">serializer.className</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The fully qualified class name of the <code>MessageSerializer</code> that will be used to communicate with the server. Note that the serializer configured on the client should be supported by the server configuration.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">serializer.config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A <code>Map</code> of configuration settings for the serializer.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">username</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The username to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">workerPoolSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Size of the pool for handling background work.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">available processors * 2</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Please see the <a href="https://tinkerpop.apache.org/javadocs/3.7.3/core/org/apache/tinkerpop/gremlin/driver/Cluster.Builder.html">Cluster.Builder javadoc</a> to get more information on these settings.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-transactions">Transactions</h3> <div class="paragraph"> <p>Transactions with Java are best described in <a href="#transactions">The Traversal - Transactions</a> section of this documentation as Java covers both embedded and remote use cases.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-serialization">Serialization</h3> <div class="paragraph"> <p>Remote systems like Gremlin Server and Remote Gremlin Providers respond to requests made in a particular serialization format and respond by serializing results to some format to be interpreted by the client. For JVM-based languages, there are two options for serialization: GraphSON and GraphBinary. It is important that the client and server have the same serializers configured in the same way or else one or the other will experience serialization exceptions and fail to always communicate. Discrepancy in serializer registration between client and server can happen fairly easily as different graph systems may automatically include serializers on the server-side, thus leaving the client to be configured manually. As an example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">IoRegistry registry = ...; <span class="comment">// an IoRegistry instance exposed by a specific graph provider</span> TypeSerializerRegistry typeSerializerRegistry = TypeSerializerRegistry.build().addRegistry(registry).create(); MessageSerializer serializer = <span class="keyword">new</span> GraphBinaryMessageSerializerV1(typeSerializerRegistry); Cluster cluster = Cluster.build(). serializer(serializer). create(); Client client = cluster.connect(); GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(client, <span class="string"><span class="delimiter">"</span><span class="content">g</span><span class="delimiter">"</span></span>));</code></pre> </div> </div> <div class="paragraph"> <p>The <code>IoRegistry</code> tells the serializer what classes from the graph provider to auto-register during serialization. Gremlin Server roughly uses this same approach when it configures its serializers, so using this same model will ensure compatibility when making requests. Obviously, it is possible to switch to GraphSON or GraphBinary by using the appropriate <code>MessageSerializer</code> (e.g. <code>GraphSONMessageSerializerV3</code> or <code>GraphBinaryMessageSerializerV1</code> respectively) in the same way and building that into the <code>Cluster</code> object.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-lambda">The Lambda Solution</h3> <div class="paragraph"> <p>Supporting <a href="https://en.wikipedia.org/wiki/Anonymous_function">anonymous functions</a> across languages is difficult as most languages do not support lambda introspection and thus, code analysis. In Gremlin-Java and with <a href="#connecting-embedded">embedded</a> usage, lambdas can be leveraged directly:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.V().out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).map(t -> t.get().value(<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"> is the friend name</span><span class="delimiter">"</span></span>) <span class="invisible">//</span><b class="conum">1</b> g.V().out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).sideEffect(<span class="predefined-type">System</span>.out::println) <span class="invisible">//</span><b class="conum">2</b> g.V().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).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">b</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>).by((Function<Vertex, <span class="predefined-type">Integer</span>>) v -> v.<<span class="predefined-type">String</span>>value(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>).length()) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>A Java <code>Function</code> is used to map a <code>Traverser<S></code> to an object <code>E</code>.</p> </li> <li> <p>Gremlin steps that take consumer arguments can be passed Java method references.</p> </li> <li> <p>Gremlin-Java may sometimes require explicit lambda typing when types can not be automatically inferred.</p> </li> </ol> </div> <div class="paragraph"> <p>When sending traversals remotely to <a href="#connecting-gremlin-server">Gremlin Server</a> or <a href="#connecting-rgp">Remote Gremlin Providers</a>, the static methods of <code>Lambda</code> should be used and should denote a particular JSR-223 <code>ScriptEngine</code> that is available on the remote end (typically, this is Groovy). <code>Lambda</code> creates a string-based lambda that is then converted into a lambda/closure/anonymous-function/etc. by the respective lambda language’s JSR-223 <code>ScriptEngine</code> implementation.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">g.V().out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).map(Lambda.function(<span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name') + ' is the friend name'</span><span class="delimiter">"</span></span>)) g.V().out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).sideEffect(Lambda.consumer(<span class="string"><span class="delimiter">"</span><span class="content">println it</span><span class="delimiter">"</span></span>)) g.V().as(<span class="string"><span class="delimiter">"</span><span class="content">a</span><span class="delimiter">"</span></span>).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">b</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">b</span><span class="delimiter">"</span></span>).by(Lambda.<Vertex,<span class="predefined-type">Integer</span>>function(<span class="string"><span class="delimiter">"</span><span class="content">it.value('name').length()</span><span class="delimiter">"</span></span>))</code></pre> </div> </div> <div class="paragraph"> <p>Finally, Gremlin <code>Bytecode</code> that includes lambdas requires that the traversal be processed by the <code>ScriptEngine</code>. To avoid continued recompilation costs, it supports the encoding of bindings, which allow Gremlin Server to cache traversals that will be reused over and over again save that some parameterization may change. Thus, instead of translating, compiling, and then executing each submitted bytecode request, it is possible to simply execute. To express bindings in Java, use <code>Bindings</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">b = Bindings.instance() g.V(b.of(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>,<span class="integer">1</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).map{t -> <span class="string"><span class="delimiter">"</span><span class="content">name: </span><span class="delimiter">"</span></span> + t.get() } g.V(b.of(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>,<span class="integer">4</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).map{t -> <span class="string"><span class="delimiter">"</span><span class="content">name: </span><span class="delimiter">"</span></span> + t.get() } g.V(b.of(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>,<span class="integer">4</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).getBytecode() g.V(b.of(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>,<span class="integer">4</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).getBytecode().getBindings() cluster.close()</code></pre> </div> </div> <div class="paragraph"> <p>Both traversals are abstractly defined as <code>g.V(id).out('created').values('name').map{t → "name: " + t.get() }</code> and thus, the first submission can be cached for faster evaluation on the next submission.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> It is generally advised to avoid lambda usage. Please consider <a href="#a-note-on-lambdas">A Note On Lambdas</a> for more information. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-java-scripts">Submitting Scripts</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/gremlin-java.png" alt="gremlin java" width="175"></span> TinkerPop comes equipped with a reference client for Java-based applications. It is referred to as <code>gremlin-driver</code>, which enables applications to send requests to Gremlin Server and get back results.</p> </div> <div class="paragraph"> <p>Gremlin scripts are sent to the server from a <code>Client</code> instance. A <code>Client</code> is created as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); <span class="invisible">//</span><b class="conum">1</b> Client client = cluster.connect(); <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Opens a reference to <code>localhost</code> - note that there are many configuration options available in defining a <code>Cluster</code> object.</p> </li> <li> <p>Creates a <code>Client</code> given the configuration options of the <code>Cluster</code>.</p> </li> </ol> </div> <div class="paragraph"> <p>Once a <code>Client</code> instance is ready, it is possible to issue some Gremlin Groovy scripts:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">ResultSet</span> results = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">[1,2,3,4]</span><span class="delimiter">"</span></span>); <span class="invisible">//</span><b class="conum">1</b> results.stream().map(i -> i.get(<span class="predefined-type">Integer</span>.class) * <span class="integer">2</span>); <span class="invisible">//</span><b class="conum">2</b> CompletableFuture<<span class="predefined-type">List</span><<span class="predefined-type">Result</span>>> results = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">[1,2,3,4]</span><span class="delimiter">"</span></span>).all(); <span class="invisible">//</span><b class="conum">3</b> CompletableFuture<<span class="predefined-type">ResultSet</span>> future = client.submitAsync(<span class="string"><span class="delimiter">"</span><span class="content">[1,2,3,4]</span><span class="delimiter">"</span></span>); <span class="invisible">//</span><b class="conum">4</b> <span class="predefined-type">Map</span><<span class="predefined-type">String</span>,<span class="predefined-type">Object</span>> params = <span class="keyword">new</span> <span class="predefined-type">HashMap</span><>(); params.put(<span class="string"><span class="delimiter">"</span><span class="content">x</span><span class="delimiter">"</span></span>,<span class="integer">4</span>); client.submit(<span class="string"><span class="delimiter">"</span><span class="content">[1,2,3,x]</span><span class="delimiter">"</span></span>, params); <span class="invisible">//</span><b class="conum">5</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Submits a script that simply returns a <code>List</code> of integers. This method blocks until the request is written to the server and a <code>ResultSet</code> is constructed.</p> </li> <li> <p>Even though the <code>ResultSet</code> is constructed, it does not mean that the server has sent back the results (or even evaluated the script potentially). The <code>ResultSet</code> is just a holder that is awaiting the results from the server. In this case, they are streamed from the server as they arrive.</p> </li> <li> <p>Submit a script, get a <code>ResultSet</code>, then return a <code>CompletableFuture</code> that will be called when all results have been returned.</p> </li> <li> <p>Submit a script asynchronously without waiting for the request to be written to the server.</p> </li> <li> <p>Parameterized request are considered the most efficient way to send Gremlin to the server as they can be cached, which will boost performance and reduce resources required on the server.</p> </li> </ol> </div> <div class="sect3"> <h4 id="_per_request_settings_2">Per Request Settings</h4> <div class="paragraph"> <p>There are a number of overloads to <code>Client.submit()</code> that accept a <code>RequestOptions</code> object. The <code>RequestOptions</code> provide a way to include options that are specific to the request made with the call to <code>submit()</code>. A good use-case for this feature is to set a per-request override to the <code>evaluationTimeout</code> so that it only applies to the current request.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Cluster cluster = Cluster.open(); Client client = cluster.connect(); RequestOptions options = RequestOptions.build().timeout(<span class="integer">500</span>).create(); <span class="predefined-type">List</span><<span class="predefined-type">Result</span>> result = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().repeat(both()).times(100)</span><span class="delimiter">"</span></span>, options).all().get();</code></pre> </div> </div> <div class="paragraph"> <p>The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try <code>g.with(EVALUATION_TIMEOUT, 500)</code> within a script. Gremlin Server will respect timeouts set this way in scripts as well. With scripts of course, it is possible to send multiple traversals at once in the same script. In such events, the timeout for the request is interpreted as a sum of all timeouts identified in the script.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">RequestOptions options = RequestOptions.build().timeout(<span class="integer">500</span>).create(); <span class="predefined-type">List</span><<span class="predefined-type">Result</span>> result = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.with(EVALUATION_TIMEOUT, 500).addV().iterate();</span><span class="delimiter">"</span></span> + <span class="string"><span class="delimiter">"</span><span class="content">g.addV().iterate();</span><span class="delimiter">"</span></span> + <span class="string"><span class="delimiter">"</span><span class="content">g.with(EVALUATION_TIMEOUT, 500).addV();</span><span class="delimiter">"</span></span>, options).all().get();</code></pre> </div> </div> <div class="paragraph"> <p>In the above example, <code>RequestOptions</code> defines a timeout of 500 milliseconds, but the script has three traversals with two internal settings for the timeout using <code>with()</code>. The request timeout used by the server will therefore be 1000 milliseconds (overriding the 500 which itself was an override for whatever configuration was on the server).</p> </div> </div> <div class="sect3"> <h4 id="_aliases">Aliases</h4> <div class="paragraph"> <p>Scripts submitted to Gremlin Server automatically have the globally configured <code>Graph</code> and <code>TraversalSource</code> instances made available to them. Therefore, if Gremlin Server configures two <code>TraversalSource</code> instances called "g1" and "g2" a script can simply reference them directly as:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g1.V()</span><span class="delimiter">"</span></span>) client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g2.V()</span><span class="delimiter">"</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>While this is an acceptable way to submit scripts, it has the downside of forcing the client to encode the server-side variable name directly into the script being sent. If the server configuration ever changed such that "g1" became "g100", the client-side code might have to see a significant amount of change. Decoupling the script code from the server configuration can be managed by the <code>alias</code> method on <code>Client</code> as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Client g1Client = client.alias(<span class="string"><span class="delimiter">"</span><span class="content">g1</span><span class="delimiter">"</span></span>) Client g2Client = client.alias(<span class="string"><span class="delimiter">"</span><span class="content">g2</span><span class="delimiter">"</span></span>) g1Client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V()</span><span class="delimiter">"</span></span>) g2Client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V()</span><span class="delimiter">"</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>The above code demonstrates how the <code>alias</code> method can be used such that the script need only contain a reference to "g" and "g1" and "g2" are automatically rebound into "g" on the server-side.</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-java-dsl">Domain Specific Languages</h3> <div class="paragraph"> <p>Creating a <a href="#dsl">Domain Specific Language</a> (DSL) in Java requires the <code>@GremlinDsl</code> Java annotation in the <code>gremlin-annotations</code> module. This annotation should be applied to a "DSL interface" that extends <code>GraphTraversal.Admin</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>gremlin-annotations<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@GremlinDsl</span> <span class="directive">public</span> <span class="type">interface</span> <span class="class">SocialTraversalDsl</span><S, E> <span class="directive">extends</span> GraphTraversal.Admin<S, E> { }</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The name of the DSL interface should be suffixed with "TraversalDSL". All characters in the interface name before that become the "name" of the DSL. </td> </tr> </table> </div> <div class="paragraph"> <p>In this interface, define the methods that the DSL will be composed of:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@GremlinDsl</span> <span class="directive">public</span> <span class="type">interface</span> <span class="class">SocialTraversalDsl</span><S, E> <span class="directive">extends</span> GraphTraversal.Admin<S, E> { <span class="directive">public</span> <span class="keyword">default</span> GraphTraversal<S, Vertex> knows(<span class="predefined-type">String</span> personName) { <span class="keyword">return</span> out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).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>, personName); } <span class="directive">public</span> <span class="keyword">default</span> <E2 <span class="directive">extends</span> <span class="predefined-type">Number</span>> GraphTraversal<S, E2> youngestFriendsAge() { <span class="keyword">return</span> out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>).min(); } <span class="directive">public</span> <span class="keyword">default</span> GraphTraversal<S, <span class="predefined-type">Long</span>> createdAtLeast(<span class="type">int</span> number) { <span class="keyword">return</span> outE(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).count().is(P.gte(number)); } }</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Follow the TinkerPop convention of using <code><S,E></code> in naming generics as those conventions are taken into account when generating the anonymous traversal class. The processor attempts to infer the appropriate type parameters when generating the anonymous traversal class. If it cannot do it correctly, it is possible to avoid the inference by using the <code>GremlinDsl.AnonymousMethod</code> annotation on the DSL method. It allows explicit specification of the types to use. </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>@GremlinDsl</code> annotation is used by the <a href="https://docs.oracle.com/javase/8/docs/api/index.html?javax/annotation/processing/Processor.html">Java Annotation Processor</a> to generate the boilerplate class structure required to properly use the DSL within the TinkerPop framework. These classes can be generated and maintained by hand, but it would be time consuming, monotonous and error-prone to do so. Typically, the Java compilation process is automatically configured to detect annotation processors on the classpath and will automatically use them when found. If that does not happen, it may be necessary to make configuration changes to the build to allow for the compilation process to be aware of the following <code>javax.annotation.processing.Processor</code> implementation:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDslProcessor</code></pre> </div> </div> <div class="paragraph"> <p>The annotation processor will generate several classes for the DSL:</p> </div> <div class="ulist"> <ul> <li> <p><code>SocialTraversal</code> - A <code>Traversal</code> interface that extends the <code>SocialTraversalDsl</code> proxying methods to its underlying interfaces (such as <code>GraphTraversal</code>) to instead return a <code>SocialTraversal</code></p> </li> <li> <p><code>DefaultSocialTraversal</code> - A default implementation of <code>SocialTraversal</code> (typically not used directly by the user)</p> </li> <li> <p><code>SocialTraversalSource</code> - Spawns <code>DefaultSocialTraversal</code> instances.</p> </li> <li> <p><code>__</code> - Spawns anonymous <code>DefaultSocialTraversal</code> instances.</p> </li> </ul> </div> <div class="paragraph"> <p>Using the DSL then just involves telling the <code>Graph</code> to use it:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">SocialTraversalSource social = traversal(SocialTraversalSource.class).withEmbedded(graph); social.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>).knows(<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>);</code></pre> </div> </div> <div class="paragraph"> <p>The <code>SocialTraversalSource</code> can also be customized with DSL functions. As an additional step, include a class that extends from <code>GraphTraversalSource</code> and with a name that is suffixed with "TraversalSourceDsl". Include in this class, any custom methods required by the DSL:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">SocialTraversalSourceDsl</span> <span class="directive">extends</span> GraphTraversalSource { <span class="directive">public</span> SocialTraversalSourceDsl(Graph graph, TraversalStrategies traversalStrategies) { <span class="local-variable">super</span>(graph, traversalStrategies); } <span class="directive">public</span> SocialTraversalSourceDsl(Graph graph) { <span class="local-variable">super</span>(graph); } <span class="directive">public</span> SocialTraversalSourceDsl(RemoteConnection connection) { <span class="local-variable">super</span>(connection); } <span class="directive">public</span> GraphTraversal<Vertex, Vertex> persons(<span class="predefined-type">String</span>... names) { GraphTraversalSource clone = <span class="local-variable">this</span>.clone(); <span class="comment">// Manually add a "start" step for the traversal in this case the equivalent of V(). GraphStep is marked</span> <span class="comment">// as a "start" step by passing "true" in the constructor.</span> clone.getBytecode().addStep(GraphTraversal.Symbols.V); GraphTraversal<Vertex, Vertex> traversal = <span class="keyword">new</span> DefaultGraphTraversal<>(clone); traversal.asAdmin().addStep(<span class="keyword">new</span> GraphStep<>(traversal.asAdmin(), Vertex.class, <span class="predefined-constant">true</span>)); traversal = traversal.hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>); <span class="keyword">if</span> (names.length > <span class="integer">0</span>) traversal = traversal.has(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>, P.within(names)); <span class="keyword">return</span> traversal; } }</code></pre> </div> </div> <div class="paragraph"> <p>Then, back in the <code>SocialTraversal</code> interface, update the <code>GremlinDsl</code> annotation with the <code>traversalSource</code> argument to point to the fully qualified class name of the <code>SocialTraversalSourceDsl</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@GremlinDsl</span>(traversalSource = <span class="string"><span class="delimiter">"</span><span class="content">com.company.SocialTraversalSourceDsl</span><span class="delimiter">"</span></span>) <span class="directive">public</span> <span class="type">interface</span> <span class="class">SocialTraversalDsl</span><S, E> <span class="directive">extends</span> GraphTraversal.Admin<S, E> { ... }</code></pre> </div> </div> <div class="paragraph"> <p>It is then possible to use the <code>persons()</code> method to start traversals:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">SocialTraversalSource social = traversal(SocialTraversalSource.class).withEmbedded(graph); social.persons(<span class="string"><span class="delimiter">"</span><span class="content">marko</span><span class="delimiter">"</span></span>).knows(<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>);</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Using Maven, as shown in the <code>gremlin-archetype-dsl</code> module, makes developing DSLs with the annotation processor straightforward in that it sets up appropriate paths to the generated code automatically. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-java-troubleshooting">Troubleshooting</h3> <div class="paragraph"> <p><strong>Max frame length of 65536 has been exceeded</strong></p> </div> <div class="paragraph"> <p>This error occurs when the driver attempts to process a request/response that exceeds the configured maximum size. The most direct way to fix this problem is to increase the <code>maxContentLength</code> setting in the driver. Ideally, the <code>maxContentLength</code> set for the driver should match the setting defined on the server.</p> </div> <div class="paragraph"> <p><strong>TimeoutException</strong></p> </div> <div class="paragraph"> <p>A <code>TimeoutException</code> is thrown by the driver when the time limit assigned by the <code>maxWaitForConnection</code> is exceeded when trying to borrow a connection from the connection pool for a particular host. There are generally two scenarios where this occurs:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>The server has actually reached its maximum capacity or the driver has just learned that the server is unreachable.</p> </li> <li> <p>The client is throttling requests when the pool is exhausted.</p> </li> </ol> </div> <div class="paragraph"> <p>The latter of the two can be addressed from the driver side in the following ways:</p> </div> <div class="ulist"> <ul> <li> <p>Increase the <code>maxWaitForConnection</code> allowing the client to wait a bit longer for a connection to become available.</p> </li> <li> <p>Increase the number of requests allowed per connection by increasing the <code>maxSimultaneousUsagePerConnection</code> and <code>maxInProcessPerConnection</code> settings.</p> </li> <li> <p>Increase the number of connections available in the connection pool by increasing the <code>maxConnectionPoolSize</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>The exception and logs (assuming they are enabled) should contain information about the state of the connection pool along with its connections which can help shed more light on which of these scenarios caused the problem. Some examples of these messages and their meaning are shown below:</p> </div> <div class="paragraph"> <p><em>The server is unavailable</em></p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Timed-out (500 MILLISECONDS) waiting for connection on Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}. Potential Cause: Connection refused: no further information > ConnectionPool (Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin})- no connections in pool</code></pre> </div> </div> <div class="paragraph"> <p><em>Client is likely issuing more requests than the pool size can handle</em></p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Timed-out (150 MILLISECONDS) waiting for connection on Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}. Potential Cause: Number of active requests exceeds pool size. Consider increasing the value for maxConnectionPoolSize. ConnectionPool (Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}) Connection Pool Status (size=1 max=1 min=1 toCreate=0 bin=0) > Connection{channel=5a859d62 isDead=false borrowed=1 pending=1 markedReplaced=false closing=false created=2022-12-19T21:08:21.569613100Z thread=gremlin-driver-conn-scheduler-1} -- bin --</code></pre> </div> </div> <div class="paragraph"> <p><em>Network traffic is slow and the websocket handshake does not complete in time</em></p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Timed-out (250 MILLISECONDS) waiting for connection on Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}. Potential Cause: WebSocket handshake not completed in stipulated time=[100]ms ConnectionPool (Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}) Connection Pool Status (size=1 max=5 min=1 toCreate=0 bin=0) > Connection{channel=205fc8d2 isDead=false borrowed=1 pending=1 markedReplaced=false closing=false created=2022-12-19T21:10:04.692921600Z thread=gremlin-driver-conn-scheduler-1} -- bin --</code></pre> </div> </div> <div class="paragraph"> <p><a id="java-application-examples"></a> <a id="gremlin-archetypes"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-archetypes">Application Archetypes</h3> <div class="paragraph"> <p>The available <a href="https://maven.apache.org/guides/introduction/introduction-to-archetypes.html">Maven archetypes</a> are as follows:</p> </div> <div class="ulist"> <ul> <li> <p><code>gremlin-archetype-dsl</code> - An example project that demonstrates how to build Domain Specific Languages with Gremlin in Java.</p> </li> <li> <p><code>gremlin-archetype-server</code> - An example project that demonstrates the basic structure of a <a href="#gremlin-server">Gremlin Server</a> project, how to connect with the Gremlin Driver, and how to embed Gremlin Server in a testing framework.</p> </li> <li> <p><code>gremlin-archetype-tinkergraph</code> - A basic example of how to structure a TinkerPop project with Maven.</p> </li> </ul> </div> <div class="paragraph"> <p>Use Maven to generate these example projects with a command like:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">$ mvn archetype:generate -DarchetypeGroupId=org.apache.tinkerpop -DarchetypeArtifactId=gremlin-archetype-server \ -DarchetypeVersion=3.7.3 -DgroupId=com.my -DartifactId=app -Dversion=0.1 -DinteractiveMode=false</code></pre> </div> </div> <div class="paragraph"> <p>This command will generate a new Maven project in a directory called "app" with a <code>pom.xml</code> specifying a <code>groupId</code> of <code>com.my</code>. Please see the <code>README.asciidoc</code> in the root of each generated project for information on how to build and execute it.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-java-examples">Application Examples</h3> <div class="paragraph"> <p>The TinkerPop source code contains some sample applications that demonstrate the basics of Gremlin-Java. They can be found in GitHub <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-driver/src/main/java/examples/">here</a>.</p> </div> <div class="paragraph"> <p>The remote connection examples in particular are designed to connect to a running <a href="#gremlin-server">Gremlin Server</a> configured with the <code>conf/gremlin-server.yaml</code> file as included with the standard release packaging.</p> </div> <div class="paragraph"> <p>To do so, download an image of Gremlin Server from Docker Hub, then launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker pull tinkerpop/gremlin-server docker run -d -p 8182:8182 tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>All examples can then be run using your IDE of choice.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-javascript">Gremlin-JavaScript</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-js.png" alt="gremlin js" width="130"></span> Apache TinkerPop’s Gremlin-JavaScript implements Gremlin within the JavaScript language. It targets Node.js runtime and can be used on different operating systems on any Node.js 6 or above. Since the JavaScript naming conventions are very similar to that of Java, it should be very easy to switch between Gremlin-Java and Gremlin-JavaScript.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">npm install gremlin</code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-connecting">Connecting</h3> <div class="paragraph"> <p>The pattern for connecting is described in <a href="#connecting-gremlin">Connecting Gremlin</a> and it basically distills down to creating a <code>GraphTraversalSource</code>. A <code>GraphTraversalSource</code> is created from the <code>AnonymousTraversalSource.traversal()</code> method where the "g" provided to the <code>DriverRemoteConnection</code> corresponds to the name of a <code>GraphTraversalSource</code> on the remote end.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const g = traversal().withRemote(<span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>));</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin-JavaScript supports plain text SASL authentication, you can set it on the connection options.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const authenticator = <span class="keyword">new</span> gremlin.driver.auth.PlainTextSaslAuthenticator(<span class="string"><span class="delimiter">'</span><span class="content">myuser</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">mypassword</span><span class="delimiter">'</span></span>); const g = traversal().withRemote(<span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, { authenticator });</code></pre> </div> </div> <div class="paragraph"> <p>Given that I/O operations in Node.js are asynchronous by default, <a href="#terminal-steps">Terminal Steps</a> return a <code>Promise</code>:</p> </div> <div class="ulist"> <ul> <li> <p><code>Traversal.toList()</code>: Returns a <code>Promise</code> with an <code>Array</code> as result value.</p> </li> <li> <p><code>Traversal.next()</code>: Returns a <code>Promise</code> with a <code>{ value, done }</code> tuple as result value, according to the <a href="https://github.com/tc39/proposal-async-iteration">async iterator proposal</a>.</p> </li> <li> <p><code>Traversal.iterate()</code>: Returns a <code>Promise</code> without a value.</p> </li> </ul> </div> <div class="paragraph"> <p>For example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).toList() .then(names => console.log(names));</code></pre> </div> </div> <div class="paragraph"> <p>When using <code>async</code> functions it is possible to <code>await</code> the promises:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const names = await g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).toList(); console.log(names);</code></pre> </div> </div> <div class="paragraph"> <p>Some connection options can also be set on individual requests made through the using <code>with()</code> step on the <code>TraversalSource</code>. For instance to set request timeout to 500 milliseconds:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const vertices = await g.with_(<span class="string"><span class="delimiter">'</span><span class="content">evaluationTimeout</span><span class="delimiter">'</span></span>, <span class="integer">500</span>).V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).toList()</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated).</p> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-imports">Common Imports</h3> <div class="paragraph"> <p>There are a number of classes, functions and tokens that are typically used with Gremlin. The following imports provide most of the typical functionality required to use Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const gremlin = require(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>); const traversal = gremlin.process.AnonymousTraversalSource.traversal; const __ = gremlin.process.statics; const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection; const column = gremlin.process.column const direction = gremlin.process.direction const Direction = { <span class="key">BOTH</span>: direction.both, <span class="key">IN</span>: direction.<span class="keyword">in</span>, <span class="key">OUT</span>: direction.out, <span class="key">from_</span>: direction.out, <span class="key">to</span>: direction.<span class="keyword">in</span>, } const p = gremlin.process.P const textp = gremlin.process.TextP const pick = gremlin.process.pick const pop = gremlin.process.pop const order = gremlin.process.order const scope = gremlin.process.scope const t = gremlin.process.t const cardinality = gremlin.process.cardinality const CardinalityValue = gremlin.process.CardinalityValue</code></pre> </div> </div> <div class="paragraph"> <p>By defining these imports it becomes possible to write Gremlin in the more shorthand, canonical style that is demonstrated in most examples found here in the documentation:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const { <span class="key">P</span>: { gt } } = gremlin.process; const { <span class="key">order</span>: { desc } } = gremlin.process; 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">age</span><span class="delimiter">'</span></span>,gt(<span class="integer">30</span>)).order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,desc).toList()</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-configuration">Configuration</h3> <div class="paragraph"> <p>The following table describes the various configuration options for the Gremlin-Javascript Driver. They can be passed in the constructor of a new <code>Client</code> or <code>DriverRemoteConnection</code> :</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 16.6666%;"> <col style="width: 55.5555%;"> <col style="width: 11.1113%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Type</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">url</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The resource uri.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">None</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Object</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The connection options.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">{}</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.ca</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Array</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Trusted certificates.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.cert</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String/Array/Buffer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The certificate key.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.mimeType</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The mime type to use.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">'application/vnd.gremlin-v3.0+json'</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.pfx</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String/Buffer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The private key, certificate, and CA certs.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.reader</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">GraphSONReader/GraphBinaryReader</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The reader to use.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">select reader according to mimeType</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.writer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">GraphSONWriter</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The writer to use.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">select writer according to mimeType</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.rejectUnauthorized</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Boolean</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines whether to verify or not the server certificate.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.traversalSource</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The traversal source.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">'g'</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.authenticator</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Authenticator</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The authentication handler to use.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.processor</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The name of the opProcessor to use, leave it undefined or set 'session' when session mode.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.session</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">String</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The sessionId of Client in session mode. undefined means session-less Client.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.enableCompression</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Boolean</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables permessage-deflate compression. Note that use of compression may increase vulnerability to attacks such as CRIME/BREACH.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.enableUserAgentOnConnect</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Boolean</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines if a user agent will be sent during connection handshake.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.headers</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Object</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">An associative array containing the additional header key/values for the initial request.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">undefined</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.pingEnabled</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Boolean</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Setup ping interval.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.pingInterval</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Number</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Ping request interval in ms if ping enabled.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">60000</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">options.pongTimeout</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Number</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Timeout of pong response in ms after sending a ping.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">30000</p></td> </tr> </tbody> </table> </div> <div class="sect2"> <h3 id="gremlin-javascript-transactions">Transactions</h3> <div class="paragraph"> <p>To get a full understanding of this section, it would be good to start by reading the <a href="#transactions">Transactions</a> section of this documentation, which discusses transactions in the general context of TinkerPop itself. This section builds on that content by demonstrating the transactional syntax for Javascript.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const g = traversal().withRemote(<span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>)); const tx = g.tx(); <span class="comment">// create a Transaction</span> <span class="comment">// spawn a new GraphTraversalSource binding all traversals established from it to tx</span> const gtx = tx.begin(); <span class="comment">// execute traversals using gtx occur within the scope of the transaction held by tx. the</span> <span class="comment">// tx is closed after calls to commit or rollback and cannot be re-used. simply spawn a</span> <span class="comment">// new Transaction from g.tx() to create a new one as needed. the g context remains</span> <span class="comment">// accessible through all this as a sessionless connection.</span> Promise.all([ gtx.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">jorge</span><span class="delimiter">"</span></span>).iterate(), gtx.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">josh</span><span class="delimiter">"</span></span>).iterate() ]).then(() => { <span class="keyword">return</span> tx.commit(); }).<span class="keyword">catch</span>(() => { <span class="keyword">return</span> tx.rollback(); });</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-lambda">The Lambda Solution</h3> <div class="paragraph"> <p>Supporting <a href="https://en.wikipedia.org/wiki/Anonymous_function">anonymous functions</a> across languages is difficult as most languages do not support lambda introspection and thus, code analysis. In Gremlin-Javascript, a Gremlin lambda should be represented as a zero-arg callable that returns a string representation of the lambda expected for use in the traversal. The returned lambda should be written as a Gremlin-Groovy string. When the lambda is represented in <code>Bytecode</code> its language is encoded such that the remote connection host can infer which translator and ultimate execution engine to use.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">g.V().out(). map(() => <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>). sum(). toList().then(total => console.log(total))</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When running into situations where Groovy cannot properly discern a method signature based on the <code>Lambda</code> instance created, it will help to fully define the closure in the lambda expression - so rather than <code>() ⇒ "it.get().value('name')"</code>, prefer <code>() ⇒ "x → x.get().value('name')"</code>. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> As explained throughout the documentation, when possible <a href="#a-note-on-lambdas">avoid</a> lambdas. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-scripts">Submitting Scripts</h3> <div class="paragraph"> <p>It is possible to submit parametrized Gremlin scripts to the server as strings, using the <code>Client</code> class:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const gremlin = require(<span class="string"><span class="delimiter">'</span><span class="content">gremlin</span><span class="delimiter">'</span></span>); const client = <span class="keyword">new</span> gremlin.driver.Client(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, { <span class="key">traversalSource</span>: <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span> }); const result1 = await client.submit(<span class="string"><span class="delimiter">'</span><span class="content">g.V(vid)</span><span class="delimiter">'</span></span>, { <span class="key">vid</span>: <span class="integer">1</span> }); const vertex = result1.first(); const result2 = await client.submit(<span class="string"><span class="delimiter">'</span><span class="content">g.V().hasLabel(label).tail(n)</span><span class="delimiter">'</span></span>, { <span class="key">label</span>: <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>, <span class="key">n</span>: <span class="integer">3</span> }); <span class="comment">// ResultSet is an iterable</span> <span class="keyword">for</span> (const vertex of result2) { console.log(vertex.id); }</code></pre> </div> </div> <div class="paragraph"> <p>It is also possible to initialize the <code>Client</code> to use <a href="#sessions">sessions</a>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const client = <span class="keyword">new</span> gremlin.driver.Client(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, { <span class="key">traversalSource</span>: <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>, <span class="key"><span class="delimiter">'</span><span class="content">session</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">unique-string-id</span><span class="delimiter">'</span></span> });</code></pre> </div> </div> <div class="paragraph"> <p>With this configuration, the state of variables within scripts are preserved between requests.</p> </div> <div class="sect3"> <h4 id="_per_request_settings_3">Per Request Settings</h4> <div class="paragraph"> <p>The <code>client.submit()</code> functions accept a <code>requestOptions</code> which expects a dictionary. The <code>requestOptions</code> provide a way to include options that are specific to the request made with the call to <code>submit()</code>. A good use-case for this feature is to set a per-request override to the <code>evaluationTimeout</code> so that it only applies to the current request.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const result = await client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V().repeat(both()).times(100)</span><span class="delimiter">"</span></span>, <span class="predefined-constant">null</span>, { <span class="key">evaluationTimeout</span>: <span class="integer">5000</span> })</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code>, <code>materializeProperties</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated).</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try <code>g.with(EVALUATION_TIMEOUT, 500)</code> within a script. Scripts with multiple traversals and multiple timeouts will be interpreted as a sum of all timeouts identified in the script for that request. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_processing_results_as_they_are_returned_from_the_gremlin_server">Processing results as they are returned from the Gremlin server</h4> <div class="paragraph"> <p>The Gremlin JavaScript driver maintains a WebSocket connection to the Gremlin server and receives messages according to the <code>batchSize</code> parameter on the per request settings or the <code>resultIterationBatchSize</code> value configured for the Gremlin server. When submitting scripts the default behavior is to wait for the entire result set to be returned from a query before allowing any processing on the result set.</p> </div> <div class="paragraph"> <p>The following examples assume that you have 100 vertices in your graph.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const result = await client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.V()</span><span class="delimiter">"</span></span>); console.log(result.toArray()); <span class="comment">// 100 - all the vertices in your graph</span></code></pre> </div> </div> <div class="paragraph"> <p>When working with larger result sets it may be beneficial for memory management to process each chunk of data as it is returned from the gremlin server. The Gremlin JavaScript driver can return a readable stream instead of waiting for the entire result set to be loaded.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const readable = client.stream(<span class="string"><span class="delimiter">"</span><span class="content">g.V()</span><span class="delimiter">"</span></span>, {}, { <span class="key">batchSize</span>: <span class="integer">25</span> }); readable.on(<span class="string"><span class="delimiter">'</span><span class="content">data</span><span class="delimiter">'</span></span>, (data) => { console.log(data.toArray()); <span class="comment">// 25 vertices</span> }) readable.on(<span class="string"><span class="delimiter">'</span><span class="content">error</span><span class="delimiter">'</span></span>, (error) => { console.log(error); <span class="comment">// errors returned from gremlin server</span> }) readable.on(<span class="string"><span class="delimiter">'</span><span class="content">end</span><span class="delimiter">'</span></span>, () => { console.log(<span class="string"><span class="delimiter">'</span><span class="content">query complete</span><span class="delimiter">'</span></span>); <span class="comment">// when the end event is received then all the results have been processed</span> })</code></pre> </div> </div> <div class="paragraph"> <p>If you are using NodeJS >= 10.0, you can asynchronously iterate readable streams:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const readable = client.stream(<span class="string"><span class="delimiter">"</span><span class="content">g.V()</span><span class="delimiter">"</span></span>, {}, { <span class="key">batchSize</span>: <span class="integer">25</span> }); <span class="keyword">try</span> { <span class="keyword">for</span> await (const result of readable) { console.log(<span class="string"><span class="delimiter">'</span><span class="content">data</span><span class="delimiter">'</span></span>, result.toArray()); <span class="comment">// 25 vertices</span> } } <span class="keyword">catch</span> (err) { console.log(err); }</code></pre> </div> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-dsl">Domain Specific Languages</h3> <div class="paragraph"> <p>Developing Gremlin DSLs in JavaScript largely requires extension of existing core classes with use of standalone functions for anonymous traversal spawning. The pattern is demonstrated in the following example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript"><span class="reserved">class</span> SocialTraversal <span class="reserved">extends</span> GraphTraversal { constructor(graph, traversalStrategies, bytecode) { <span class="reserved">super</span>(graph, traversalStrategies, bytecode); } aged(age) { <span class="keyword">return</span> <span class="local-variable">this</span>.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">age</span><span class="delimiter">'</span></span>, age); } } <span class="reserved">class</span> SocialTraversalSource <span class="reserved">extends</span> GraphTraversalSource { constructor(graph, traversalStrategies, bytecode) { <span class="reserved">super</span>(graph, traversalStrategies, bytecode, SocialTraversalSource, SocialTraversal); } person(name) { <span class="keyword">return</span> <span class="local-variable">this</span>.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>, name); } } <span class="keyword">function</span> <span class="function">anonymous</span>() { <span class="keyword">return</span> <span class="keyword">new</span> SocialTraversal(<span class="predefined-constant">null</span>, <span class="predefined-constant">null</span>, <span class="keyword">new</span> Bytecode()); } <span class="keyword">function</span> <span class="function">aged</span>(age) { <span class="keyword">return</span> anonymous().aged(age); }</code></pre> </div> </div> <div class="paragraph"> <p><code>SocialTraversal</code> extends the core <code>GraphTraversal</code> class and has a three argument constructor which is immediately proxied to the <code>GraphTraversal</code> constructor. New DSL steps are then added to this class using available steps to construct the underlying traversal to execute as demonstrated in the <code>aged()</code> step.</p> </div> <div class="paragraph"> <p>The <code>SocialTraversal</code> is spawned from a <code>SocialTraversalSource</code> which is extended from <code>GraphTraversalSource</code>. Steps added here are meant to be start steps. In the above case, the <code>person()</code> start step find a "person" vertex to begin the traversal from.</p> </div> <div class="paragraph"> <p>Typically, steps that are made available on a <code>GraphTraversal</code> (i.e. SocialTraversal in this example) should also be made available as spawns for anonymous traversals. The recommendation is that these steps be exposed in the module as standalone functions. In the example above, the standalone <code>aged()</code> step creates an anonymous traversal through an <code>anonymous()</code> utility function. The method for creating these standalone functions can be handled in other ways if desired.</p> </div> <div class="paragraph"> <p>To use the DSL, simply initialize the <code>g</code> as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">const g = traversal(SocialTraversalSource).withRemote(connection); g.person(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).aged(<span class="integer">29</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).toList(). then(names => console.log(names));</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-differences">Differences</h3> <div class="paragraph"> <p>In situations where Javascript reserved words and global functions overlap with standard Gremlin steps and tokens, those bits of conflicting Gremlin get an underscore appended as a suffix:</p> </div> <div class="paragraph"> <p><strong>Steps</strong> - <a href="#from-step">from_()</a>, <a href="#in-step">in_()</a>, <a href="#with-step">with_()</a> <strong>Tokens</strong> - <code>Direction.from_</code></p> </div> <div class="paragraph"> <p>In addition, the enum construct for <code>Cardinality</code> cannot have functions attached to it the way it can be done in Java, therefore cardinality functions that take a value like <code>list()</code>, <code>set()</code>, and <code>single()</code> are referenced from a <code>CardinalityValue</code> class rather than <code>Cardinality</code> itself.</p> </div> <div class="paragraph"> <p>Gremlin allows for <code>Map</code> instances to include <code>null</code> keys, but <code>null</code> keys in Javascript have some interesting behavior as in:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">> var a = { null: 'something', 'b': 'else' }; > JSON.stringify(a) '{"null":"something","b":"else"}' > JSON.parse(JSON.stringify(a)) { null: 'something', b: 'else' } > a[null] 'something' > a['null'] 'something'</code></pre> </div> </div> <div class="paragraph"> <p>This behavior needs to be considered when using Gremlin to return such results. A typical situation where this might happen is with <code>group()</code> or <code>groupCount()</code> as in:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">g.V().groupCount().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>where "age" is not a valid key for all vertices. In these cases, it will return <code>null</code> for that key and group on that. It may bet better in Javascript to filter away those vertices to avoid the return of <code>null</code> in the returned <code>Map</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="javascript">g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">age</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>) 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 class="paragraph"> <p>Either of the above two options accomplishes the desired goal as both prevent <code>groupCount()</code> from having to process the possibility of <code>null</code>.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-limitations">Limitations</h3> <div class="ulist"> <ul> <li> <p>The <code>subgraph()</code>-step is not supported by any variant that is not running on the Java Virtual Machine as there is no <code>Graph</code> instance to deserialize a result into on the client-side. A workaround is to replace the step with <code>aggregate(local)</code> and then convert those results to something the client can use locally.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="gremlin-javascript-examples">Application Examples</h3> <div class="paragraph"> <p>The TinkerPop source code contains some sample applications that demonstrate the basics of Gremlin-JavaScript. They can be found in GitHub <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-javascript/examples/">here</a> and are designed to connect to a running <a href="#gremlin-server">Gremlin Server</a> configured with the <code>conf/gremlin-server.yaml</code> and <code>conf/gremlin-server-modern.yaml</code> files as included with the standard release packaging.</p> </div> <div class="paragraph"> <p>To run the examples, first download an image of Gremlin Server from Docker Hub:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker pull tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The remote connection and basic Gremlin examples can be run on a clean server, which uses the default configuration file <code>conf/gremlin-server.yaml</code>. To start a clean server, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The traversal examples should be run on a server configured to start with the Modern toy graph, using <code>conf/gremlin-server-modern.yaml</code>. To start a server with the Modern graph preloaded, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server conf/gremlin-server-modern.yaml</code></pre> </div> </div> <div class="paragraph"> <p>Make sure to install all necessary packages:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">npm install</code></pre> </div> </div> <div class="paragraph"> <p>Each example can now be run with the following commands:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">node connections.js node basic-gremlin.js node modern-traversals.js</code></pre> </div> </div> <div class="paragraph"> <p><a id="gremlin-DotNet"></a></p> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-dotnet">Gremlin.Net</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-dotnet-logo.png" alt="gremlin dotnet logo" width="371"></span> Apache TinkerPop’s Gremlin.Net implements Gremlin within the C# language. It targets .NET Standard and can therefore be used on different operating systems and with different .NET frameworks, such as .NET Framework and <a href="https://www.microsoft.com/net/core">.NET Core</a>. Since the C# syntax is very similar to that of Java, it should be easy to switch between Gremlin-Java and Gremlin.Net. The only major syntactical difference is that all method names in Gremlin.Net use PascalCase as opposed to camelCase in Gremlin-Java in order to comply with .NET conventions.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="powershell">nuget install Gremlin.Net</code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-connecting">Connecting</h3> <div class="paragraph"> <p>The pattern for connecting is described in <a href="#connecting-gremlin">Connecting Gremlin</a> and it basically distills down to creating a <code>GraphTraversalSource</code>. A <code>GraphTraversalSource</code> is created from the <code>AnonymousTraversalSource.traversal()</code> method where the "g" provided to the <code>DriverRemoteConnection</code> corresponds to the name of a <code>GraphTraversalSource</code> on the remote end.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">using var remoteConnection = new DriverRemoteConnection(new GremlinClient(new GremlinServer("localhost", 8182)), "g"); var g = Traversal().WithRemote(remoteConnection);</code></pre> </div> </div> <div class="paragraph"> <p>Some connection options can also be set on individual requests using the <code>With()</code> step on the <code>TraversalSource</code>. For instance to set request timeout to 500 milliseconds:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var l = g.With(Tokens.ArgsEvalTimeout, 500).V().Out("knows").Count().ToList();</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated). These options are available as constants on the <code>Gremlin.Net.Driver.Tokens</code> class.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-imports">Common Imports</h3> <div class="paragraph"> <p>There are a number of classes, functions and tokens that are typically used with Gremlin. The following imports provide most of the typical functionality required to use Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource; using static Gremlin.Net.Process.Traversal.__; using static Gremlin.Net.Process.Traversal.P; using static Gremlin.Net.Process.Traversal.Order; using static Gremlin.Net.Process.Traversal.Operator; using static Gremlin.Net.Process.Traversal.Pop; using static Gremlin.Net.Process.Traversal.Scope; using static Gremlin.Net.Process.Traversal.TextP; using static Gremlin.Net.Process.Traversal.Column; using static Gremlin.Net.Process.Traversal.Direction; using static Gremlin.Net.Process.Traversal.Cardinality; using static Gremlin.Net.Process.Traversal.CardinalityValue; using static Gremlin.Net.Process.Traversal.T;</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-configuration">Configuration</h3> <div class="paragraph"> <p>The connection properties for the Gremlin.Net driver can be passed to the <code>GremlinServer</code> instance as keyword arguments:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">hostname</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The hostname that the driver will connect to.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">localhost</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">port</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The port on which Gremlin Server can be reached.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">8182</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enableSsl</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Determines if SSL should be enabled or not. If enabled on the server then it must be enabled on the client.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">false</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">username</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The username to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">password</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>none</em></p></td> </tr> </tbody> </table> <div class="sect3"> <h4 id="_connection_pool">Connection Pool</h4> <div class="paragraph"> <p>It is also possible to configure the <code>ConnectionPool</code> of the Gremlin.Net driver. These configuration options can be set as properties on the <code>ConnectionPoolSettings</code> instance that can be passed to the <code>GremlinClient</code>:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">PoolSize</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The size of the connection pool.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">MaxInProcessPerConnection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The maximum number of in-flight requests that can occur on a connection.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">32</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ReconnectionAttempts</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The number of attempts to get an open connection from the pool to submit a request.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">ReconnectionBaseDelay</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The base delay used for the exponential backoff for the reconnection attempts.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">1 s</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">EnableUserAgentOnConnect</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables sending a user agent to the server during connection requests. More details can be found in provider docs <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_graph_driver_provider_requirements">here</a>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">true</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>A <code>NoConnectionAvailableException</code> is thrown if all connections have reached the <code>MaxInProcessPerConnection</code> limit when a new request comes in. A <code>ServerUnavailableException</code> is thrown if no connection is available to the server to submit a request after <code>ReconnectionAttempts</code> retries.</p> </div> </div> <div class="sect3"> <h4 id="_websocket_configuration">WebSocket Configuration</h4> <div class="paragraph"> <p>The WebSocket connections can also be configured, directly as parameters of the <code>GremlinClient</code> constructor. It takes an optional delegate <code>webSocketConfiguration</code> that will be invoked for each connection. This makes it possible to configure more advanced options like the <code>KeepAliveInterval</code> or client certificates.</p> </div> <div class="paragraph"> <p>Starting with .NET 6, it is also possible to use compression for WebSockets. This is enabled by default starting with TinkerPop 3.5.3 (again, only on .NET 6 or higher). Note that compression might make an application susceptible to attacks like CRIME/BREACH. Compression should therefore be turned off if the application sends sensitive data to the server as well as data that could potentially be controlled by an untrusted user. Compression can be disabled via the <code>disableCompression</code> parameter.</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-logging">Logging</h3> <div class="paragraph"> <p>It is possible to enable logging for the Gremlin.Net driver by providing an <code>ILoggerFactory</code> (from the <code>Microsoft.Extensions.Logging.Abstractions</code> package) to the <code>GremlinClient</code> constructor:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); var client = new GremlinClient(new GremlinServer("localhost", 8182), loggerFactory: loggerFactory);</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-serialization">Serialization</h3> <div class="paragraph"> <p>The Gremlin.Net driver uses by default GraphBinary but it is also possible to use another serialization format by passing a message serializer when creating the <code>GremlinClient</code>.</p> </div> <div class="paragraph"> <p>GraphSON 3.0 can be configured like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON3MessageSerializer());</code></pre> </div> </div> <div class="paragraph"> <p>and GraphSON 2.0 like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2MessageSerializer());</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-strategies">Traversal Strategies</h3> <div class="paragraph"> <p>In order to add and remove traversal strategies from a traversal source, Gremlin.Net has an <code>AbstractTraversalStrategy</code> class along with a collection of subclasses that mirror the standard Gremlin-Java strategies.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g = g.WithStrategies(new SubgraphStrategy(vertices: HasLabel("person"), edges: Has("weight", Gt(0.5)))); var names = g.V().Values<string>("name").ToList(); // names: [marko, vadas, josh, peter] g = g.WithoutStrategies(typeof(SubgraphStrategy)); names = g.V().Values<string>("name").ToList(); // names: [marko, vadas, lop, josh, ripple, peter] var edgeValueMaps = g.V().OutE().ValueMap<object, object>().With(WithOptions.Tokens).ToList(); // edgeValueMaps: [[label:created, id:9, weight:0.4], [label:knows, id:7, weight:0.5], [label:knows, id:8, weight:1.0], // [label:created, id:10, weight:1.0], [label:created, id:11, weight:0.4], [label:created, id:12, weight:0.2]] g = g.WithComputer(workers: 2, vertices: Has("name", "marko")); names = g.V().Values<string>("name").ToList(); // names: [marko] edgeValueMaps = g.V().OutE().ValueMap<object, object>().With(WithOptions.Tokens).ToList(); // edgeValueMaps: [[label:created, id:9, weight:0.4], [label:knows, id:7, weight:0.5], [label:knows, id:8, weight:1.0]]</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Many of the TraversalStrategy classes in Gremlin.Net are proxies to the respective strategy on Apache TinkerPop’s JVM-based Gremlin traversal machine. As such, their <code>Apply(ITraversal)</code> method does nothing. However, the strategy is encoded in the Gremlin.Net bytecode and transmitted to the Gremlin traversal machine for re-construction machine-side. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-transactions">Transactions</h3> <div class="paragraph"> <p>To get a full understanding of this section, it would be good to start by reading the <a href="#transactions">Transactions</a> section of this documentation, which discusses transactions in the general context of TinkerPop itself. This section builds on that content by demonstrating the transactional syntax for C#.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">using var gremlinClient = new GremlinClient(new GremlinServer("localhost", 8182)); var g = Traversal().WithRemote(new DriverRemoteConnection(gremlinClient)); var tx = g.Tx(); // create a transaction // spawn a new GraphTraversalSource binding all traversals established from it to tx var gtx = tx.Begin(); // execute traversals using gtx occur within the scope of the transaction held by tx. the // tx is closed after calls to CommitAsync or RollbackAsync and cannot be re-used. simply spawn a // new Transaction from g.Tx() to create a new one as needed. the g context remains // accessible through all this as a sessionless connection. try { await gtx.AddV("person").Property("name", "jorge").Promise(t => t.Iterate()); await gtx.AddV("person").Property("name", "josh").Promise(t => t.Iterate()); await tx.CommitAsync(); } catch (Exception) { await tx.RollbackAsync(); }</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-lambda">The Lambda Solution</h3> <div class="paragraph"> <p>Supporting <a href="https://en.wikipedia.org/wiki/Anonymous_function">anonymous functions</a> across languages is difficult as most languages do not support lambda introspection and thus, code analysis. While Gremlin.Net doesn’t support C# lambdas, it is still able to represent lambdas in other languages. When the lambda is represented in <code>Bytecode</code> its language is encoded such that the remote connection host can infer which translator and ultimate execution engine to use.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g.V().Out().Map<int>(Lambda.Groovy("it.get().value('name').length()")).Sum<int>().ToList(); <span class="invisible">//</span><b class="conum">1</b> g.V().Out().Map<int>(Lambda.Python("lambda x: len(x.get().value('name'))")).Sum<int>().ToList(); <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p><code>Lambda.Groovy()</code> can be used to create a Groovy lambda.</p> </li> <li> <p><code>Lambda.Python()</code> can be used to create a Python lambda.</p> </li> </ol> </div> <div class="paragraph"> <p>The <code>ILambda</code> interface returned by these two methods inherits interfaces like <code>IFunction</code> and <code>IPredicate</code> that mirror their Java counterparts which makes it possible to use lambdas with Gremlin.Net for the same steps as in Gremlin-Java.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When running into situations where Groovy cannot properly discern a method signature based on the <code>Lambda</code> instance created, it will help to fully define the closure in the lambda expression - so rather than <code>Lambda.Groovy("it.get().value('name'))</code>, prefer <code>Lambda.Groovy("x → x.get().value('name'))</code>. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-scripts">Submitting Scripts</h3> <div class="paragraph"> <p>Gremlin scripts are sent to the server from a <code>IGremlinClient</code> instance. A <code>IGremlinClient</code> is created as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var gremlinServer = new GremlinServer("localhost", 8182); using var gremlinClient = new GremlinClient(gremlinServer); var response = await gremlinClient.SubmitWithSingleResultAsync<string>("g.V().has('person','name','marko')");</code></pre> </div> </div> <div class="paragraph"> <p>If the remote system has authentication and SSL enabled, then the <code>GremlinServer</code> object can be configured as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var username = "username"; var password = "password"; var gremlinServer = new GremlinServer("localhost", 8182, true, username, password);</code></pre> </div> </div> <div class="paragraph"> <p>It is also possible to initialize the <code>Client</code> to use <a href="#sessions">sessions</a>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var gremlinServer = new GremlinServer("localhost", 8182); var client = new GremlinClient(gremlinServer, sessionId: Guid.NewGuid().ToString()))</code></pre> </div> </div> <div class="sect3"> <h4 id="_per_request_settings_4">Per Request Settings</h4> <div class="paragraph"> <p>The <code>GremlinClient.Submit()</code> functions accept an option to build a raw <code>RequestMessage</code>. A good use-case for this feature is to set a per-request override to the <code>evaluationTimeout</code> so that it only applies to the current request.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var gremlinServer = new GremlinServer("localhost", 8182); using var gremlinClient = new GremlinClient(gremlinServer); var response = await gremlinClient.SubmitWithSingleResultAsync<string>( RequestMessage.Build(Tokens.OpsEval). AddArgument(Tokens.ArgsGremlin, "g.V().count()"). AddArgument(Tokens.ArgsEvalTimeout, 500). Create());</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code>, <code>materializeProperties</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated). These options are available as constants on the <code>Gremlin.Net.Driver.Tokens</code> class.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try <code>g.with(EVALUATION_TIMEOUT, 500)</code> within a script. Scripts with multiple traversals and multiple timeouts will be interpreted as a sum of all timeouts identified in the script for that request. </td> </tr> </table> </div> <div class="paragraph"> <p><a id="gremlin-net-dsl"></a></p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-dsl">Domain Specific Languages</h3> <div class="paragraph"> <p>Developing a <a href="#dsl">Domain Specific Language</a> (DSL) for .Net is most easily implemented using <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods">Extension Methods</a> as they don’t require direct extension of classes in the TinkerPop hierarchy. Extension Method classes simply need to be constructed for the <code>GraphTraversal</code> and the <code>GraphTraversalSource</code>. Unfortunately, anonymous traversals (spawned from <code>__</code>) can’t use the Extension Method approach as they do not work for static classes and static classes can’t be extended. The only option is to re-implement the methods of <code>__</code> as a wrapper in the anonymous traversal for the DSL or to simply create a static class for the DSL and use the two anonymous traversals creators independently. The following example uses the latter approach as it saves a lot of boilerplate code with the minor annoyance of having a second static class to deal with when writing traversals rather than just calling <code>__</code> for everything.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">namespace Dsl { public static class SocialTraversalExtensions { public static GraphTraversal<Vertex,Vertex> Knows(this GraphTraversal<Vertex,Vertex> t, string personName) { return t.Out("knows").HasLabel("person").Has("name", personName); } public static GraphTraversal<Vertex, int> YoungestFriendsAge(this GraphTraversal<Vertex,Vertex> t) { return t.Out("knows").HasLabel("person").Values<int>("age").Min<int>(); } public static GraphTraversal<Vertex,long> CreatedAtLeast(this GraphTraversal<Vertex,Vertex> t, long number) { return t.OutE("created").Count().Is(P.Gte(number)); } } public static class __Social { public static GraphTraversal<object,Vertex> Knows(string personName) { return __.Out("knows").HasLabel("person").Has("name", personName); } public static GraphTraversal<object, int> YoungestFriendsAge() { return __.Out("knows").HasLabel("person").Values<int>("age").Min<int>(); } public static GraphTraversal<object,long> CreatedAtLeast(long number) { return __.OutE("created").Count().Is(P.Gte(number)); } } public static class SocialTraversalSourceExtensions { public static GraphTraversal<Vertex,Vertex> Persons(this GraphTraversalSource g, params string[] personNames) { GraphTraversal<Vertex,Vertex> t = g.V().HasLabel("person"); if (personNames.Length > 0) { t = t.Has("name", P.Within(personNames)); } return t; } } }</code></pre> </div> </div> <div class="paragraph"> <p>Note the creation of <code>__Social</code> as the Social DSL’s "extension" to the available ways in which to spawn anonymous traversals. The use of the double underscore prefix in the name is just a convention to consider using and is not a requirement. To use the DSL, bring it into scope with the <code>using</code> directive:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">using Dsl; using static Dsl.__Social;</code></pre> </div> </div> <div class="paragraph"> <p>and then it can be called from the application as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">var connection = new DriverRemoteConnection(new GremlinClient(new GremlinServer("localhost", 8182))); var social = Traversal().WithRemote(connection); social.Persons("marko").Knows("josh"); social.Persons("marko").YoungestFriendsAge(); social.Persons().Filter(CreatedAtLeast(2)).Count();</code></pre> </div> </div> <div class="paragraph"> <p><a id="gremlin-net-differences"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-differences">Differences</h3> <div class="paragraph"> <p>The biggest difference between Gremlin in .NET and the canonical version in Java is the casing of steps. Canonical Gremlin utilizes <code>camelCase</code> as is typical in Java for function names, but C# utilizes <code>PascalCase</code> as it is more typical in that language. Therefore, when viewing a typical Gremlin example written in Gremlin Console, the conversion to C# usually just requires capitalization of the first letter in the step name, thus the following example in Groovy:</p> </div> <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>). out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>). elementMap().toList()</code></pre> </div> </div> <div class="paragraph"> <p>would become the following in C#:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g.V().Has("Person","name","marko"). Out("knows"). ElementMap().ToList();</code></pre> </div> </div> <div class="paragraph"> <p>In addition to the uppercase change, also note the conversion of the single quotes to double quotes as is expected for declaring string values in C# and the addition of the semi-colon at the end of the line. In short, don’t forget to apply the common syntax expectations for C# when trying to convert an example of Gremlin from a different language.</p> </div> <div class="paragraph"> <p>Another common conversion issues lies in having to explicitly define generics, which can make canonical Gremlin appear much more complex in C# where type erasure is not a feature of the language. For example, the following example in Groovy:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.V().repeat(__.out()).times(<span class="integer">2</span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>must be written as:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g.V().Repeat(__.Out()).Times(2).Values<string>("name");</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin allows for <code>Map</code> instances to include <code>null</code> keys, but <code>null</code> keys in C# <code>Dictionary</code> instances are not allowed. It is therefore necessary to rewrite a traversal such as:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g.V().GroupCount<object>().By("age")</code></pre> </div> </div> <div class="paragraph"> <p>where "age" is not a valid key for all vertices in a way that will remove the need for a <code>null</code> to be returned.</p> </div> <div class="paragraph"> <p>Finally, the enum construct for <code>Cardinality</code> cannot have functions attached to it the way it can be done in Java, therefore cardinality functions that take a value like <code>list()</code>, <code>set()</code>, and <code>single()</code> are referenced from a <code>CardinalityValue</code> class rather than <code>Cardinality</code> itself.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="csharp">g.V().Has("age").GroupCount<object>().By("age") g.V().HasLabel("person").GroupCount<object>().By("age")</code></pre> </div> </div> <div class="paragraph"> <p>Either of the above two options accomplishes the desired goal as both prevent <code>groupCount()</code> from having to process the possibility of <code>null</code>.</p> </div> <div class="paragraph"> <p><a id="gremlin-net-limitations"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-limitations">Limitations</h3> <div class="ulist"> <ul> <li> <p>The <code>subgraph()</code>-step is not supported by any variant that is not running on the Java Virtual Machine as there is no <code>Graph</code> instance to deserialize a result into on the client-side. A workaround is to replace the step with <code>aggregate(local)</code> and then convert those results to something the client can use locally.</p> </li> </ul> </div> <div class="paragraph"> <p><a id="gremlin-dotnet-template"></a> <a id="dotnet-application-examples"></a> <a id="gremlin-net-examples"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-startup">Getting Started</h3> <div class="paragraph"> <p>This <a href="https://docs.microsoft.com/dotnet/core/tools/custom-templates">dotnet template</a> helps getting started with <a href="#gremlin-dotnet">Gremlin.Net</a>. It creates a new C# console project that shows how to connect to a <a href="#gremlin-server">Gremlin Server</a> with Gremlin.Net.</p> </div> <div class="paragraph"> <p>You can install the template with the dotnet CLI tool:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">dotnet new -i Gremlin.Net.Template</code></pre> </div> </div> <div class="paragraph"> <p>After the template is installed, a new project based on this template can be installed:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">dotnet new gremlin</code></pre> </div> </div> <div class="paragraph"> <p>Specify the output directory for the new project which will then also be used as the name of the created project:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">dotnet new gremlin -o MyFirstGremlinProject</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-dotnet-examples">Application Examples</h3> <div class="paragraph"> <p>The TinkerPop source code contains some sample applications that demonstrate the basics of Gremlin-Dotnet. They can be found in GitHub <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-dotnet/Examples/">here</a> and are designed to connect to a running <a href="#gremlin-server">Gremlin Server</a> configured with the <code>conf/gremlin-server.yaml</code> and <code>conf/gremlin-server-modern.yaml</code> files as included with the standard release packaging.</p> </div> <div class="paragraph"> <p>To run the examples, first download an image of Gremlin Server from Docker Hub:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker pull tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The remote connection and basic Gremlin examples can be run on a clean server, which uses the default configuration file <code>conf/gremlin-server.yaml</code>. To start a clean server, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The traversal examples should be run on a server configured to start with the Modern toy graph, using <code>conf/gremlin-server-modern.yaml</code>. To start a server with the Modern graph preloaded, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server conf/gremlin-server-modern.yaml</code></pre> </div> </div> <div class="paragraph"> <p>Each example can now be run with the following command in their respective project directories:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">dotnet run</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="gremlin-python">Gremlin-Python</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image right"><img src="../images/gremlin-python-drawing.png" alt="gremlin python drawing" width="130"></span> Apache TinkerPop’s Gremlin-Python implements Gremlin within the <a href="https://www.python.org/">Python</a> language and can be used on any Python virtual machine including the popular <a href="https://en.wikipedia.org/wiki/CPython">CPython</a> machine. Python’s syntax has the same constructs as Java including "dot notation" for function chaining (<code>a.b.c</code>), round bracket function arguments (<code>a(b,c)</code>), and support for global namespaces (<code>a(b())</code> vs <code>a(__.b())</code>). As such, anyone familiar with Gremlin-Java will immediately be able to work with Gremlin-Python. Moreover, there are a few added constructs to Gremlin-Python that make traversals a bit more succinct.</p> </div> <div class="paragraph"> <p>To install Gremlin-Python, use Python’s <a href="https://en.wikipedia.org/wiki/Pip_(package_manager)">pip</a> package manager.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">pip install gremlinpython pip install gremlinpython[kerberos] # Optional, not available on Microsoft Windows</code></pre> </div> </div> <div class="sect2"> <h3 id="gremlin-python-connecting">Connecting</h3> <div class="paragraph"> <p>The pattern for connecting is described in <a href="#connecting-gremlin">Connecting Gremlin</a> and it basically distills down to creating a <code>GraphTraversalSource</code>. A <code>GraphTraversalSource</code> is created from the anonymous <code>traversal()</code> method where the "g" provided to the <code>DriverRemoteConnection</code> corresponds to the name of a <code>GraphTraversalSource</code> on the remote end.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">g = traversal().with_remote(DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> <div class="paragraph"> <p>If you need to send additional headers in the websockets connection, you can pass an optional <code>headers</code> parameter to the <code>DriverRemoteConnection</code> constructor.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">g = traversal().with_remote(DriverRemoteConnection( <span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>, headers={<span class="string"><span class="delimiter">'</span><span class="content">Header</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">Value</span><span class="delimiter">'</span></span>}))</code></pre> </div> </div> <div class="paragraph"> <p>Gremlin-Python supports plain text and Kerberos SASL authentication, you can set it on the connection options.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="comment"># Plain text authentication</span> g = traversal().with_remote(DriverRemoteConnection( <span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>, username=<span class="string"><span class="delimiter">'</span><span class="content">stephen</span><span class="delimiter">'</span></span>, password=<span class="string"><span class="delimiter">'</span><span class="content">password</span><span class="delimiter">'</span></span>)) <span class="comment"># Kerberos authentication</span> g = traversal().with_remote(DriverRemoteConnection( <span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>, kerberized_service=<span class="string"><span class="delimiter">'</span><span class="content">gremlin@hostname.your.org</span><span class="delimiter">'</span></span>))</code></pre> </div> </div> <div class="paragraph"> <p>The value specified for the kerberized_service should correspond to the first part of the principal name configured for the gremlin service, but with the slash replaced by an <em>at</em> sign. The Gremlin-Python client reads the kerberos configurations from your system. It finds the KDC’s hostname and port from the krb5.conf file at the <a href="https://web.mit.edu/kerberos/krb5-devel/doc/mitK5defaults.html">default location</a> or as indicated in the KRB5_CONFIG environment variable. It finds credentials from the credential cache or a keytab file at the <a href="https://web.mit.edu/kerberos/krb5-devel/doc/mitK5defaults.html">default locations</a> or as indicated in the KRB5CCNAME or KRB5_KTNAME environment variables.</p> </div> <div class="paragraph"> <p>If you authenticate to a remote <a href="#connecting-gremlin-server">Gremlin Server</a> or <a href="#connecting-rgp">Remote Gremlin Provider</a>, this server normally has SSL activated and the websockets url will start with 'wss://'. If Gremlin-Server uses a self-signed certificate for SSL, Gremlin-Python needs access to a local copy of the CA certificate file (in openssl .pem format), to be specified in the SSL_CERT_FILE environment variable.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> If connecting from an inherently single-threaded Python process where blocking while waiting for Gremlin traversals to complete is acceptable, it might be helpful to set <code>pool_size</code> and <code>max_workers</code> parameters to 1. See the <a href="#python-configuration">Configuration</a> section just below. Examples where this could apply are serverless cloud functions or WSGI worker processes. </td> </tr> </table> </div> <div class="paragraph"> <p>Some connection options can also be set on individual requests made through the using <code>with()</code> step on the <code>TraversalSource</code>. For instance to set request timeout to 500 milliseconds:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">vertices = g.with_(<span class="string"><span class="delimiter">'</span><span class="content">evaluationTimeout</span><span class="delimiter">'</span></span>, <span class="integer">500</span>).V().out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).to_list()</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated).</p> </div> <div class="paragraph"> <p><a id="python-imports"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-python-imports">Common Imports</h3> <div class="paragraph"> <p>There are a number of classes, functions and tokens that are typically used with Gremlin. The following imports provide most of the typical functionality required to use Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">from</span> <span class="include">gremlin_python</span> <span class="keyword">import</span> <span class="include">statics</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.anonymous_traversal</span> <span class="keyword">import</span> <span class="include">traversal</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.graph_traversal</span> <span class="keyword">import</span> <span class="include">__</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.strategies</span> <span class="keyword">import</span> <span class="include">*</span> <span class="keyword">from</span> <span class="include">gremlin_python.driver.driver_remote_connection</span> <span class="keyword">import</span> <span class="include">DriverRemoteConnection</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">T</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Order</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Cardinality</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">CardinalityValue</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Column</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Direction</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Operator</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">P</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">TextP</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Pop</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Scope</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Barrier</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">Bindings</span> <span class="keyword">from</span> <span class="include">gremlin_python.process.traversal</span> <span class="keyword">import</span> <span class="include">WithOptions</span></code></pre> </div> </div> <div class="paragraph"> <p>These can be used analogously to how they are used in Gremlin-Java.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V().has_label(<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">age</span><span class="delimiter">'</span></span>,P.gt(<span class="integer">30</span>)).order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,Order.desc).to_list() [v[<span class="integer">6</span>], v[<span class="integer">4</span>]]</code></pre> </div> </div> <div class="paragraph"> <p>Moreover, by importing the <code>statics</code> of Gremlin-Python, the class prefixes can be omitted.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> statics.load_statics(<span class="predefined">globals</span>())</code></pre> </div> </div> <div class="paragraph"> <p>With statics loaded its possible to represent the above traversal as below.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V().has_label(<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">age</span><span class="delimiter">'</span></span>,gt(<span class="integer">30</span>)).order().by(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>,desc).to_list() [v[<span class="integer">6</span>], v[<span class="integer">4</span>]]</code></pre> </div> </div> <div class="paragraph"> <p>Statics includes all the <code>__</code>-methods and thus, anonymous traversals like <code>__.out()</code> can be expressed as below. That is, without the <code>__</code>-prefix.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V().repeat(out()).times(<span class="integer">2</span>).name.fold().to_list() [[<span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>]]</code></pre> </div> </div> <div class="paragraph"> <p>There may be situations where certain graphs may want a more exact data type than what Python will allow as a language. To support these situations <code>gremlin-python</code> has a few special type classes that can be imported from <code>statics</code>. They include:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">from</span> <span class="include">gremlin_python.statics</span> <span class="keyword">import</span> <span class="include">long</span> <span class="comment"># Java long</span> <span class="keyword">from</span> <span class="include">gremlin_python.statics</span> <span class="keyword">import</span> <span class="include">timestamp</span> <span class="comment"># Java timestamp</span> <span class="keyword">from</span> <span class="include">gremlin_python.statics</span> <span class="keyword">import</span> <span class="include">SingleByte</span> <span class="comment"># Java byte type</span> <span class="keyword">from</span> <span class="include">gremlin_python.statics</span> <span class="keyword">import</span> <span class="include">SingleChar</span> <span class="comment"># Java char type</span> <span class="keyword">from</span> <span class="include">gremlin_python.statics</span> <span class="keyword">import</span> <span class="include">GremlinType</span> <span class="comment"># Java Class</span></code></pre> </div> </div> <div class="paragraph"> <p><a id="python-configuration"></a></p> </div> </div> <div class="sect2"> <h3 id="gremlin-python-configuration">Configuration</h3> <div class="paragraph"> <p>The following table describes the various configuration options for the Gremlin-Python Driver. They can be passed to the <code>Client</code> or <code>DriverRemoteConnection</code> instance as keyword arguments:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 20%;"> <col style="width: 66.6666%;"> <col style="width: 13.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Key</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-center valign-top">Default</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enable_compression</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables sending a user agent to the server during connection requests.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">False</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">enable_user_agent_on_connect</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enables sending a user agent to the server during connection requests. More details can be found in provider docs <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#_graph_driver_provider_requirements">here</a>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">True</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">headers</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Additional headers that will be added to each request message.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>None</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">kerberized_service</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">the first part of the principal name configured for the gremlin service</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">"""</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">max_workers</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Maximum number of worker threads.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">Number of CPUs * 5</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">message_serializer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The message serializer implementation.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>gremlin_python.driver.serializer.GraphBinarySerializersV1</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">password</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The password to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">""</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">pool_size</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The number of connections used by the pool.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">4</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">protocol_factory</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A callable that returns an instance of <code>AbstractBaseProtocol</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>gremlin_python.driver.protocol.GremlinServerWSProtocol</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">session</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A unique string-based identifier (typically a UUID) to enable a <a href="#sessions">session-based connection</a>. This is not a valid configuration for <code>DriverRemoteConnection</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">None</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">transport_factory</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A callable that returns an instance of <code>AbstractBaseTransport</code>.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>gremlin_python.driver.aiohttp.transport.AiohttpTransport</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">username</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The username to submit on requests that require authentication.</p></td> <td class="tableblock halign-center valign-top"><p class="tableblock">""</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Note that the <code>transport_factory</code> can allow for additional configuration of the <code>AiohttpTransport</code>, which allows pass through of the named parameters available in <a href="https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession.ws_connect">AIOHTTP’s ws_connect</a>, and the ability to call the api from an event loop:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">import</span> <span class="include">ssl</span> ... g = traversal().with_remote( DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>, transport_factory=<span class="keyword">lambda</span>: AiohttpTransport(read_timeout=<span class="integer">60</span>, write_timeout=<span class="integer">20</span>, heartbeat=<span class="integer">10</span>, call_from_event_loop=<span class="predefined-constant">True</span>, max_content_length=<span class="integer">100</span>*<span class="integer">1024</span>*<span class="integer">1024</span>, ssl_options=ssl.create_default_context(Purpose.CLIENT_AUTH))))</code></pre> </div> </div> <div class="paragraph"> <p>Note that the <code>heartbeat</code> enables keep-alive functionality within aiohttp and it is not enabled by default. It is important that the heartbeat interval is not too short, as the wait for the server response to the heartbeat request is half the amount of this value. Therefore, if the heartbeat is ten seconds then the wait for the response is just five seconds. If the response is not received in that time period then the connection will be closed and any ongoing requests on that connection will fail to retrieve results. Therefore, if the heartbeat is set to one second, it only provides a half-second to get the response which raises the possibility considerably that the connection will be inadvertently closed.</p> </div> <div class="paragraph"> <p>Compression configuration options are described in the <a href="https://docs.python.org/3.6/library/zlib.html#zlib.compressobj">zlib documentation</a>. By default, compression settings are configured as shown in the above example.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-python-strategies">Traversal Strategies</h3> <div class="paragraph"> <p>In order to add and remove <a href="#traversalstrategy">traversal strategies</a> from a traversal source, Gremlin-Python has a <code>TraversalStrategy</code> class along with a collection of subclasses that mirror the standard Gremlin-Java strategies.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g = g.with_strategies(SubgraphStrategy(vertices=has_label(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>),edges=has(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>,gt(<span class="float">0.5</span>)))) >>> g.V().name.to_list() [<span class="string"><span class="delimiter">'</span><span class="content">marko</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="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>] >>> g.V().out_e().element_map().to_list() [{<T.id: <span class="integer">1</span>>: <span class="integer">8</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">4</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">1</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">1.0</span>}] >>> g = g.without_strategies(SubgraphStrategy) >>> g.V().name.to_list() [<span class="string"><span class="delimiter">'</span><span class="content">marko</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="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>] >>> g.V().out_e().element_map().to_list() [{<T.id: <span class="integer">1</span>>: <span class="integer">9</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">3</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">1</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">0.4</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">7</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">2</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">1</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">0.5</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">8</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">4</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">1</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">1.0</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">10</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">5</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">4</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">1.0</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">11</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">3</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">4</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">0.4</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">12</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>, <Direction.IN: <span class="integer">2</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">3</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">software</span><span class="delimiter">'</span></span>}, <Direction.OUT: <span class="integer">3</span>>: {<T.id: <span class="integer">1</span>>: <span class="integer">6</span>, <T.label: <span class="integer">4</span>>: <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">weight</span><span class="delimiter">'</span></span>: <span class="float">0.2</span>}] >>> g = g.with_computer(workers=<span class="integer">2</span>,vertices=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().name.to_list() [<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>] >>> g.V().out_e().value_map().with_(WithOptions.tokens).to_list() [{<T.id: <span class="integer">1</span>>: <span class="integer">9</span>, <T.label: <span class="integer">4</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">weight</span><span class="delimiter">'</span></span>: <span class="float">0.4</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">7</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>: <span class="float">0.5</span>}, {<T.id: <span class="integer">1</span>>: <span class="integer">8</span>, <T.label: <span class="integer">4</span>>: <span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>: <span class="float">1.0</span>}]</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Many of the <code>TraversalStrategy</code> classes in Gremlin-Python are proxies to the respective strategy on Apache TinkerPop’s JVM-based Gremlin traversal machine. As such, their <code>apply(Traversal)</code> method does nothing. However, the strategy is encoded in the Gremlin-Python bytecode and transmitted to the Gremlin traversal machine for re-construction machine-side. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-python-transactions">Transactions</h3> <div class="paragraph"> <p>To get a full understanding of this section, it would be good to start by reading the <a href="#transactions">Transactions</a> section of this documentation, which discusses transactions in the general context of TinkerPop itself. This section builds on that content by demonstrating the transactional syntax for Python.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">g = traversal().with_remote(DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>)) <span class="comment"># Create a Transaction.</span> tx = g.tx() <span class="comment"># Spawn a new GraphTraversalSource, binding all traversals established from it to tx.</span> gtx = tx.begin() <span class="keyword">try</span>: <span class="comment"># Execute a traversal within the transaction.</span> gtx.add_v(<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">Lyndon</span><span class="delimiter">"</span></span>).iterate(), <span class="comment"># Commit the transaction. The transaction can no longer be used and cannot be re-used.</span> <span class="comment"># A new transaction can be spawned through g.tx().</span> <span class="comment"># The context of g remains sessionless throughout the process.</span> tx.commit() <span class="keyword">except</span> <span class="exception">Exception</span> <span class="keyword">as</span> e: <span class="comment"># Rollback the transaction if an error occurs.</span> tx.rollback()</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-python-lambda">The Lambda Solution</h3> <div class="paragraph"> <p>Supporting <a href="https://en.wikipedia.org/wiki/Anonymous_function">anonymous functions</a> across languages is difficult as most languages do not support lambda introspection and thus, code analysis. In Gremlin-Python, a Gremlin lambda should be represented as a zero-arg callable that returns a string representation of the lambda expected for use in the traversal. The lambda should be written as a <code>Gremlin-Groovy</code> string. When the lambda is represented in <code>Bytecode</code> its language is encoded such that the remote connection host can infer which translator and ultimate execution engine to use.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V().out().map(<span class="keyword">lambda</span>: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>).sum().to_list() [<span class="integer">24</span>]</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> When running into situations where Groovy cannot properly discern a method signature based on the <code>Lambda</code> instance created, it will help to fully define the closure in the lambda expression - so rather than <code>lambda: ('it.get().value('name')','gremlin-groovy')</code>, prefer <code>lambda: ('x → x.get().value('name'),'gremlin-groovy')</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>Finally, Gremlin <code>Bytecode</code> that includes lambdas requires that the traversal be processed by the <code>ScriptEngine</code>. To avoid continued recompilation costs, it supports the encoding of bindings, which allow a remote engine to to cache traversals that will be reused over and over again save that some parameterization may change. Thus, instead of translating, compiling, and then executing each submitted bytecode, it is possible to simply execute.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V(Bindings.of(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,<span class="integer">1</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).map(<span class="keyword">lambda</span>: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>).sum_().to_list() [<span class="integer">3</span>] >>> g.V(Bindings.of(<span class="string"><span class="delimiter">'</span><span class="content">x</span><span class="delimiter">'</span></span>,<span class="integer">4</span>)).out(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).map(<span class="keyword">lambda</span>: <span class="string"><span class="delimiter">"</span><span class="content">it.get().value('name').length()</span><span class="delimiter">"</span></span>).sum_().to_list() [<span class="integer">9</span>]</code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> As explained throughout the documentation, when possible <a href="#a-note-on-lambdas">avoid</a> lambdas. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="gremlin-python-scripts">Submitting Scripts</h3> <div class="paragraph"> <p>The <code>Client</code> class implementation/interface is based on the Java Driver, with some restrictions. Most notably, Gremlin-Python does not yet implement the <code>Cluster</code> class. Instead, <code>Client</code> is instantiated directly. Usage is as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">from</span> <span class="include">gremlin_python.driver</span> <span class="keyword">import</span> <span class="include">client</span> <span class="invisible">//</span><b class="conum">1</b> client = client.Client(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Import the Gremlin-Python <code>client</code> module.</p> </li> <li> <p>Opens a reference to <code>localhost</code> - note that there are various configuration options that can be passed to the <code>Client</code> object upon instantiation as keyword arguments.</p> </li> </ol> </div> <div class="paragraph"> <p>Once a <code>Client</code> instance is ready, it is possible to issue some Gremlin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">result_set = client.submit(<span class="string"><span class="delimiter">'</span><span class="content">[1,2,3,4]</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">1</b> future_results = result_set.all() <span class="invisible">//</span><b class="conum">2</b> results = future_results.result() <span class="invisible">//</span><b class="conum">3</b> <span class="keyword">assert</span> results == [<span class="integer">1</span>, <span class="integer">2</span>, <span class="integer">3</span>, <span class="integer">4</span>] <span class="invisible">//</span><b class="conum">4</b> future_result_set = client.submit_async(<span class="string"><span class="delimiter">'</span><span class="content">[1,2,3,4]</span><span class="delimiter">'</span></span>) <span class="invisible">//</span><b class="conum">5</b> result_set = future_result_set.result() <span class="invisible">//</span><b class="conum">6</b> result = result_set.one() <span class="invisible">//</span><b class="conum">7</b> <span class="keyword">assert</span> results == [<span class="integer">1</span>, <span class="integer">2</span>, <span class="integer">3</span>, <span class="integer">4</span>] <span class="invisible">//</span><b class="conum">8</b> <span class="keyword">assert</span> result_set.done.done() <span class="invisible">//</span><b class="conum">9</b> client.close() <span class="invisible">//</span><b class="conum">10</b></code></pre> </div> </div> <div class="colist arabic"> <ol> <li> <p>Submit a script that simply returns a <code>List</code> of integers. This method blocks until the request is written to the server and a <code>ResultSet</code> is constructed.</p> </li> <li> <p>Even though the <code>ResultSet</code> is constructed, it does not mean that the server has sent back the results (or even evaluated the script potentially). The <code>ResultSet</code> is just a holder that is awaiting the results from the server. The <code>all</code> method returns a <code>concurrent.futures.Future</code> that resolves to a list when it is complete.</p> </li> <li> <p>Block until the the script is evaluated and results are sent back by the server.</p> </li> <li> <p>Verify the result.</p> </li> <li> <p>Submit the same script to the server but don’t block.</p> </li> <li> <p>Wait until request is written to the server and <code>ResultSet</code> is constructed.</p> </li> <li> <p>Read a single result off the result stream.</p> </li> <li> <p>Again, verify the result.</p> </li> <li> <p>Verify that the all results have been read and stream is closed.</p> </li> <li> <p>Close client and underlying pool connections.</p> </li> </ol> </div> <div class="sect3"> <h4 id="_per_request_settings_5">Per Request Settings</h4> <div class="paragraph"> <p>The <code>client.submit()</code> functions accept a <code>request_options</code> which expects a dictionary. The <code>request_options</code> provide a way to include options that are specific to the request made with the call to <code>submit()</code>. A good use-case for this feature is to set a per-request override to the <code>evaluationTimeout</code> so that it only applies to the current request.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">result_set = client.submit(<span class="string"><span class="delimiter">'</span><span class="content">g.V().repeat(both()).times(100)</span><span class="delimiter">'</span></span>, request_options={<span class="string"><span class="delimiter">'</span><span class="content">evaluationTimeout</span><span class="delimiter">'</span></span>: <span class="integer">5000</span>})</code></pre> </div> </div> <div class="paragraph"> <p>The following options are allowed on a per-request basis in this fashion: <code>batchSize</code>, <code>requestId</code>, <code>userAgent</code>, <code>materializeProperties</code> and <code>evaluationTimeout</code> (formerly <code>scriptEvaluationTimeout</code> which is also supported but now deprecated).</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try <code>g.with(EVALUATION_TIMEOUT, 500)</code> within a script. Scripts with multiple traversals and multiple timeouts will be interpreted as a sum of all timeouts identified in the script for that request. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">RequestOptions options = RequestOptions.build().timeout(<span class="integer">500</span>).create(); <span class="predefined-type">List</span><<span class="predefined-type">Result</span>> result = client.submit(<span class="string"><span class="delimiter">"</span><span class="content">g.with(EVALUATION_TIMEOUT, 500).addV().iterate();</span><span class="delimiter">"</span></span> + <span class="string"><span class="delimiter">"</span><span class="content">g.addV().iterate(); </span><span class="delimiter">"</span></span>g.with(EVALUATION_TIMEOUT, <span class="integer">500</span>).addV();<span class="string"><span class="delimiter">"</span><span class="content">, options).all().get();</span></span></code></pre> </div> </div> <div class="paragraph"> <p>In the above example, <code>RequestOptions</code> defines a timeout of 500 milliseconds, but the script has three traversals with two internal settings for the timeout using <code>with()</code>. The request timeout used by the server will therefore be 1000 milliseconds (overriding the 500 which itself was an override for whatever configuration was on the server).</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-python-dsl">Domain Specific Languages</h3> <div class="paragraph"> <p>Writing a Gremlin <a href="#dsl">Domain Specific Language</a> (DSL) in Python simply requires direct extension of several classes:</p> </div> <div class="ulist"> <ul> <li> <p><code>GraphTraversal</code> - which exposes the various steps used in traversal writing</p> </li> <li> <p><code>__</code> - which spawns anonymous traversals from steps</p> </li> <li> <p><code>GraphTraversalSource</code> - which spawns <code>GraphTraversal</code> instances</p> </li> </ul> </div> <div class="paragraph"> <p>The Social DSL based on the <a href="https://tinkerpop.apache.org/docs/3.7.3/images/tinkerpop-modern.png">"modern" toy graph</a> might look like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">class</span> <span class="class">SocialTraversal</span>(GraphTraversal): <span class="keyword">def</span> <span class="function">knows</span>(<span class="predefined-constant">self</span>, person_name): <span class="keyword">return</span> <span class="predefined-constant">self</span>.out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has_label(<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>, person_name) <span class="keyword">def</span> <span class="function">youngest_friends_age</span>(<span class="predefined-constant">self</span>): <span class="keyword">return</span> <span class="predefined-constant">self</span>.out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).has_label(<span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>).min() <span class="keyword">def</span> <span class="function">created_at_least</span>(<span class="predefined-constant">self</span>, number): <span class="keyword">return</span> <span class="predefined-constant">self</span>.out_e(<span class="string"><span class="delimiter">'</span><span class="content">created</span><span class="delimiter">'</span></span>).count().is_(P.gte(number)) <span class="keyword">class</span> <span class="class">__</span>(AnonymousTraversal): graph_traversal = SocialTraversal <span class="decorator">@classmethod</span> <span class="keyword">def</span> <span class="function">knows</span>(cls, *args): <span class="keyword">return</span> cls.graph_traversal(<span class="predefined-constant">None</span>, <span class="predefined-constant">None</span>, Bytecode()).knows(*args) <span class="decorator">@classmethod</span> <span class="keyword">def</span> <span class="function">youngest_friends_age</span>(cls, *args): <span class="keyword">return</span> cls.graph_traversal(<span class="predefined-constant">None</span>, <span class="predefined-constant">None</span>, Bytecode()).youngest_friends_age(*args) <span class="decorator">@classmethod</span> <span class="keyword">def</span> <span class="function">created_at_least</span>(cls, *args): <span class="keyword">return</span> cls.graph_traversal(<span class="predefined-constant">None</span>, <span class="predefined-constant">None</span>, Bytecode()).created_at_least(*args) <span class="keyword">class</span> <span class="class">SocialTraversalSource</span>(GraphTraversalSource): <span class="keyword">def</span> <span class="function">__init__</span>(<span class="predefined-constant">self</span>, *args, **kwargs): <span class="predefined">super</span>(SocialTraversalSource, <span class="predefined-constant">self</span>).__init__(*args, **kwargs) <span class="predefined-constant">self</span>.graph_traversal = SocialTraversal <span class="keyword">def</span> <span class="function">persons</span>(<span class="predefined-constant">self</span>, *args): traversal = <span class="predefined-constant">self</span>.get_graph_traversal() traversal.bytecode.add_step(<span class="string"><span class="delimiter">'</span><span class="content">V</span><span class="delimiter">'</span></span>) traversal.bytecode.add_step(<span class="string"><span class="delimiter">'</span><span class="content">hasLabel</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">person</span><span class="delimiter">'</span></span>) <span class="keyword">if</span> <span class="predefined">len</span>(args) > <span class="integer">0</span>: traversal.bytecode.add_step(<span class="string"><span class="delimiter">'</span><span class="content">has</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, P.within(args)) <span class="keyword">return</span> traversal</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>AnonymousTraversal</code> class above is just an alias for <code>__</code> as in <code>from gremlin_python.process.graph_traversal import __ as AnonymousTraversal</code> </td> </tr> </table> </div> <div class="paragraph"> <p>Using the DSL is straightforward and just requires that the graph instance know the <code>SocialTraversalSource</code> should be used:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">social = traversal(SocialTraversalSource).with_remote(DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">g</span><span class="delimiter">'</span></span>)) social.persons(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).knows(<span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>) social.persons(<span class="string"><span class="delimiter">'</span><span class="content">marko</span><span class="delimiter">'</span></span>).youngest_friends_age() social.persons().filter(__.created_at_least(<span class="integer">2</span>)).count()</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-python-sugar">Syntactic Sugar</h3> <div class="paragraph"> <p>Python supports meta-programming and operator overloading. There are three uses of these techniques in Gremlin-Python that makes traversals a bit more concise.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python">>>> g.V().both()[<span class="integer">1</span>:<span class="integer">3</span>].to_list() [v[<span class="integer">2</span>], v[<span class="integer">4</span>]] >>> g.V().both()[<span class="integer">1</span>].to_list() [v[<span class="integer">2</span>]] >>> g.V().both().name.to_list() [<span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">lop</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">lop</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="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">josh</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">josh</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="string"><span class="delimiter">'</span><span class="content">marko</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="string"><span class="delimiter">'</span><span class="content">peter</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">ripple</span><span class="delimiter">'</span></span>]</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-python-differences">Differences</h3> <div class="paragraph"> <p>In situations where Python reserved words and global functions overlap with standard Gremlin steps and tokens, those bits of conflicting Gremlin get an underscore appended as a suffix:</p> </div> <div class="paragraph"> <p><strong>Steps</strong> - <a href="#all-step">all_()</a>, <a href="#and-step">and_()</a>, <a href="#any-step">any_()</a>, <a href="#as-step">as_()</a>, <a href="#filter-step">filter_()</a>, <a href="#from-step">from_()</a>, <a href="#has-step">has_key_</a>, <a href="#id-step">id_()</a>, <a href="#is-step">is_()</a>, <a href="#in-step">in_()</a>, <a href="#max-step">max_()</a>, <a href="#min-step">min_()</a>, <a href="#not-step">not_()</a>, <a href="#or-step">or_()</a>, <a href="#range-step">range_()</a>, <a href="#sum-step">sum_()</a>, <a href="#with-step">with_()</a></p> </div> <div class="paragraph"> <p><strong>Tokens</strong> - <a href="#a-note-on-scopes">Scope.global_</a>, <code>Direction.from_</code>, <code>Operator.sum_</code></p> </div> <div class="paragraph"> <p>In addition, the enum construct for <code>Cardinality</code> cannot have functions attached to it the way it can be done in Java, therefore cardinality functions that take a value like <code>list()</code>, <code>set()</code>, and <code>single()</code> are referenced from a <code>CardinalityValue</code> class rather than <code>Cardinality</code> itself.</p> </div> </div> <div class="sect2"> <h3 id="gremlin-python-limitations">Limitations</h3> <div class="ulist"> <ul> <li> <p>Traversals that return a <code>Set</code> <strong>might</strong> be coerced to a <code>List</code> in Python. In the case of Python, number equality is different from JVM languages which produces different <code>Set</code> results when those types are in use. When this case is detected during deserialization, the <code>Set</code> is coerced to a <code>List</code> so that traversals return consistent results within a collection across different languages. If a <code>Set</code> is needed then convert <code>List</code> results to <code>Set</code> manually.</p> </li> <li> <p>Gremlin is capable of returning <code>Dictionary</code> results that use non-hashable keys (e.g. Dictionary as a key) and Python does not support that at a language level. Using GraphSON 3.0 or GraphBinary (after 3.5.0) makes it possible to return such results. In all other cases, Gremlin that returns such results will need to be re-written to avoid that sort of key.</p> </li> <li> <p>The <code>subgraph()</code>-step is not supported by any variant that is not running on the Java Virtual Machine as there is no <code>Graph</code> instance to deserialize a result into on the client-side. A workaround is to replace the step with <code>aggregate(local)</code> and then convert those results to something the client can use locally.</p> </li> <li> <p>Use of the aiohttp library in the default transport requires the use of asyncio’s event loop to run the async functions. This can be an issue in situations where the application calling Gremlin-Python is already using an event loop. Certain types of event loops can be patched using nest-asyncio which allows Gremlin-Python to proceed without an error like "Cannot run the event loop while another loop is running". This is the preferred approach to avoiding the issue and can be enabled by passing <code>call_from_event_loop=True</code> to the <code>AiohttpTransport</code> class.</p> <div class="paragraph"> <p>However, in situations where the loop cannot be patched (e.g. uvloop), then the current suggested workaround is to run Gremlin-Python in a separate thread. This is not ideal for asynchronous web servers as the number of concurrent connections will be limited by the number of threads the system can handle. The following snippet shows how Gremlin-Python can be called from asynchronous code using a thread.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="python"><span class="keyword">def</span> <span class="function">print_vertices</span>(): g = traversal().withRemote(DriverRemoteConnection(<span class="string"><span class="delimiter">"</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">"</span></span>)) <span class="comment"># Do your traversal.</span> async <span class="keyword">def</span> <span class="function">run_in_thread</span>(): running_loop = asyncio.get_running_loop() <span class="keyword">with</span> ThreadPoolExecutor() <span class="keyword">as</span> pool: await running_loop.run_in_executor(pool, print_vertices)</code></pre> </div> </div> </li> </ul> </div> </div> <div class="sect2"> <h3 id="gremlin-python-examples">Application Examples</h3> <div class="paragraph"> <p>The TinkerPop source code contains some sample applications that demonstrate the basics of Gremlin-Python. They can be found in GitHub <a href="https://github.com/apache/tinkerpop/tree/3.7.3/gremlin-python/src/main/python/examples/">here</a> and are designed to connect to a running <a href="#gremlin-server">Gremlin Server</a> configured with the <code>conf/gremlin-server.yaml</code> and <code>conf/gremlin-server-modern.yaml</code> files as included with the standard release packaging.</p> </div> <div class="paragraph"> <p>To run the examples, first download an image of Gremlin Server from Docker Hub:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker pull tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The remote connection and basic Gremlin examples can be run on a clean server, which uses the default configuration file <code>conf/gremlin-server.yaml</code>. To start a clean server, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server</code></pre> </div> </div> <div class="paragraph"> <p>The traversal examples should be run on a server configured to start with the Modern toy graph, using <code>conf/gremlin-server-modern.yaml</code>. To start a server with the Modern graph preloaded, launch a new container with <code>docker run</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">docker run -d -p 8182:8182 tinkerpop/gremlin-server conf/gremlin-server-modern.yaml</code></pre> </div> </div> <div class="paragraph"> <p>Each example can now be run with the following commands:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">python connections.py python basic_gremlin.py python modern_traversals.py</code></pre> </div> </div> </div> </div> </div> <h1 id="implementations" class="sect0">Implementations</h1> <div class="openblock partintro"> <div class="content"> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-racecar.png" alt="gremlin racecar" width="325"> </div> </div> <div class="paragraph"> <p>TinkerPop offers several reference implementations of its interfaces that are not only meant for production usage, but also represent models by which different graph providers can build their systems. More specific documentation on how to build systems at this level of the API can be found in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/">Provider Documentation</a>. The following sections describe the various reference implementations and their usage.</p> </div> </div> </div> <div class="sect1"> <h2 id="tinkergraph-gremlin">TinkerGraph-Gremlin</h2> <div class="sectionbody"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>tinkergraph-gremlin<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/tinkerpop-character.png" alt="tinkerpop character" width="100"></span> TinkerGraph is a single machine, in-memory (with optional persistence), non-transactional graph engine that provides both OLTP and OLAP functionality. It is deployed with TinkerPop and serves as the reference implementation for other providers to study in order to understand the semantics of the various methods of the TinkerPop API. Its status as a reference implementation does not however imply that it is not suitable for production. TinkerGraph has many practical use cases in production applications and their development. Some examples of TinkerGraph use cases include:</p> </div> <div class="ulist"> <ul> <li> <p>Ad-hoc analysis of large immutable graphs that fit in memory.</p> </li> <li> <p>Extract subgraphs, from larger graphs that don’t fit in memory, into TinkerGraph for further analysis or other purposes.</p> </li> <li> <p>Use TinkerGraph as a sandbox to develop and debug complex traversals by simulating data from a larger graph inside a TinkerGraph.</p> </li> </ul> </div> <div class="paragraph"> <p>Constructing a simple graph using TinkerGraph in Java is presented below:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Graph graph = TinkerGraph.open(); GraphTraversalSource g = traversal().withEmbedded(graph); Vertex marko = 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>).next(); Vertex lop = g.addV(<span class="string"><span class="delimiter">"</span><span class="content">software</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">lop</span><span class="delimiter">"</span></span>).property(<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>).next(); g.addE(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).from(marko).to(lop).property(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>,<span class="float">0.6d</span>).iterate();</code></pre> </div> </div> <div class="paragraph"> <p>The above Gremlin creates two vertices named "marko" and "lop" and connects them via a created-edge with a weight=0.6 property. The addition of these two vertices and the edge between them could also be done in a single Gremlin statement as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">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>).as(<span class="string"><span class="delimiter">"</span><span class="content">m</span><span class="delimiter">"</span></span>). addV(<span class="string"><span class="delimiter">"</span><span class="content">software</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">lop</span><span class="delimiter">"</span></span>).property(<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>).as(<span class="string"><span class="delimiter">"</span><span class="content">l</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">m</span><span class="delimiter">"</span></span>).to(<span class="string"><span class="delimiter">"</span><span class="content">l</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.6d</span>).iterate();</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Pay attention to the fact that traversals end with <code>next()</code> or <code>iterate()</code>. These methods advance the objects in the traversal stream and without those methods, the traversal does nothing. Review the <a href="https://tinkerpop.apache.org/docs/3.7.3/tutorials/the-gremlin-console/#result-iteration">Result Iteration Section</a> of The Gremlin Console tutorial for more information. </td> </tr> </table> </div> <div class="paragraph"> <p>Next, the graph can be queried as such.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">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>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>The <code>g.V().has("name","marko")</code> part of the query can be executed in two ways.</p> </div> <div class="ulist"> <ul> <li> <p>A linear scan of all vertices filtering out those vertices that don’t have the name "marko"</p> </li> <li> <p>A <code>O(log(|V|))</code> index lookup for all vertices with the name "marko"</p> </li> </ul> </div> <div class="paragraph"> <p>Given the initial graph construction in the first code block, no index was defined and thus, a linear scan is executed. However, if the graph was constructed as such, then an index lookup would be used.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Graph g = TinkerGraph.open(); g.createIndex(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>,Vertex.class)</code></pre> </div> </div> <div class="paragraph"> <p>The execution times for a vertex lookup by property is provided below for both no-index and indexed version of TinkerGraph over the Grateful Dead graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797177-1" type="radio" name="radio-set-1729797177-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797177-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797177-2" type="radio" name="radio-set-1729797177-1" class="tab-selector-2" /> <label for="tab-1729797177-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> 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> clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="float">0.18357163599999998</span> gremlin> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> graph.createIndex(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,Vertex.class) ==><span class="predefined-constant">null</span> gremlin> 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> clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="float">0.020509884</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/grateful-dead.xml</span><span class="delimiter">'</span></span>).read().iterate() clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(1)</b> graph = TinkerGraph.open() g = traversal().withEmbedded(graph) graph.createIndex(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>,Vertex.class) g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/grateful-dead.xml</span><span class="delimiter">'</span></span>).read().iterate() clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="invisible">//</span><b class="conum">2</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Determine the average runtime of 1000 vertex lookups when no <code>name</code>-index is defined.</p> </li> <li> <p>Determine the average runtime of 1000 vertex lookups when a <code>name</code>-index is defined.</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Each graph system will have different mechanism by which indices and schemas are defined. TinkerPop does not require any conformance in this area. In TinkerGraph, the only definitions are around indices. With other graph systems, property value types, indices, edge labels, etc. may be required to be defined <em>a priori</em> to adding data to the graph. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> TinkerGraph is distributed with Gremlin Server and is therefore automatically available to it for configuration. </td> </tr> </table> </div> <div class="sect2"> <h3 id="_data_types">Data Types</h3> <div class="paragraph"> <p>TinkerGraph can store any Java <code>Object</code> for a property value. It is therefore important to take note of the types of the values that are being used and it is often best to be explicit in terms of exactly what type is being used, especially in the case of numbers.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797177-3" type="radio" name="radio-set-1729797177-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797177-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797177-4" type="radio" name="radio-set-1729797177-3" class="tab-selector-2" /> <label for="tab-1729797177-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294</span>) ==>v[<span class="integer">0</span>] gremlin> g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294f</span>) ==>v[<span class="integer">2</span>] gremlin> g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294d</span>) ==>v[<span class="integer">4</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294</span>) <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">0</span>] ==>v[<span class="integer">4</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294f</span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>v[<span class="integer">2</span>] gremlin> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294d</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==>v[<span class="integer">0</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294</span>) g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294f</span>) g.addV().property(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294d</span>) g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294</span>) <span class="comment">//</span>// <b class="conum">(1)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294f</span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.V().has(<span class="string"><span class="delimiter">'</span><span class="content">vp2</span><span class="delimiter">'</span></span>,<span class="float">0.65780294d</span>) <span class="invisible">//</span><b class="conum">3</b></code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>In Gremlin Console, <code>0.65780294</code> actually evaluates to a <code>BigDecimal</code>, which won’t match the specifically typed <code>float</code> property value.</p> </li> <li> <p>The explicit <code>float</code> will only match the <code>float</code> property value.</p> </li> <li> <p>The explicit <code>double</code> will only match the <code>double</code> and <code>BigDecimal</code> values.</p> </li> </ol> </div> <div class="paragraph"> <p>Unlike other graphs, the above demonstration shows that TinkerGraph does not do any form of type coercion (except for type coercion related to element identifiers as described in the <a href="#next section">tinkergraph-configuration</a>).</p> </div> </div> <div class="sect2"> <h3 id="tinkergraph-configuration">Configuration</h3> <div class="paragraph"> <p>TinkerGraph has several settings that can be provided on creation via <code>Configuration</code> object:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 83.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Property</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.graph</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph</code></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.vertexIdManager</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The <code>IdManager</code> implementation to use for vertices.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.edgeIdManager</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The <code>IdManager</code> implementation to use for edges.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.vertexPropertyIdManager</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The <code>IdManager</code> implementation to use for vertex properties.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.defaultVertexPropertyCardinality</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The default <code>VertexProperty.Cardinality</code> to use when <code>Vertex.property(k,v)</code> is called.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.allowNullPropertyValues</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A boolean value that determines whether or not <code>null</code> property values are allowed and defaults to <code>false</code>.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.graphLocation</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The path and file name for where TinkerGraph should persist the graph data. If a value is specified here, the <code>gremlin.tinkergraph.graphFormat</code> should also be specified. If this value is not included (default), then the graph will stay in-memory and not be loaded/persisted to disk.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.tinkergraph.graphFormat</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The format to use to serialize the graph which may be one of the following: <code>graphml</code>, <code>graphson</code>, <code>gryo</code>, or a fully qualified class name that implements Io.Builder interface (which allows for external third party graph reader/writer formats to be used for persistence). If a value is specified here, then the <code>gremlin.tinkergraph.graphLocation</code> should also be specified. If this value is not included (default), then the graph will stay in-memory and not be loaded/persisted to disk.</p></td> </tr> </tbody> </table> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> To use <a href="#tinkergraph-gremlin-tx">transactions</a>, configure <code>gremlin.graph</code> as <code>org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerTransactionGraph</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>The <code>IdManager</code> settings above refer to how TinkerGraph will control identifiers for vertices, edges and vertex properties. There are several options for each of these settings: <code>ANY</code>, <code>LONG</code>, <code>INTEGER</code>, <code>UUID</code>, or the fully qualified class name of an <code>IdManager</code> implementation on the classpath. When not specified, the default values for all settings is <code>ANY</code>, meaning that the graph will work with any object on the JVM as the identifier and will generate new identifiers from <code>Long</code> when the identifier is not user supplied. TinkerGraph will also expect the user to understand the types used for identifiers when querying, meaning that <code>g.V(1)</code> and <code>g.V(1L)</code> could return two different vertices. <code>LONG</code>, <code>INTEGER</code> and <code>UUID</code> settings will try to coerce identifier values to the expected type as well as generate new identifiers with that specified type.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Setting the <code>IdManager</code> to <code>ANY</code> also allows <code>String</code> type ID values to be used. </td> </tr> </table> </div> <div class="paragraph"> <p>If the TinkerGraph is configured for persistence with <code>gremlin.tinkergraph.graphLocation</code> and <code>gremlin.tinkergraph.graphFormat</code>, then the graph will be written to the specified location with the specified format when <code>Graph.close()</code> is called. In addition, if these settings are present, TinkerGraph will attempt to load the graph from the specified location.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> If choosing <code>graphson</code> as the <code>gremlin.tinkergraph.graphFormat</code>, be sure to also establish the various <code>IdManager</code> settings as well to ensure that identifiers are properly coerced to the appropriate types as GraphSON can lose the identifier’s type during serialization (i.e. it will assume <code>Integer</code> when the default for TinkerGraph is <code>Long</code>, which could lead to load errors that result in a message like, "Vertex with id already exists"). </td> </tr> </table> </div> <div class="paragraph"> <p>It is important to consider the data being imported to TinkerGraph with respect to <code>defaultVertexPropertyCardinality</code> setting. For example, if a <code>.gryo</code> file is known to contain multi-property data, be sure to set the default cardinality to <code>list</code> or else the data will import as <code>single</code>. Consider the following:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797177-5" type="radio" name="radio-set-1729797177-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797177-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729797177-6" type="radio" name="radio-set-1729797177-5" class="tab-selector-2" /> <label for="tab-1729797177-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> graph = TinkerGraph.open() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.io(<span class="string"><span class="delimiter">"</span><span class="content">data/tinkerpop-crew.kryo</span><span class="delimiter">"</span></span>).read().iterate() [WARN] o.a.t.g.s.u.Attachable<span class="error">$</span><span class="predefined-type">Method</span> - location has SINGLE cardinality but with more than one <span class="key">value</span>: [vp[location->san diego], vp[location->santa cruz], vp[location->brussels], vp[location->santa fe]]. Only last value will be retained. [WARN] o.a.t.g.s.u.Attachable<span class="error">$</span><span class="predefined-type">Method</span> - location has SINGLE cardinality but with more than one <span class="key">value</span>: [vp[location->centreville], vp[location->dulles], vp[location->purcellville]]. Only last value will be retained. [WARN] o.a.t.g.s.u.Attachable<span class="error">$</span><span class="predefined-type">Method</span> - location has SINGLE cardinality but with more than one <span class="key">value</span>: [vp[location->bremen], vp[location->baltimore], vp[location->oakland], vp[location->seattle]]. Only last value will be retained. [WARN] o.a.t.g.s.u.Attachable<span class="error">$</span><span class="predefined-type">Method</span> - location has SINGLE cardinality but with more than one <span class="key">value</span>: [vp[location->spremberg], vp[location->kaiserslautern], vp[location->aachen]]. Only last value will be retained. gremlin> g.V().properties() ==>vp[name->marko] ==>vp[location->santa fe] ==>vp[name->stephen] ==>vp[location->purcellville] ==>vp[name->matthias] ==>vp[location->seattle] ==>vp[name->daniel] ==>vp[location->aachen] ==>vp[name->gremlin] ==>vp[name->tinkergraph] gremlin> conf = <span class="keyword">new</span> BaseConfiguration() ==>org.apache.commons.configuration2.BaseConfiguration<span class="error">@</span><span class="float">73483d</span><span class="integer">4</span>b gremlin> conf.setProperty(<span class="string"><span class="delimiter">"</span><span class="content">gremlin.tinkergraph.defaultVertexPropertyCardinality</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">list</span><span class="delimiter">"</span></span>) ==><span class="predefined-constant">null</span> gremlin> graph = TinkerGraph.open(conf) ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.io(<span class="string"><span class="delimiter">"</span><span class="content">data/tinkerpop-crew.kryo</span><span class="delimiter">"</span></span>).read().iterate() gremlin> g.V().properties() ==>vp[name->marko] ==>vp[location->san diego] ==>vp[location->santa cruz] ==>vp[location->brussels] ==>vp[location->santa fe] ==>vp[name->stephen] ==>vp[location->centreville] ==>vp[location->dulles] ==>vp[location->purcellville] ==>vp[name->matthias] ==>vp[location->bremen] ==>vp[location->baltimore] ==>vp[location->oakland] ==>vp[location->seattle] ==>vp[name->daniel] ==>vp[location->spremberg] ==>vp[location->kaiserslautern] ==>vp[location->aachen] ==>vp[name->gremlin] ==>vp[name->tinkergraph]</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">graph = TinkerGraph.open() g = traversal().withEmbedded(graph) g.io(<span class="string"><span class="delimiter">"</span><span class="content">data/tinkerpop-crew.kryo</span><span class="delimiter">"</span></span>).read().iterate() g.V().properties() conf = <span class="keyword">new</span> BaseConfiguration() conf.setProperty(<span class="string"><span class="delimiter">"</span><span class="content">gremlin.tinkergraph.defaultVertexPropertyCardinality</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">list</span><span class="delimiter">"</span></span>) graph = TinkerGraph.open(conf) g = traversal().withEmbedded(graph) g.io(<span class="string"><span class="delimiter">"</span><span class="content">data/tinkerpop-crew.kryo</span><span class="delimiter">"</span></span>).read().iterate() g.V().properties()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="tinkergraph-gremlin-tx">Transactions</h3> <div class="paragraph"> <p><code>TinkerGraph</code> includes optional transaction support and thread-safety through the <code>TinkerTransactionGraph</code> class. The default configuration of TinkerGraph remains non-transactional.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> This feature was first made available in TinkerPop 3.7.0. </td> </tr> </table> </div> <div class="sect3"> <h4 id="_transaction_semantics">Transaction Semantics</h4> <div class="paragraph"> <p><code>TinkerTransactionGraph</code> only has support for <code>ThreadLocal</code> transactions, so embedded graph transactions may not be fully supported. You can think of the transaction as belonging to a thread, any traversals executed within the same thread will share the same transaction even if you attempt to start a new transaction.</p> </div> <div class="paragraph"> <p><code>TinkerTransactionGraph</code> provides the <code>read committed</code> transaction isolation level. This means that it will always try to guard against dirty reads. While you may notice stricter isolation semantics in some cases, you should not depend on this behavior as it may change in the future.</p> </div> <div class="paragraph"> <p><code>TinkerTransactionGraph</code> employs optimistic locking as its locking strategy. This reduces complexity in the design as there are fewer timeouts that the user needs to manage. However, a consequence of this approach is that a transaction will throw a <code>TransactionException</code> if two different transactions attempt to lock the same element (see "Best Practices" below).</p> </div> </div> <div class="sect3"> <h4 id="testing-remote-providers">Testing Remote Providers</h4> <div class="paragraph"> <p>These transaction semantics described above may not fit use cases for some production scenarios that require strict ACID-like transactions. Therefore, it is recommended that <code>TinkerTransactionGraph</code> be used as a <code>Graph</code> for test environments where you still require access to a <code>Graph</code> that supports transactions. <code>TinkerTransactionGraph</code> does fully support TinkerPop’s <code>Transaction</code> interface which still makes it a useful <code>Graph</code> for exploring the <a href="#transactions">Transaction API</a>.</p> </div> <div class="paragraph"> <p>A common scenario where this sort of testing is helpful is with <a href="#connecting-rgp">Remote Graph Providers</a>, where developing unit tests might be hard against a graph service. Instead, configure <code>TinkerTransactionGraph</code>, either in an embedded style if using Java or with Gremlin Server for other cases.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// consider this class that returns the results of some Gremlin. by constructing the</span> <span class="comment">// GraphService in a way that takes a GraphTraversalSource it becomes possible to</span> <span class="comment">// execute getPersons() under any graph system.</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">GraphService</span> { <span class="directive">private</span> <span class="directive">final</span> GraphTraversalSource g; <span class="directive">public</span> GraphService(GraphTraversalSource g) { <span class="local-variable">this</span>.g = g; } <span class="directive">public</span> <span class="predefined-type">List</span><Vertex> getPersons() { <span class="keyword">return</span> g.V().hasLabel(<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>).toList(); } } <span class="comment">// when writing tests for the GraphService it becomes possible to configure the test</span> <span class="comment">// to run in a variety of scenarios. here we decide that TinkerTransactionGraph is a</span> <span class="comment">// suitable test graph replacement for our actual production graph.</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">GraphServiceTest</span> { <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> TinkerTransactionGraph graph = TinkerTransactionGraph().open(); <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> GraphTraversalSource g = traversal.withEmbedded(graph); <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> GraphService service = <span class="keyword">new</span> GraphService(g); <span class="annotation">@Test</span> <span class="directive">public</span> <span class="type">void</span> shouldGetPersons() { <span class="directive">final</span> <span class="predefined-type">List</span><Vertex> persons = service.getPersons(); assertEquals(<span class="integer">6</span>, persons.size()); } } <span class="comment">// or perhaps, since we're using a remote graph provider, we feel it would be better to</span> <span class="comment">// start Gremlin Server with a TinkerTransactionGraph configured using a docker container,</span> <span class="comment">// embedding it directly in our tests or running it as a separate process like:</span> <span class="comment">//</span> <span class="comment">// bin/gremlin-server.sh conf/gremlin-server-transaction.yaml</span> <span class="comment">//</span> <span class="comment">// and then connect to it with a driver in more of an integration test style. obviously,</span> <span class="comment">// with this approach you could also configure your production graph directly or use custom</span> <span class="comment">// build options to trigger different test configurations for a more dynamic approach</span> <span class="directive">public</span> <span class="type">class</span> <span class="class">GraphServiceTest</span> { <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> GraphTraversalSource g = traversal.withRemote( <span class="keyword">new</span> DriverRemoteConnection(<span class="string"><span class="delimiter">'</span><span class="content">ws://localhost:8182/gremlin</span><span class="delimiter">'</span></span>)); <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> GraphService service = <span class="keyword">new</span> GraphService(g); <span class="annotation">@Test</span> <span class="directive">public</span> <span class="type">void</span> shouldGetPersons() { <span class="directive">final</span> <span class="predefined-type">List</span><Vertex> persons = service.getPersons(); assertEquals(<span class="integer">6</span>, persons.size()); } }</code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> There can be subtle behavioral differences between TinkerGraph and the graph ultimately intended for use. Be aware of the differences when writing tests to ensure that you are testing behaviors of your applications appropriately. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_best_practices_2">Best Practices</h4> <div class="paragraph"> <p>Errors can occur before a transaction gets committed. Specifically for <code>TinkerTransactionGraph</code>, you may encounter many <code>TransactionException</code> errors in a highly concurrent environment due its optimistic approach to locking. Users should follow the try-catch-rollback pattern described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#transactions">transactions</a> section in combination with exponential backoff based retries to mitigate this issue.</p> </div> </div> <div class="sect3"> <h4 id="_performance_considerations">Performance Considerations</h4> <div class="paragraph"> <p>While transactions impose minimal impact for mutating workloads, users should expect performance degradation for read-only work relative to the non-transactional configuration. However, its approach to locking (write-only, optimistic) and its in-memory nature, TinkerTransactionGraph is likely faster than other <code>Graph</code> implementations that support transactions.</p> </div> </div> <div class="sect3"> <h4 id="_examples">Examples</h4> <div class="paragraph"> <p>Constructing a simple graph using <code>TinkerTransactionGraph</code> in Java is presented below:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java">Graph graph = TinkerTransactionGraph.open(); g = traversal().withEmbedded(graph) GraphTraversalSource gtx = g.tx().begin(); <span class="keyword">try</span> { Vertex marko = gtx.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>).next(); Vertex lop = gtx.addV(<span class="string"><span class="delimiter">"</span><span class="content">software</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">lop</span><span class="delimiter">"</span></span>).property(<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>).next(); gtx.addE(<span class="string"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>).from(marko).to(lop).property(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>,<span class="float">0.6d</span>).iterate(); gtx.tx().commit(); } <span class="keyword">catch</span> (<span class="exception">Exception</span> ex) { gtx.tx().rollback(); }</code></pre> </div> </div> <div class="paragraph"> <p>The above Gremlin creates two vertices named "marko" and "lop" and connects them via a created-edge with a weight=0.6 property. In case of any errors <code>rollback()</code> will be called and no changes will be performed.</p> </div> <div class="paragraph"> <p>To use the embedded TinkerTransactionGraph in Gremlin Console:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797177-7" type="radio" name="radio-set-1729797177-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729797177-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729797177-8" type="radio" name="radio-set-1729797177-7" class="tab-selector-2" /> <label for="tab-1729797177-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> graph = TinkerTransactionGraph.open() <span class="comment">//</span>// <b class="conum">(1)</b> ==>tinkertransactiongraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>] gremlin> g = traversal().withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(2)</b> ==>graphtraversalsource[tinkertransactiongraph[<span class="key">vertices</span>:<span class="integer">0</span> <span class="key">edges</span>:<span class="integer">0</span>], standard] gremlin> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">test</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">one</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">0</span>] gremlin> g.tx().commit() <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="predefined-constant">null</span> gremlin> g.V().valueMap() ==>[<span class="key">name</span>:[one]] gremlin> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">test</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">two</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==>v[<span class="integer">2</span>] gremlin> g.V().valueMap() ==>[<span class="key">name</span>:[one]] ==>[<span class="key">name</span>:[two]] gremlin> g.tx().rollback() <span class="comment">//</span>// <b class="conum">(5)</b> ==><span class="predefined-constant">null</span> gremlin> g.V().valueMap() ==>[<span class="key">name</span>:[one]]</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">graph = TinkerTransactionGraph.open() <span class="comment">//</span>// <b class="conum">(1)</b> g = traversal().withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(2)</b> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">test</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">one</span><span class="delimiter">'</span></span>) g.tx().commit() <span class="comment">//</span>// <b class="conum">(3)</b> g.V().valueMap() g.addV(<span class="string"><span class="delimiter">'</span><span class="content">test</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">two</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> g.V().valueMap() g.tx().rollback() <span class="comment">//</span>// <b class="conum">(5)</b> g.V().valueMap()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Open transactional graph.</p> </li> <li> <p>Spawn a GraphTraversalSource with transactional graph.</p> </li> <li> <p>Commit the add vertex operation</p> </li> <li> <p>Add a second vertex without committing</p> </li> <li> <p>Rollback the change</p> </li> </ol> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="neo4j-gremlin">Neo4j-Gremlin (Deprecated)</h2> <div class="sectionbody"> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Deprecated: Neo4j-Gremlin is not compatible with versions of Neo4j beyond 3.4 (Reached End of Life March 31, 2020). For this reason, use of Neo4j-Gremlin is not recommended for production environments. Neo4j-Gremlin is expected to remain compatible with upcoming releases of TinkerPop, however long term support is not guaranteed. Neo4j-Gremlin may be dropped from future versions of TinkerPop if compatibility cannot reasonably be maintained. Alternative TinkerPop enabled graph providers can be found on the <a href="https://tinkerpop.apache.org/providers.html">TinkerPop site</a>. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Neo4j-Gremlin can work with JDK17, but requires the use of the <code>--add-opens</code> flag to be provided to the JVM as follows: <code>--add-opens=java.base/sun.nio.ch=ALL-UNNAMED</code>. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>neo4j-gremlin<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span> <span class="comment"><!-- neo4j-tinkerpop-api-impl is NOT Apache 2 licensed - more information below --></span> <span class="comment"><!-- supports Neo4j 3.4.11 --></span> <span class="tag"><dependency></span> <span class="tag"><groupId></span>org.neo4j<span class="tag"></groupId></span> <span class="tag"><artifactId></span>neo4j-tinkerpop-api-impl<span class="tag"></artifactId></span> <span class="tag"><version></span>0.9-3.4.0<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="paragraph"> <p><a href="http://neo4j.com">Neo4j, Inc.</a> are the developers of the OLTP-based <a href="http://neo4j.com">Neo4j graph database</a>.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Unless under a commercial agreement with Neo4j, Inc., Neo4j is licensed <a href="http://en.wikipedia.org/wiki/Affero_General_Public_License">AGPL</a>. The <code>neo4j-gremlin</code> module is licensed Apache2 because it only references the Apache2-licensed Neo4j API (not its implementation). Note that neither the <a href="#gremlin-console">Gremlin Console</a> nor <a href="#gremlin-server">Gremlin Server</a> distribute with the Neo4j implementation binaries. To access the binaries, use the <code>:install</code> command to download binaries from <a href="http://search.maven.org/">Maven Central Repository</a>. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> When connecting to existing Neo4j databases, ensure that this database is compatible with the version of Neo4j that TinkerPop currently supports in the <code>neo4j-tinkerpop-api-impl</code>. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> For configuring Grape, the dependency resolver of Groovy, please refer to the <a href="#gremlin-applications">Gremlin Applications</a> section. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">gremlin> :install org.apache.tinkerpop neo4j-gremlin 3.7.3 ==><span class="key">Loaded</span>: [org.apache.tinkerpop, neo4j-gremlin, 3.7.3] - restart the console to use [tinkerpop.neo4j] gremlin> :q ... gremlin> :plugin use tinkerpop.neo4j ==>tinkerpop.neo4j activated gremlin> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[EmbeddedGraphDatabase [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]]</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> To host Neo4j in <a href="#gremlin-server">Gremlin Server</a>, the dependencies must first be "installed" or otherwise copied to the Gremlin Server path. The automated method for doing this would be to execute <code>bin/gremlin-server.sh install org.apache.tinkerpop neo4j-gremlin 3.7.3</code>. Once installed, the Gremlin Server configuration file must be edited to include the <code>Neo4jGremlinPlugin</code> as shown in <code>conf/gremlin-server-neo4j.yaml</code>. </td> </tr> </table> </div> <div class="sect2"> <h3 id="_indices">Indices</h3> <div class="paragraph"> <p>Neo4j 2.x indices leverage vertex labels to partition the index space. TinkerPop does not provide method interfaces for defining schemas/indices for the underlying graph system. Thus, in order to create indices, it is important to call the Neo4j API directly.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> <code>Neo4jGraphStep</code> will attempt to discern which indices to use when executing a traversal of the form <code>g.V().has()</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>The Gremlin-Console session below demonstrates Neo4j indices. For more information, please refer to the Neo4j documentation:</p> </div> <div class="ulist"> <ul> <li> <p>Manipulating indices with <a href="http://neo4j.com/docs/developer-manual/current/#query-schema-index">Cypher</a>.</p> </li> <li> <p>Manipulating indices with the Neo4j <a href="http://neo4j.com/docs/stable/tutorials-java-embedded-new-index.html">Java API</a>.</p> </li> </ul> </div> <section class="tabs tabs-2"> <input id="tab-1729797189-1" type="radio" name="radio-set-1729797189-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797189-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797189-2" type="radio" name="radio-set-1729797189-1" class="tab-selector-2" /> <label for="tab-1729797189-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> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">CREATE INDEX ON :person(name)</span><span class="delimiter">"</span></span>) gremlin> graph.tx().commit() <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="predefined-constant">null</span> gremlin> 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>) ==>v[<span class="integer">0</span>] gremlin> g.addV(<span class="string"><span class="delimiter">'</span><span class="content">dog</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">puppy</span><span class="delimiter">'</span></span>) ==>v[<span class="integer">1</span>] gremlin> 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>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>marko gremlin> graph.close() ==><span class="predefined-constant">null</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">graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph) graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">CREATE INDEX ON :person(name)</span><span class="delimiter">"</span></span>) graph.tx().commit() <span class="comment">//</span>// <b class="conum">(1)</b> 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>) g.addV(<span class="string"><span class="delimiter">'</span><span class="content">dog</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">puppy</span><span class="delimiter">'</span></span>) 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>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) graph.close()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Schema mutations must happen in a different transaction than graph mutations</p> </li> </ol> </div> <div class="paragraph"> <p>Below demonstrates the runtime benefits of indices and demonstrates how if there is no defined index (only vertex labels), a linear scan of the vertex-label partition is still faster than a linear scan of all vertices.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797189-3" type="radio" name="radio-set-1729797189-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797189-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797189-4" type="radio" name="radio-set-1729797189-3" class="tab-selector-2" /> <label for="tab-1729797189-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> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> 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> g.tx().commit() ==><span class="predefined-constant">null</span> gremlin> clock(<span class="integer">1000</span>) {g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">artist</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(1)</b> ==><span class="float">0.35031228</span> gremlin> graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">CREATE INDEX ON :artist(name)</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> gremlin> g.tx().commit() ==><span class="predefined-constant">null</span> gremlin> <span class="predefined-type">Thread</span>.sleep(<span class="integer">5000</span>) <span class="comment">//</span>// <b class="conum">(3)</b> ==><span class="predefined-constant">null</span> gremlin> clock(<span class="integer">1000</span>) {g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">artist</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="float">0.061846079</span> gremlin> clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(5)</b> ==><span class="float">0.6680353569999999</span> gremlin> graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">DROP INDEX ON :artist(name)</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(6)</b> gremlin> g.tx().commit() ==><span class="predefined-constant">null</span> gremlin> graph.close() ==><span class="predefined-constant">null</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">graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph) 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.tx().commit() clock(<span class="integer">1000</span>) {g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">artist</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(1)</b> graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">CREATE INDEX ON :artist(name)</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> g.tx().commit() <span class="predefined-type">Thread</span>.sleep(<span class="integer">5000</span>) <span class="comment">//</span>// <b class="conum">(3)</b> clock(<span class="integer">1000</span>) {g.V().hasLabel(<span class="string"><span class="delimiter">'</span><span class="content">artist</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(4)</b> clock(<span class="integer">1000</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">Garcia</span><span class="delimiter">'</span></span>).iterate()} <span class="comment">//</span>// <b class="conum">(5)</b> graph.cypher(<span class="string"><span class="delimiter">"</span><span class="content">DROP INDEX ON :artist(name)</span><span class="delimiter">"</span></span>) <span class="comment">//</span>// <b class="conum">(6)</b> g.tx().commit() graph.close()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Find all artists whose name is Garcia which does a linear scan of the artist vertex-label partition.</p> </li> <li> <p>Create an index for all artist vertices on their name property.</p> </li> <li> <p>Neo4j indices are eventually consistent so this stalls to give the index time to populate itself.</p> </li> <li> <p>Find all artists whose name is Garcia which uses the pre-defined schema index.</p> </li> <li> <p>Find all vertices whose name is Garcia which requires a linear scan of all the data in the graph.</p> </li> <li> <p>Drop the created index.</p> </li> </ol> </div> </div> <div class="sect2"> <h3 id="_cypher">Cypher</h3> <div class="imageblock"> <div class="content"> <img src="../images/gremlin-loves-cypher.png" alt="gremlin loves cypher" width="400"> </div> </div> <div class="paragraph"> <p>NeoTechnology are the creators of the graph pattern-match query language <a href="https://neo4j.com/developer/cypher-query-language/">Cypher</a>. It is possible to leverage Cypher from within Gremlin by using the <code>Neo4jGraph.cypher()</code> graph traversal method.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797189-5" type="radio" name="radio-set-1729797189-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797189-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729797189-6" type="radio" name="radio-set-1729797189-5" class="tab-selector-2" /> <label for="tab-1729797189-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> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>).read().iterate() gremlin> graph.cypher(<span class="string"><span class="delimiter">'</span><span class="content">MATCH (a {name:"marko"}) RETURN a</span><span class="delimiter">'</span></span>) ==>[<span class="key">a</span>:v[<span class="integer">0</span>]] gremlin> graph.cypher(<span class="string"><span class="delimiter">'</span><span class="content">MATCH (a {name:"marko"}) RETURN 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>josh ==>vadas gremlin> graph.close() ==><span class="predefined-constant">null</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">graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph) g.io(<span class="string"><span class="delimiter">'</span><span class="content">data/tinkerpop-modern.kryo</span><span class="delimiter">'</span></span>).read().iterate() graph.cypher(<span class="string"><span class="delimiter">'</span><span class="content">MATCH (a {name:"marko"}) RETURN a</span><span class="delimiter">'</span></span>) graph.cypher(<span class="string"><span class="delimiter">'</span><span class="content">MATCH (a {name:"marko"}) RETURN 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>).out(<span class="string"><span class="delimiter">'</span><span class="content">knows</span><span class="delimiter">'</span></span>).values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) graph.close()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Thus, like <a href="#match-step"><code>match()</code></a>-step in Gremlin, it is possible to do a declarative pattern match and then move back into imperative Gremlin.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> For those developers using <a href="#gremlin-server">Gremlin Server</a> against Neo4j, it is possible to do Cypher queries by simply placing the Cypher string in <code>graph.cypher(…​)</code> before submission to the server. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_multi_label">Multi-Label</h3> <div class="paragraph"> <p>TinkerPop requires every <code>Element</code> to have a single, immutable string label (i.e. a <code>Vertex</code>, <code>Edge</code>, and <code>VertexProperty</code>). In Neo4j, a <code>Node</code> (vertex) can have an <a href="http://neo4j.com/docs/developer-manual/current/#graphdb-neo4j-labels">arbitrary number of labels</a> while a <code>Relationship</code> (edge) can have one and only one. Furthermore, in Neo4j, <code>Node</code> labels are mutable while <code>Relationship</code> labels are not. In order to handle this mismatch, three <code>Neo4jVertex</code> specific methods exist in Neo4j-Gremlin.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="predefined-type">Set</span><<span class="predefined-type">String</span>> labels() <span class="comment">// get all the labels of the vertex</span> <span class="directive">public</span> <span class="type">void</span> addLabel(<span class="predefined-type">String</span> label) <span class="comment">// add a label to the vertex</span> <span class="directive">public</span> <span class="type">void</span> removeLabel(<span class="predefined-type">String</span> label) <span class="comment">// remove a label from the vertex</span></code></pre> </div> </div> <div class="paragraph"> <p>An example use case is presented below.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797189-7" type="radio" name="radio-set-1729797189-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729797189-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729797189-8" type="radio" name="radio-set-1729797189-7" class="tab-selector-2" /> <label for="tab-1729797189-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> graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) ==>neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [<span class="regexp"><span class="delimiter">/</span><span class="content">tmp</span><span class="delimiter">/</span></span>neo4j]], standard] gremlin> vertex = (Neo4jVertex) g.addV(<span class="string"><span class="delimiter">'</span><span class="content">human::animal</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> ==>v[<span class="integer">0</span>] gremlin> vertex.label() <span class="comment">//</span>// <b class="conum">(2)</b> ==><span class="key">animal</span>::human gremlin> vertex.labels() <span class="comment">//</span>// <b class="conum">(3)</b> ==>animal ==>human gremlin> vertex.addLabel(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> ==><span class="predefined-constant">null</span> gremlin> vertex.label() ==><span class="key">animal</span>::<span class="key">human</span>::organism gremlin> vertex.removeLabel(<span class="string"><span class="delimiter">'</span><span class="content">human</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> ==><span class="predefined-constant">null</span> gremlin> vertex.labels() ==>animal ==>organism gremlin> vertex.addLabel(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(6)</b> ==><span class="predefined-constant">null</span> gremlin> vertex.labels() ==>animal ==>organism gremlin> vertex.removeLabel(<span class="string"><span class="delimiter">'</span><span class="content">human</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> ==><span class="predefined-constant">null</span> gremlin> vertex.label() ==><span class="key">animal</span>::organism gremlin> g.V().has(label,<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(8)</b> gremlin> g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(9)</b> ==>v[<span class="integer">0</span>] gremlin> g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>)).has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">animal</span><span class="delimiter">'</span></span>)) ==>v[<span class="integer">0</span>] gremlin> g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>).and(of(<span class="string"><span class="delimiter">'</span><span class="content">animal</span><span class="delimiter">'</span></span>))) ==>v[<span class="integer">0</span>] gremlin> graph.close() ==><span class="predefined-constant">null</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">graph = Neo4jGraph.open(<span class="string"><span class="delimiter">'</span><span class="content">/tmp/neo4j</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph) vertex = (Neo4jVertex) g.addV(<span class="string"><span class="delimiter">'</span><span class="content">human::animal</span><span class="delimiter">'</span></span>).next() <span class="comment">//</span>// <b class="conum">(1)</b> vertex.label() <span class="comment">//</span>// <b class="conum">(2)</b> vertex.labels() <span class="comment">//</span>// <b class="conum">(3)</b> vertex.addLabel(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(4)</b> vertex.label() vertex.removeLabel(<span class="string"><span class="delimiter">'</span><span class="content">human</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(5)</b> vertex.labels() vertex.addLabel(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(6)</b> vertex.labels() vertex.removeLabel(<span class="string"><span class="delimiter">'</span><span class="content">human</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(7)</b> vertex.label() g.V().has(label,<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>) <span class="comment">//</span>// <b class="conum">(8)</b> g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>)) <span class="comment">//</span>// <b class="conum">(9)</b> g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>)).has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">animal</span><span class="delimiter">'</span></span>)) g.V().has(label,of(<span class="string"><span class="delimiter">'</span><span class="content">organism</span><span class="delimiter">'</span></span>).and(of(<span class="string"><span class="delimiter">'</span><span class="content">animal</span><span class="delimiter">'</span></span>))) graph.close()</code></pre> </div> </div> </div> </div> </section> <div class="colist arabic"> <ol> <li> <p>Typecasting to a <code>Neo4jVertex</code> is only required in Java.</p> </li> <li> <p>The standard <code>Vertex.label()</code> method returns all the labels in alphabetical order concatenated using <code>::</code>.</p> </li> <li> <p><code>Neo4jVertex.labels()</code> method returns the individual labels as a set.</p> </li> <li> <p><code>Neo4jVertex.addLabel()</code> method adds a single label.</p> </li> <li> <p><code>Neo4jVertex.removeLabel()</code> method removes a single label.</p> </li> <li> <p>Labels are unique and thus duplicate labels don’t exist.</p> </li> <li> <p>If a label that does not exist is removed, nothing happens.</p> </li> <li> <p><code>P.eq()</code> does a full string match and should only be used if multi-labels are not leveraged.</p> </li> <li> <p><code>LabelP.of()</code> is specific to <code>Neo4jGraph</code> and used for multi-label matching.</p> </li> </ol> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> <code>LabelP.of()</code> is only required if multi-labels are leveraged. <code>LabelP.of()</code> is used when filtering/looking-up vertices by their label(s) as the standard <code>P.eq()</code> does a direct match on the <code>::</code>-representation of <code>vertex.label()</code> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_configuration">Configuration</h3> <div class="paragraph"> <p>The previous examples showed how to create a <code>Neo4jGraph</code> with the default configuration, but Neo4j has many other options to initialize it that are native to Neo4j. In order to expose those, <code>Neo4jGraph</code> has an <code>open(Configuration)</code> method which takes a standard Apache Configuration object. The same can be said of the standard method for creating <code>Graph</code> instances with <code>GraphFactory</code>. Each configuration key that Neo4j has must simply be prefixed with <code>gremlin.neo4j.conf.</code> and the suffix configuration key will be passed through to Neo4j.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Gremlin Server uses <code>GraphFactory</code> to instantiate the <code>Graph</code> instances it manages, so the example below is also relevant for that purpose as well. </td> </tr> </table> </div> <div class="paragraph"> <p>For example, a standard configuration file called <code>neo4j.properties</code> that sets the Neo4j <code>dbms.index_sampling.background_enabled</code> setting might look like:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="properties">gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=/tmp/neo4j gremlin.neo4j.conf.dbms.index_sampling.background_enabled=true</code></pre> </div> </div> <div class="paragraph"> <p>which can then be used as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> graph = GraphFactory.open('neo4j.properties') ==>neo4jgraph[community single [/tmp/neo4j]] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[neo4jgraph[community single [/tmp/neo4j]], standard]</code></pre> </div> </div> <div class="paragraph"> <p>Having this ability to set standard Neo4j configurations makes it possible to better control the initialization of Neo4j itself and provides the ability to enable certain features that would not otherwise be accessible.</p> </div> </div> <div class="sect2"> <h3 id="_bolt_configuration">Bolt Configuration</h3> <div class="paragraph"> <p>While <code>Neo4jGraph</code> enables Gremlin based queries, users may find it helpful to also be able to connect to that graph with native Neo4j drivers and other tools from that space. It is possible to enable the <a href="https://boltprotocol.org/">Bolt Protocol</a> as a way to do this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="properties">gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=/tmp/neo4j gremlin.neo4j.conf.dbms.connector.0.type=BOLT gremlin.neo4j.conf.dbms.connector.0.enabled=true gremlin.neo4j.conf.dbms.connector.0.address=localhost:7687</code></pre> </div> </div> <div class="paragraph"> <p>This configuration is especially relevant to Gremlin Server where one might want to connect to the same graph instance with both Gremlin and Cypher.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> :install org.neo4j.driver neo4j-java-driver 1.7.2 ==>Loaded: [org.neo4j.driver, neo4j-java-driver, 1.7.2] ... // restart Gremlin Console gremlin> import org.neo4j.driver.v1.* ==>org.apache.tinkerpop.gremlin.structure.*, org.apache.tinkerpop.gremlin.structure.util.*, ... org.neo4j.driver.v1.* gremlin> driver = GraphDatabase.driver( "bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j")) Oct 28, 2019 3:28:20 PM org.neo4j.driver.internal.logging.JULogger info INFO: Direct driver instance 1385140107 created for server address localhost:7687 ==>org.neo4j.driver.internal.InternalDriver@528f8f8b gremlin> session = driver.session() ==>org.neo4j.driver.internal.NetworkSession@f3fcd59 gremlin> session.run( "CREATE (a:person {name: {name}, age: {age}})", ......1> Values.parameters("name", "stephen", "age", 29)) gremlin> :remote connect tinkerpop.server conf/remote.yaml ==>Configured localhost/127.0.0.1:8182 gremlin> :remote console ==>All scripts will now be sent to Gremlin Server - [localhost/127.0.0.1:8182] - type ':remote console' to return to local mode gremlin> g.V().elementMap() ==>{id=0, label=person, name=stephen, age=29}</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_high_availability_configuration">High Availability Configuration</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/neo4j-ha.png" alt="neo4j ha" width="400"></span> TinkerPop supports running Neo4j with its fault tolerant master-slave replication configuration, referred to as its <a href="http://neo4j.com/docs/operations-manual/current/#_neo4j_cluster_install">High Availability (HA) cluster</a>. From the TinkerPop perspective, configuring for HA is not that different than configuring for embedded mode as shown above. The main difference is the usage of HA configuration options that enable the cluster. Once connected to a cluster, usage from the TinkerPop perspective is largely the same.</p> </div> <div class="paragraph"> <p>In configuring for HA the most important thing to realize is that all Neo4j HA settings are simply passed through the TinkerPop configuration settings given to the <code>GraphFactory.open()</code> or <code>Neo4j.open()</code> methods. For example, to provide the all-important <code>ha.server_id</code> configuration option through TinkerPop, simply prefix that key with the TinkerPop Neo4j key of <code>gremlin.neo4j.conf</code>.</p> </div> <div class="paragraph"> <p>The following properties demonstrates one of the three configuration files required to setup a simple three node HA cluster on the same machine instance:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="properties">gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=/tmp/neo4j.server1 gremlin.neo4j.conf.ha.server_id=1 gremlin.neo4j.conf.ha.initial_hosts=localhost:5001\,localhost:5002\,localhost:5003 gremlin.neo4j.conf.ha.host.coordination=localhost:5001 gremlin.neo4j.conf.ha.host.data=localhost:6001</code></pre> </div> </div> <div class="paragraph"> <p>Assuming the intent is to configure this cluster completely within TinkerPop (perhaps within three separate Gremlin Server instances), the other two configuration files will be quite similar. The second will be:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="properties">gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=/tmp/neo4j.server2 gremlin.neo4j.conf.ha.server_id=2 gremlin.neo4j.conf.ha.initial_hosts=localhost:5001\,localhost:5002\,localhost:5003 gremlin.neo4j.conf.ha.host.coordination=localhost:5002 gremlin.neo4j.conf.ha.host.data=localhost:6002</code></pre> </div> </div> <div class="paragraph"> <p>and the third will be:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="properties">gremlin.graph=org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph gremlin.neo4j.directory=/tmp/neo4j.server3 gremlin.neo4j.conf.ha.server_id=3 gremlin.neo4j.conf.ha.initial_hosts=localhost:5001\,localhost:5002\,localhost:5003 gremlin.neo4j.conf.ha.host.coordination=localhost:5003 gremlin.neo4j.conf.ha.host.data=localhost:6003</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The backslashes in the values provided to <code>gremlin.neo4j.conf.ha.initial_hosts</code> prevent that configuration setting as being interpreted as a <code>List</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>Create three separate Gremlin Server configuration files and point each at one of these Neo4j files. Since these Gremlin Server instances will be running on the same machine, ensure that each Gremlin Server instance has a unique <code>port</code> setting in that Gremlin Server configuration file. Start each Gremlin Server instance to bring the HA cluster online.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> <code>Neo4jGraph</code> instances will block until all nodes join the cluster. </td> </tr> </table> </div> <div class="paragraph"> <p>Neither Gremlin Server nor Neo4j will share transactions across the cluster. Be sure to either use Gremlin Server managed transactions or, if using a session without that option, ensure that all requests are being routed to the same server.</p> </div> <div class="paragraph"> <p>This example discussed use of Gremlin Server to demonstrate the HA configuration, but it is also easy to setup with three Gremlin Console instances. Simply start three Gremlin Console instances and use <code>GraphFactory</code> to read those configuration files to form the cluster. Furthermore, keep in mind that it is possible to have a Gremlin Console join a cluster handled by two Gremlin Servers or Neo4j Enterprise. The only limits as to how the configuration can be utilized are prescribed by Neo4j itself. Please refer to their <a href="http://neo4j.com/docs/operations-manual/current/#ha-setup-tutorial">documentation</a> for more information on how this feature works.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="hadoop-gremlin">Hadoop-Gremlin</h2> <div class="sectionbody"> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>hadoop-gremlin<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/hadoop-logo-notext.png" alt="hadoop logo notext" width="100"></span> <a href="http://hadoop.apache.org/">Hadoop</a> is a distributed computing framework that is used to process data represented across a multi-machine compute cluster. When the data in the Hadoop cluster represents a TinkerPop graph, then Hadoop-Gremlin can be used to process the graph using both TinkerPop’s OLTP and OLAP graph computing models.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> This section assumes that the user has a Hadoop 3.x cluster functioning. For more information on getting started with Hadoop, please see the <a href="http://hadoop.apache.org/docs/r3.3.1/hadoop-project-dist/hadoop-common/SingleCluster.html">Single Node Setup</a> tutorial. Moreover, if using <code>SparkGraphComputer</code> it is advisable that the reader also familiarize their self with and Spark (<a href="http://spark.apache.org/docs/latest/quick-start.html">Quick Start</a>). </td> </tr> </table> </div> <div class="sect2"> <h3 id="_installing_hadoop_gremlin">Installing Hadoop-Gremlin</h3> <div class="paragraph"> <p>If using <a href="#gremlin-console">Gremlin Console</a>, it is important to install the Hadoop-Gremlin plugin. Note that Hadoop-Gremlin requires a Gremlin Console restart after installing.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph gremlin> :install org.apache.tinkerpop hadoop-gremlin 3.7.3 ==>loaded: [org.apache.tinkerpop, hadoop-gremlin, 3.7.3] - restart the console to use [tinkerpop.hadoop] gremlin> :q $ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph gremlin> :plugin use tinkerpop.hadoop ==>tinkerpop.hadoop activated gremlin></code></pre> </div> </div> <div class="paragraph"> <p>It is important that the <code>CLASSPATH</code> environmental variable references <code>HADOOP_CONF_DIR</code> and that the configuration files in <code>HADOOP_CONF_DIR</code> contain references to a live Hadoop cluster. It is easy to verify a proper configuration from within the Gremlin Console. If <code>hdfs</code> references the local file system, then there is a configuration issue.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> hdfs ==>storage[org.apache.hadoop.fs.LocalFileSystem@65bb9029] // BAD gremlin> hdfs ==>storage[DFS[DFSClient[clientName=DFSClient_NONMAPREDUCE_1229457199_1, ugi=user (auth:SIMPLE)]]] // GOOD</code></pre> </div> </div> <div class="paragraph"> <p>The <code>HADOOP_GREMLIN_LIBS</code> references locations that contain jars that should be uploaded to a respective distributed cache (<a href="http://hadoop.apache.org/docs/3.7.3/hadoop-yarn/hadoop-yarn-site/YARN.html">YARN</a> or SparkServer). Note that the locations in <code>HADOOP_GREMLIN_LIBS</code> can be colon-separated (<code>:</code>) and all jars from all locations will be loaded into the cluster. Locations can be local paths (e.g. <code>/path/to/libs</code>), but may also be prefixed with a file scheme to reference files or directories in different file systems (e.g. <code>hdfs:///path/to/distributed/libs</code>). Typically, only the jars of the respective <code>GraphComputer</code> are required to be loaded.</p> </div> </div> <div class="sect2"> <h3 id="_properties_files">Properties Files</h3> <div class="paragraph"> <p><code>HadoopGraph</code> makes use of properties files which ultimately get turned into Apache configurations and/or Hadoop configurations.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin.graph=org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph gremlin.hadoop.inputLocation=tinkerpop-modern.kryo gremlin.hadoop.graphReader=org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat gremlin.hadoop.outputLocation=output gremlin.hadoop.graphWriter=org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoOutputFormat gremlin.hadoop.jarsInDistributedCache=true gremlin.hadoop.defaultGraphComputer=org.apache.tinkerpop.gremlin.spark.process.computer.SparkGraphComputer #################################### # Spark Configuration # #################################### spark.master=local[4] spark.executor.memory=1g spark.serializer=org.apache.tinkerpop.gremlin.spark.structure.io.gryo.GryoSerializer gremlin.spark.persistContext=true</code></pre> </div> </div> <div class="paragraph"> <p>A review of the Hadoop-Gremlin specific properties are provided in the table below. For the respective OLAP engines (<a href="#sparkgraphcomputer"><code>SparkGraphComputer</code></a> refer to their respective documentation for configuration options.</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 83.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Property</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.graph</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The class of the graph to construct using GraphFactory.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.inputLocation</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The location of the input file(s) for Hadoop-Gremlin to read the graph from.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.graphReader</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The class that the graph input file(s) are read with (e.g. an <code>InputFormat</code>).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.outputLocation</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The location to write the computed HadoopGraph to.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.graphWriter</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The class that the graph output file(s) are written with (e.g. an <code>OutputFormat</code>).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.jarsInDistributedCache</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Whether to upload the Hadoop-Gremlin jars to a distributed cache (necessary if jars are not on the machines' classpaths).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.defaultGraphComputer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">The default <code>GraphComputer</code> to use when <code>graph.compute()</code> is called. This is optional.</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Along with the properties above, the numerous <a href="http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/core-default.xml">Hadoop specific properties</a> can be added as needed to tune and parameterize the executed Hadoop-Gremlin job on the respective Hadoop cluster.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> As the size of the graphs being processed becomes large, it is important to fully understand how the underlying OLAP engine (e.g. Spark, etc.) works and understand the numerous parameterizations offered by these systems. Such knowledge can help alleviate out of memory exceptions, slow load times, slow processing times, garbage collection issues, etc. </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="_oltp_hadoop_gremlin">OLTP Hadoop-Gremlin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/hadoop-pipes.png" alt="hadoop pipes" width="180"></span> It is possible to execute OLTP operations over a <code>HadoopGraph</code>. However, realize that the underlying HDFS files are not random access and thus, to retrieve a vertex, a linear scan is required. OLTP operations are useful for peeking into the graph prior to executing a long running OLAP job — e.g. <code>g.V().valueMap().limit(10)</code>.</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> OLTP operations on <code>HadoopGraph</code> are not efficient. They require linear scans to execute and are unreasonable for large graphs. In such large graph situations, make use of <a href="#traversalvertexprogram">TraversalVertexProgram</a> which is the OLAP Gremlin machine. </td> </tr> </table> </div> <section class="tabs tabs-2"> <input id="tab-1729797209-1" type="radio" name="radio-set-1729797209-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797209-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797209-2" type="radio" name="radio-set-1729797209-1" class="tab-selector-2" /> <label for="tab-1729797209-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> 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>) ==><span class="predefined-constant">null</span> gremlin> hdfs.ls() ==>rw------- ubuntu ubuntu <span class="integer">5689</span> .bash_history ==>rw-r--r-- ubuntu ubuntu <span class="integer">220</span> .bash_logout ==>rw-r--r-- ubuntu ubuntu <span class="integer">4151</span> .bashrc ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .cache ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .docker ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .dotnet ==>rw-rw-r-- ubuntu ubuntu <span class="integer">26482</span> .gremlin_groovy_history ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .groovy ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .java ==>rw------- ubuntu ubuntu <span class="integer">20</span> .lesshst ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .local ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .m2 ==>rwxrwxr-x ubuntu docker <span class="integer">4096</span> (D) .npm ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nuget ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nvm ==>rw-r--r-- ubuntu ubuntu <span class="integer">807</span> .profile ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sdkman ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sparkStaging ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .ssh ==>rw-r--r-- ubuntu ubuntu <span class="integer">0</span> .sudo_as_admin_successful ==>rw------- ubuntu ubuntu <span class="integer">18114</span> .viminfo ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183</span> .zshrc ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178473115</span> <span class="float">3.6</span><span class="float">.8</span>-docsBuilt.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178468811</span> <span class="float">3.6</span><span class="float">.8</span>-finalDocs.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183482646</span> <span class="float">3.7</span><span class="float">.3</span>-docsBuilt.zip ==>rwxrwxr-x ubuntu ubuntu <span class="integer">489</span> cleanGrapes.sh ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) tinkerpop gremlin> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> g = traversal().withEmbedded(graph) ==>graphtraversalsource[hadoopgraph[gryoinputformat->gryooutputformat], standard] gremlin> g.V().count() ==><span class="integer">6</span> gremlin> g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>ripple ==>lop gremlin> g.V().group().by{<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)[<span class="integer">1</span>]}.by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).next() ==>a=[marko, vadas] ==>e=[peter] ==>i=[ripple] ==>o=[lop, 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">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>) hdfs.ls() graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph) g.V().count() g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) g.V().group().by{<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)[<span class="integer">1</span>]}.by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>).next()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="_olap_hadoop_gremlin">OLAP Hadoop-Gremlin</h3> <div class="paragraph"> <p><span class="image left"><img src="../images/hadoop-furnace.png" alt="hadoop furnace" width="180"></span> Hadoop-Gremlin was designed to execute OLAP operations via <code>GraphComputer</code>. The OLTP examples presented previously are reproduced below, but using <code>TraversalVertexProgram</code> for the execution of the Gremlin traversal.</p> </div> <div class="paragraph"> <p>A <code>Graph</code> in TinkerPop can support any number of <code>GraphComputer</code> implementations. Out of the box, Hadoop-Gremlin supports the following two implementations.</p> </div> <div class="ulist"> <ul> <li> <p><a href="#sparkgraphcomputer"><code>SparkGraphComputer</code></a>: Leverages Apache Spark to execute TinkerPop OLAP computations.</p> <div class="ulist"> <ul> <li> <p>The graph may fit within the total RAM of the cluster (supports larger graphs). Message passing is coordinated via Spark map/reduce/join operations on in-memory and disk-cached data (average speed traversals).</p> </li> </ul> </div> </li> </ul> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> <span class="image left"><img src="../images/gremlin-sugar.png" alt="gremlin sugar" width="50"></span> For those wanting to use the <a href="#sugar-plugin">SugarPlugin</a> with their submitted traversal, do <code>:remote config useSugar true</code> as well as <code>:plugin use tinkerpop.sugar</code> at the start of the Gremlin Console session if it is not already activated. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">$ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph plugin activated: tinkerpop.hadoop gremlin> :install org.apache.tinkerpop spark-gremlin 3.7.3 ==>loaded: [org.apache.tinkerpop, spark-gremlin, 3.7.3] - restart the console to use [tinkerpop.spark] gremlin> :q $ bin/gremlin.sh \,,,/ (o o) -----oOOo-(3)-oOOo----- plugin activated: tinkerpop.server plugin activated: tinkerpop.utilities plugin activated: tinkerpop.tinkergraph plugin activated: tinkerpop.hadoop gremlin> :plugin use tinkerpop.spark ==>tinkerpop.spark activated</code></pre> </div> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> Hadoop and Spark all depend on many of the same libraries (e.g. ZooKeeper, Snappy, Netty, Guava, etc.). Unfortunately, typically these dependencies are not to the same versions of the respective libraries. As such, it is may be necessary to manually cleanup dependency conflicts among different plugins. </td> </tr> </table> </div> <div class="sect3"> <h4 id="sparkgraphcomputer">SparkGraphComputer</h4> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>spark-gremlin<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="paragraph"> <p><span class="image left"><img src="../images/spark-logo.png" alt="spark logo" width="175"></span> <a href="http://spark.apache.org">Spark</a> is an Apache Software Foundation project focused on general-purpose OLAP data processing. Spark provides a hybrid in-memory/disk-based distributed computing model that is similar to Hadoop’s MapReduce model. Spark maintains a fluent function chaining DSL that is arguably easier for developers to work with than native Hadoop MapReduce. Spark-Gremlin provides an implementation of the bulk-synchronous parallel, distributed message passing algorithm within Spark and thus, any <code>VertexProgram</code> can be executed over <code>SparkGraphComputer</code>.</p> </div> <div class="paragraph"> <p>Furthermore the <code>lib/</code> directory should be distributed across all machines in the SparkServer cluster. For this purpose TinkerPop provides a helper script, which takes the Spark installation directory and the Spark machines as input:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="shell">bin/hadoop/init-tp-spark.sh /usr/local/spark spark@10.0.0.1 spark@10.0.0.2 spark@10.0.0.3</code></pre> </div> </div> <div class="paragraph"> <p>Once the <code>lib/</code> directory is distributed, <code>SparkGraphComputer</code> can be used as follows.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797218-1" type="radio" name="radio-set-1729797218-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797218-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797218-2" type="radio" name="radio-set-1729797218-1" class="tab-selector-2" /> <label for="tab-1729797218-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> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) ==>graphtraversalsource[hadoopgraph[gryoinputformat->gryooutputformat], sparkgraphcomputer] gremlin> g.V().count() ==><span class="integer">6</span> gremlin> g.V().out().out().values(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>lop ==>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">graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) g.V().count() g.V().out().out().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>For using lambdas in Gremlin-Groovy, simply provide <code>:remote connect</code> a <code>TraversalSource</code> which leverages SparkGraphComputer.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797218-3" type="radio" name="radio-set-1729797218-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797218-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797218-4" type="radio" name="radio-set-1729797218-3" class="tab-selector-2" /> <label for="tab-1729797218-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> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) ==>graphtraversalsource[hadoopgraph[gryoinputformat->gryooutputformat], sparkgraphcomputer] gremlin> :remote connect tinkerpop.hadoop graph g [INFO] o.a.t.g.h.j.HadoopGremlinPlugin - HADOOP_GREMLIN_LIBS is set <span class="key">to</span>: <span class="regexp"><span class="delimiter">/</span><span class="content">home</span><span class="delimiter">/</span></span>ubuntu/tinkerpop/gremlin-console/target/apache-tinkerpop-gremlin-console-<span class="float">3.7</span><span class="float">.3</span>-standalone/ext/tinkergraph-gremlin/lib [INFO] o.a.t.g.h.j.HadoopGremlinPlugin - HADOOP_GREMLIN_LIBS is set <span class="key">to</span>: <span class="regexp"><span class="delimiter">/</span><span class="content">home</span><span class="delimiter">/</span></span>ubuntu/tinkerpop/gremlin-console/target/apache-tinkerpop-gremlin-console-<span class="float">3.7</span><span class="float">.3</span>-standalone/ext/tinkergraph-gremlin/lib [INFO] o.a.t.g.h.j.HadoopGremlinPlugin - HADOOP_GREMLIN_LIBS is set <span class="key">to</span>: <span class="regexp"><span class="delimiter">/</span><span class="content">home</span><span class="delimiter">/</span></span>ubuntu/tinkerpop/gremlin-console/target/apache-tinkerpop-gremlin-console-<span class="float">3.7</span><span class="float">.3</span>-standalone/ext/tinkergraph-gremlin/lib ==>useTraversalSource=graphtraversalsource[hadoopgraph[gryoinputformat->gryooutputformat], sparkgraphcomputer] ==>useSugar=<span class="predefined-constant">false</span> gremlin> :> g.V().group().by{<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)[<span class="integer">1</span>]}.by(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>) ==>[<span class="key">a</span>:[marko,vadas],<span class="key">i</span>:[ripple],<span class="key">e</span>:[peter],<span class="key">o</span>:[lop,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">graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) g = traversal().withEmbedded(graph).withComputer(SparkGraphComputer) :remote connect tinkerpop.hadoop graph g :> g.V().group().by{<span class="local-variable">it</span>.value(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>)[<span class="integer">1</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>SparkGraphComputer</code> algorithm leverages Spark’s caching abilities to reduce the amount of data shuffled across the wire on each iteration of the <a href="#vertexprogram"><code>VertexProgram</code></a>. When the graph is loaded as a Spark RDD (Resilient Distributed Dataset) it is immediately cached as <code>graphRDD</code>. The <code>graphRDD</code> is a distributed adjacency list which encodes the vertex, its properties, and all its incident edges. On the first iteration, each vertex (in parallel) is passed through <code>VertexProgram.execute()</code>. This yields an output of the vertex’s mutated state (i.e. updated compute keys — <code>propertyX</code>) and its outgoing messages. This <code>viewOutgoingRDD</code> is then reduced to <code>viewIncomingRDD</code> where the outgoing messages are sent to their respective vertices. If a <code>MessageCombiner</code> exists for the vertex program, then messages are aggregated locally and globally to ultimately yield one incoming message for the vertex. This reduce sequence is the "message pass." If the vertex program does not terminate on this iteration, then the <code>viewIncomingRDD</code> is joined with the cached <code>graphRDD</code> and the process continues. When there are no more iterations, there is a final join and the resultant RDD is stripped of its edges and messages. This <code>mapReduceRDD</code> is cached and is processed by each <a href="#mapreduce"><code>MapReduce</code></a> job in the <a href="#graphcomputer"><code>GraphComputer</code></a> computation.</p> </div> <div class="imageblock"> <div class="content"> <img src="../images/spark-algorithm.png" alt="spark algorithm" width="775"> </div> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 83.3334%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Property</th> <th class="tableblock halign-left valign-top">Description</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.graphReader</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A class for reading a graph-based RDD (e.g. an <code>InputRDD</code> or <code>InputFormat</code>).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.hadoop.graphWriter</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A class for writing a graph-based RDD (e.g. an <code>OutputRDD</code> or <code>OutputFormat</code>).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.spark.graphStorageLevel</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">What <code>StorageLevel</code> to use for the cached graph during job execution (default <code>MEMORY_ONLY</code>).</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.spark.persistContext</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Whether to create a new <code>SparkContext</code> for every <code>SparkGraphComputer</code> or to reuse an existing one.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">gremlin.spark.persistStorageLevel</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">What <code>StorageLevel</code> to use when persisted RDDs via <code>PersistedOutputRDD</code> (default <code>MEMORY_ONLY</code>).</p></td> </tr> </tbody> </table> <div class="sect4"> <h5 id="_inputrdd_and_outputrdd">InputRDD and OutputRDD</h5> <div class="paragraph"> <p>If the provider/user does not want to use Hadoop <code>InputFormats</code>, it is possible to leverage Spark’s RDD constructs directly. An <code>InputRDD</code> provides a read method that takes a <code>SparkContext</code> and returns a graphRDD. Likewise, and <code>OutputRDD</code> is used for writing a graphRDD.</p> </div> <div class="paragraph"> <p>If the graph system provider uses an <code>InputRDD</code>, the RDD should maintain an associated <code>org.apache.spark.Partitioner</code>. By doing so, <code>SparkGraphComputer</code> will not partition the loaded graph across the cluster as it has already been partitioned by the graph system provider. This can save a significant amount of time and space resources. If the <code>InputRDD</code> does not have a registered partitioner, <code>SparkGraphComputer</code> will partition the graph using a <code>org.apache.spark.HashPartitioner</code> with the number of partitions being either the number of existing partitions in the input (i.e. input splits) or the user specified number of <code>GraphComputer.workers()</code>.</p> </div> </div> <div class="sect4"> <h5 id="_storage_levels">Storage Levels</h5> <div class="paragraph"> <p>The <code>SparkGraphComputer</code> uses <code>MEMORY_ONLY</code> to cache the input graph and the output graph by default. Users should be aware of the impact of different storage levels, since the default settings can quickly lead to memory issues on larger graphs. An overview of Spark’s persistence settings is provided in <a href="http://spark.apache.org/docs/latest/rdd-programming-guide.html#rdd-persistence">Spark’s programming guide</a>.</p> </div> </div> <div class="sect4"> <h5 id="_using_a_persisted_context">Using a Persisted Context</h5> <div class="paragraph"> <p>It is possible to persist the graph RDD between jobs within the <code>SparkContext</code> (e.g. SparkServer) by leveraging <code>PersistedOutputRDD</code>. Note that <code>gremlin.spark.persistContext</code> should be set to <code>true</code> or else the persisted RDD will be destroyed when the <code>SparkContext</code> closes. The persisted RDD is named by the <code>gremlin.hadoop.outputLocation</code> configuration. Similarly, <code>PersistedInputRDD</code> is used with respective <code>gremlin.hadoop.inputLocation</code> to retrieve the persisted RDD from the <code>SparkContext</code>.</p> </div> <div class="paragraph"> <p>When using a persistent <code>SparkContext</code> the configuration used by the original Spark Configuration will be inherited by all threaded references to that Spark Context. The exception to this rule are those properties which have a specific thread local effect.</p> </div> <div class="olist arabic"> <div class="title">Thread Local Properties</div> <ol class="arabic"> <li> <p>spark.jobGroup.id</p> </li> <li> <p>spark.job.description</p> </li> <li> <p>spark.job.interruptOnCancel</p> </li> <li> <p>spark.scheduler.pool</p> </li> </ol> </div> <div class="paragraph"> <p>Finally, there is a <code>spark</code> object that can be used to manage persisted RDDs (see <a href="#interacting-with-spark">Interacting with Spark</a>).</p> </div> </div> <div class="sect4"> <h5 id="clonevertexprogramusingspark">Using CloneVertexProgram</h5> <div class="paragraph"> <p>The <a href="#clonevertexprogram">CloneVertexProgram</a> copies a whole graph from any graph <code>InputFormat</code> to any graph <code>OutputFormat</code>. TinkerPop provides formats such as <code>GraphSONOutputFormat</code>, <code>GryoOutputFormat</code> or <code>ScriptOutputFormat</code>. The example below takes a Hadoop graph as the input (in <code>GryoInputFormat</code>) and exports it as a GraphSON file (<code>GraphSONOutputFormat</code>).</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797218-5" type="radio" name="radio-set-1729797218-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797218-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729797218-6" type="radio" name="radio-set-1729797218-5" class="tab-selector-2" /> <label for="tab-1729797218-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> 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>) ==><span class="predefined-constant">null</span> gremlin> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.hadoop.graphWriter</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONOutputFormat</span><span class="delimiter">'</span></span>) ==><span class="predefined-constant">null</span> gremlin> graph.compute(SparkGraphComputer).program(CloneVertexProgram.build().create()).submit().get() ==>result[hadoopgraph[graphsoninputformat->graphsonoutputformat],memory[<span class="key">size</span>:<span class="integer">0</span>]] gremlin> hdfs.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) ~g gremlin> hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output/~g</span><span class="delimiter">'</span></span>) ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">1</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">outE</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">9</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">3</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}}],<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">7</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">2</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.5</span>}}},{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">8</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">4</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">0</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</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="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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">1</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">29</span>}}]}} ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">2</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">inE</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">7</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">1</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.5</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">2</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</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="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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">3</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">27</span>}}]}} ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">3</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">inE</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">9</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">1</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}},{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">11</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">4</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}},{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">12</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">6</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.2</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">4</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>}],<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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">5</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">java</span><span class="delimiter">"</span></span>}]}} ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">4</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">inE</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">8</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">1</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">outE</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">10</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">5</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}},{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">11</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">3</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">6</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">josh</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">7</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">32</span>}}]}} ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">5</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">inE</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">10</span>},<span class="string"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">4</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">8</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">ripple</span><span class="delimiter">"</span></span>}],<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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">9</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">java</span><span class="delimiter">"</span></span>}]}} ==>{<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">6</span>},<span class="string"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<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">outE</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">12</span>},<span class="string"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">3</span>},<span class="string"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Double</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="float">0.2</span>}}}]},<span class="string"><span class="delimiter">"</span><span class="content">properties</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">10</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">peter</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">id</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int64</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></span>:<span class="integer">11</span>},<span class="string"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:{<span class="string"><span class="delimiter">"</span><span class="content">@type</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">g:Int32</span><span class="delimiter">"</span></span>,<span class="string"><span class="delimiter">"</span><span class="content">@value</span><span class="delimiter">"</span></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">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(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.hadoop.graphWriter</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONOutputFormat</span><span class="delimiter">'</span></span>) graph.compute(SparkGraphComputer).program(CloneVertexProgram.build().create()).submit().get() hdfs.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output/~g</span><span class="delimiter">'</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> </div> </div> <div class="sect2"> <h3 id="_inputoutput_formats">Input/Output Formats</h3> <div class="paragraph"> <p><span class="image right"><img src="../images/adjacency-list.png" alt="adjacency list" width="300"></span> Hadoop-Gremlin provides various I/O formats — i.e. Hadoop <code>InputFormat</code> and <code>OutputFormat</code>. All of the formats make use of an <a href="http://en.wikipedia.org/wiki/Adjacency_list">adjacency list</a> representation of the graph where each "row" represents a single vertex, its properties, and its incoming and outgoing edges.</p> </div> <div class="paragraph"> <p><br></p> </div> <div class="sect3"> <h4 id="gryo-io-format">Gryo I/O Format</h4> <div class="ulist"> <ul> <li> <p><strong>InputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat</code></p> </li> <li> <p><strong>OutputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoOutputFormat</code></p> </li> </ul> </div> <div class="paragraph"> <p><a href="#gryo">Gryo</a> is a binary graph format that leverages <a href="https://github.com/EsotericSoftware/kryo">Kryo</a> to make a compact, binary representation of a vertex. It is recommended that users leverage Gryo given its space/time savings over text-based representations.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The <code>GryoInputFormat</code> is splittable. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="graphson-io-format">GraphSON I/O Format</h4> <div class="ulist"> <ul> <li> <p><strong>InputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONInputFormat</code></p> </li> <li> <p><strong>OutputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONOutputFormat</code></p> </li> </ul> </div> <div class="paragraph"> <p><a href="#graphson">GraphSON</a> is a JSON based graph format. GraphSON is a space-expensive graph format in that it is a text-based markup language. However, it is convenient for many developers to work with as its structure is simple (easy to create and parse).</p> </div> <div class="paragraph"> <p>The data below represents an adjacency list representation of the classic TinkerGraph toy graph in GraphSON format.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="json">{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">1</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">outE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">9</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">3</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}],<span class="key"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">7</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">2</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.5</span>}},{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">8</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">4</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">0</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</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="key"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">1</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="integer">29</span>}]}} {<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">2</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">inE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">7</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">1</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.5</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">2</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</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="key"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">3</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="integer">27</span>}]}} {<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">3</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">inE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">9</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">1</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}},{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">11</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">4</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}},{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">12</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">6</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.2</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">4</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">lop</span><span class="delimiter">"</span></span>}],<span class="key"><span class="delimiter">"</span><span class="content">lang</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">5</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">java</span><span class="delimiter">"</span></span>}]}} {<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">4</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">inE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">8</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">1</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">outE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">10</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">5</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}},{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">11</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">3</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.4</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">6</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">josh</span><span class="delimiter">"</span></span>}],<span class="key"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">7</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="integer">32</span>}]}} {<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">5</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">software</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">inE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">10</span>,<span class="key"><span class="delimiter">"</span><span class="content">outV</span><span class="delimiter">"</span></span>:<span class="integer">4</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">1.0</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">8</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">ripple</span><span class="delimiter">"</span></span>}],<span class="key"><span class="delimiter">"</span><span class="content">lang</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">9</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">java</span><span class="delimiter">"</span></span>}]}} {<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">6</span>,<span class="key"><span class="delimiter">"</span><span class="content">label</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">person</span><span class="delimiter">"</span></span>,<span class="key"><span class="delimiter">"</span><span class="content">outE</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">created</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">12</span>,<span class="key"><span class="delimiter">"</span><span class="content">inV</span><span class="delimiter">"</span></span>:<span class="integer">3</span>,<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>:<span class="float">0.2</span>}}]},<span class="key"><span class="delimiter">"</span><span class="content">properties</span><span class="delimiter">"</span></span>:{<span class="key"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">10</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="string"><span class="delimiter">"</span><span class="content">peter</span><span class="delimiter">"</span></span>}],<span class="key"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>:[{<span class="key"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>:<span class="integer">11</span>,<span class="key"><span class="delimiter">"</span><span class="content">value</span><span class="delimiter">"</span></span>:<span class="integer">35</span>}]}}</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="script-io-format">Script I/O Format</h4> <div class="ulist"> <ul> <li> <p><strong>InputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptInputFormat</code></p> </li> <li> <p><strong>OutputFormat</strong>: <code>org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptOutputFormat</code></p> </li> </ul> </div> <div class="paragraph"> <p><code>ScriptInputFormat</code> and <code>ScriptOutputFormat</code> take an arbitrary script and use that script to either read or write <code>Vertex</code> objects, respectively. This can be considered the most general <code>InputFormat</code>/<code>OutputFormat</code> possible in that Hadoop-Gremlin uses the user provided script for all reading/writing.</p> </div> <div class="sect4"> <h5 id="_scriptinputformat">ScriptInputFormat</h5> <div class="paragraph"> <p>The data below represents an adjacency list representation of the classic TinkerGraph toy graph. First line reads, "vertex <code>1</code>, labeled <code>person</code> having 2 property values (<code>marko</code> and <code>29</code>) has 3 outgoing edges; the first edge is labeled <code>knows</code>, connects the current vertex <code>1</code> with vertex <code>2</code> and has a property value <code>0.4</code>, and so on."</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code>1:person:marko:29 knows:2:0.5,knows:4:1.0,created:3:0.4 2:person:vadas:27 3:project:lop:java 4:person:josh:32 created:3:0.4,created:5:1.0 5:project:ripple:java 6:person:peter:35 created:3:0.2</code></pre> </div> </div> <div class="paragraph"> <p>There is no corresponding <code>InputFormat</code> that can parse this particular file (or some adjacency list variant of it). As such, <code>ScriptInputFormat</code> can be used. With <code>ScriptInputFormat</code> a script is stored in HDFS and leveraged by each mapper in the Hadoop job. The script must have the following method defined:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">parse</span>(<span class="predefined-type">String</span> line) { ... }</code></pre> </div> </div> <div class="paragraph"> <p>In order to create vertices and edges, the <code>parse()</code> method gets access to a global variable named <code>graph</code>, which holds the local <code>StarGraph</code> for the current line/vertex.</p> </div> <div class="paragraph"> <p>An appropriate <code>parse()</code> for the above adjacency list file is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">parse</span>(line) { <span class="keyword">def</span> parts = line.split(<span class="regexp"><span class="delimiter">/</span><span class="content"> </span><span class="delimiter">/</span></span>) <span class="keyword">def</span> (id, label, name, x) = parts[<span class="integer">0</span>].split(<span class="regexp"><span class="delimiter">/</span><span class="content">:</span><span class="delimiter">/</span></span>).toList() <span class="keyword">def</span> v1 = graph.addVertex(T.id, id, T.label, label) <span class="keyword">if</span> (name != <span class="predefined-constant">null</span>) v1.property(<span class="string"><span class="delimiter">'</span><span class="content">name</span><span class="delimiter">'</span></span>, name) <span class="comment">// first value is always the name</span> <span class="keyword">if</span> (x != <span class="predefined-constant">null</span>) { <span class="comment">// second value depends on the vertex label; it's either</span> <span class="comment">// the age of a person or the language of a project</span> <span class="keyword">if</span> (label.equals(<span class="string"><span class="delimiter">'</span><span class="content">project</span><span class="delimiter">'</span></span>)) v1.property(<span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>, x) <span class="keyword">else</span> v1.property(<span class="string"><span class="delimiter">'</span><span class="content">age</span><span class="delimiter">'</span></span>, <span class="predefined-type">Integer</span>.valueOf(x)) } <span class="keyword">if</span> (parts.length == <span class="integer">2</span>) { parts[<span class="integer">1</span>].split(<span class="regexp"><span class="delimiter">/</span><span class="content">,</span><span class="delimiter">/</span></span>).grep { !<span class="local-variable">it</span>.isEmpty() }.each { <span class="keyword">def</span> (eLabel, refId, weight) = <span class="local-variable">it</span>.split(<span class="regexp"><span class="delimiter">/</span><span class="content">:</span><span class="delimiter">/</span></span>).toList() <span class="keyword">def</span> v2 = graph.addVertex(T.id, refId) v1.addOutEdge(eLabel, v2, <span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>, <span class="predefined-type">Double</span>.valueOf(weight)) } } <span class="keyword">return</span> v1 }</code></pre> </div> </div> <div class="paragraph"> <p>The resultant <code>Vertex</code> denotes whether the line parsed yielded a valid Vertex. As such, if the line is not valid (e.g. a comment line, a skip line, etc.), then simply return <code>null</code>.</p> </div> </div> <div class="sect4"> <h5 id="_scriptoutputformat_support">ScriptOutputFormat Support</h5> <div class="paragraph"> <p>The principle above can also be used to convert a vertex to an arbitrary <code>String</code> representation that is ultimately streamed back to a file in HDFS. This is the role of <code>ScriptOutputFormat</code>. <code>ScriptOutputFormat</code> requires that the provided script maintains a method with the following signature:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">stringify</span>(Vertex vertex) { ... }</code></pre> </div> </div> <div class="paragraph"> <p>An appropriate <code>stringify()</code> to produce output in the same format that was shown in the <code>ScriptInputFormat</code> sample is:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> <span class="function">stringify</span>(vertex) { <span class="keyword">def</span> v = vertex.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">age</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">lang</span><span class="delimiter">'</span></span>).inject(vertex.id(), vertex.label()).join(<span class="string"><span class="delimiter">'</span><span class="content">:</span><span class="delimiter">'</span></span>) <span class="keyword">def</span> outE = vertex.outE().map { <span class="keyword">def</span> e = <span class="local-variable">it</span>.get() e.values(<span class="string"><span class="delimiter">'</span><span class="content">weight</span><span class="delimiter">'</span></span>).inject(e.label(), e.inV().next().id()).join(<span class="string"><span class="delimiter">'</span><span class="content">:</span><span class="delimiter">'</span></span>) }.join(<span class="string"><span class="delimiter">'</span><span class="content">,</span><span class="delimiter">'</span></span>) <span class="keyword">return</span> [v, outE].join(<span class="string"><span class="delimiter">'</span><span class="content">\t</span><span class="delimiter">'</span></span>) }</code></pre> </div> </div> </div> </div> </div> <div class="sect2"> <h3 id="_storage_systems">Storage Systems</h3> <div class="paragraph"> <p>Hadoop-Gremlin provides two implementations of the <code>Storage</code> API:</p> </div> <div class="ulist"> <ul> <li> <p><code>FileSystemStorage</code>: Access HDFS and local file system data.</p> </li> <li> <p><code>SparkContextStorage</code>: Access Spark persisted RDD data.</p> </li> </ul> </div> <div class="sect3"> <h4 id="interacting-with-hdfs">Interacting with HDFS</h4> <div class="paragraph"> <p>The distributed file system of Hadoop is called <a href="http://en.wikipedia.org/wiki/Apache_Hadoop#Hadoop_distributed_file_system">HDFS</a>. The results of any OLAP operation are stored in HDFS accessible via <code>hdfs</code>. For local file system access, there is <code>fs</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797232-1" type="radio" name="radio-set-1729797232-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797232-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797232-2" type="radio" name="radio-set-1729797232-1" class="tab-selector-2" /> <label for="tab-1729797232-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> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> graph.compute(SparkGraphComputer).program(PeerPressureVertexProgram.build().create(graph)).mapReduce(ClusterCountMapReduce.build().memoryKey(<span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>).create()).submit().get(); ==>result[hadoopgraph[gryoinputformat->gryooutputformat],memory[<span class="key">size</span>:<span class="integer">1</span>]] gremlin> hdfs.ls() ==>rw------- ubuntu ubuntu <span class="integer">5689</span> .bash_history ==>rw-r--r-- ubuntu ubuntu <span class="integer">220</span> .bash_logout ==>rw-r--r-- ubuntu ubuntu <span class="integer">4151</span> .bashrc ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .cache ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .docker ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .dotnet ==>rw-rw-r-- ubuntu ubuntu <span class="integer">26434</span> .gremlin_groovy_history ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .groovy ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .java ==>rw------- ubuntu ubuntu <span class="integer">20</span> .lesshst ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .local ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .m2 ==>rwxrwxr-x ubuntu docker <span class="integer">4096</span> (D) .npm ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nuget ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nvm ==>rw-r--r-- ubuntu ubuntu <span class="integer">807</span> .profile ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sdkman ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sparkStaging ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .ssh ==>rw-r--r-- ubuntu ubuntu <span class="integer">0</span> .sudo_as_admin_successful ==>rw------- ubuntu ubuntu <span class="integer">18114</span> .viminfo ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183</span> .zshrc ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178473115</span> <span class="float">3.6</span><span class="float">.8</span>-docsBuilt.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178468811</span> <span class="float">3.6</span><span class="float">.8</span>-finalDocs.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183482646</span> <span class="float">3.7</span><span class="float">.3</span>-docsBuilt.zip ==>rwxrwxr-x ubuntu ubuntu <span class="integer">489</span> cleanGrapes.sh ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) tinkerpop gremlin> hdfs.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) clusterCount ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) ~g gremlin> hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, GryoInputFormat) ==>v[<span class="integer">4</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">2</span>] gremlin> hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>, SequenceFileInputFormat) ==><span class="integer">2</span> gremlin> hdfs.rm(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) ==><span class="predefined-constant">true</span> gremlin> hdfs.ls() ==>rw------- ubuntu ubuntu <span class="integer">5689</span> .bash_history ==>rw-r--r-- ubuntu ubuntu <span class="integer">220</span> .bash_logout ==>rw-r--r-- ubuntu ubuntu <span class="integer">4151</span> .bashrc ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .cache ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .docker ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .dotnet ==>rw-rw-r-- ubuntu ubuntu <span class="integer">26368</span> .gremlin_groovy_history ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .groovy ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .java ==>rw------- ubuntu ubuntu <span class="integer">20</span> .lesshst ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .local ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .m2 ==>rwxrwxr-x ubuntu docker <span class="integer">4096</span> (D) .npm ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nuget ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .nvm ==>rw-r--r-- ubuntu ubuntu <span class="integer">807</span> .profile ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sdkman ==>rwxr-xr-x ubuntu ubuntu <span class="integer">4096</span> (D) .sparkStaging ==>rwx------ ubuntu ubuntu <span class="integer">4096</span> (D) .ssh ==>rw-r--r-- ubuntu ubuntu <span class="integer">0</span> .sudo_as_admin_successful ==>rw------- ubuntu ubuntu <span class="integer">18114</span> .viminfo ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183</span> .zshrc ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178473115</span> <span class="float">3.6</span><span class="float">.8</span>-docsBuilt.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">178468811</span> <span class="float">3.6</span><span class="float">.8</span>-finalDocs.zip ==>rw-rw-r-- ubuntu ubuntu <span class="integer">183482646</span> <span class="float">3.7</span><span class="float">.3</span>-docsBuilt.zip ==>rwxrwxr-x ubuntu ubuntu <span class="integer">489</span> cleanGrapes.sh ==>rwxrwxr-x ubuntu ubuntu <span class="integer">4096</span> (D) tinkerpop</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">graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) graph.compute(SparkGraphComputer).program(PeerPressureVertexProgram.build().create(graph)).mapReduce(ClusterCountMapReduce.build().memoryKey(<span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>).create()).submit().get(); hdfs.ls() hdfs.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, GryoInputFormat) hdfs.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>, SequenceFileInputFormat) hdfs.rm(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) hdfs.ls()</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="interacting-with-spark">Interacting with Spark</h4> <div class="paragraph"> <p>If a Spark context is persisted, then Spark RDDs will remain the Spark cache and accessible over subsequent jobs. RDDs are retrieved and saved to the <code>SparkContext</code> via <code>PersistedInputRDD</code> and <code>PersistedOutputRDD</code> respectively. Persisted RDDs can be accessed using <code>spark</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797232-3" type="radio" name="radio-set-1729797232-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797232-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797232-4" type="radio" name="radio-set-1729797232-3" class="tab-selector-2" /> <label for="tab-1729797232-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> Spark.create(<span class="string"><span class="delimiter">'</span><span class="content">local[4]</span><span class="delimiter">'</span></span>) ==>org.apache.spark.SparkContext<span class="error">@</span><span class="integer">35</span>c3d6e8 gremlin> graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) ==>hadoopgraph[gryoinputformat->gryooutputformat] gremlin> graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.hadoop.graphWriter</span><span class="delimiter">'</span></span>, PersistedOutputRDD.class.getCanonicalName()) ==><span class="predefined-constant">null</span> gremlin> graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.spark.persistContext</span><span class="delimiter">'</span></span>,<span class="predefined-constant">true</span>) ==><span class="predefined-constant">null</span> gremlin> graph.compute(SparkGraphComputer).program(PeerPressureVertexProgram.build().create(graph)).mapReduce(ClusterCountMapReduce.build().memoryKey(<span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>).create()).submit().get(); ==>result[hadoopgraph[persistedinputrdd->persistedoutputrdd],memory[<span class="key">size</span>:<span class="integer">1</span>]] gremlin> spark.ls() gremlin> spark.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) ==>output/clusterCount [Memory Deserialized <span class="integer">1</span>x Replicated] ==>output/~g [Memory Deserialized <span class="integer">1</span>x Replicated] gremlin> spark.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, PersistedInputRDD) ==>v[<span class="integer">4</span>] ==>v[<span class="integer">1</span>] ==>v[<span class="integer">6</span>] ==>v[<span class="integer">3</span>] ==>v[<span class="integer">5</span>] ==>v[<span class="integer">2</span>] gremlin> spark.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>, PersistedInputRDD) ==><span class="integer">2</span> gremlin> spark.rm(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) ==><span class="predefined-constant">true</span> gremlin> spark.ls()</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">Spark.create(<span class="string"><span class="delimiter">'</span><span class="content">local[4]</span><span class="delimiter">'</span></span>) graph = GraphFactory.open(<span class="string"><span class="delimiter">'</span><span class="content">conf/hadoop/hadoop-gryo.properties</span><span class="delimiter">'</span></span>) graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.hadoop.graphWriter</span><span class="delimiter">'</span></span>, PersistedOutputRDD.class.getCanonicalName()) graph.configuration().setProperty(<span class="string"><span class="delimiter">'</span><span class="content">gremlin.spark.persistContext</span><span class="delimiter">'</span></span>,<span class="predefined-constant">true</span>) graph.compute(SparkGraphComputer).program(PeerPressureVertexProgram.build().create(graph)).mapReduce(ClusterCountMapReduce.build().memoryKey(<span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>).create()).submit().get(); spark.ls() spark.ls(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) spark.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, PersistedInputRDD) spark.head(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">clusterCount</span><span class="delimiter">'</span></span>, PersistedInputRDD) spark.rm(<span class="string"><span class="delimiter">'</span><span class="content">output</span><span class="delimiter">'</span></span>) spark.ls()</code></pre> </div> </div> </div> </div> </section> </div> </div> </div> </div> <h1 id="compilers" class="sect0">Gremlin Compilers</h1> <div class="openblock partintro"> <div class="content"> There are many languages built to query data. SQL is typically used to query relational data. There is SPARQL for RDF data. Cypher is used to do pattern matching in graph data. The list could go on. Compilers convert languages like these to Gremlin so that it becomes possible to use them in any context that Gremlin is used. In other words, a Gremlin Compiler enables a particular query language to work on any TinkerPop-enabled graph system. </div> </div> <div class="sect1"> <h2 id="sparql-gremlin">SPARQL-Gremlin</h2> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <img src="../images/gremlintron.png" alt="gremlintron" width="225"> </div> </div> <div class="paragraph"> <p>The SPARQL-Gremlin compiler, transforms <a href="https://en.wikipedia.org/wiki/SPARQL">SPARQL</a> queries into Gremlin traversals. It uses the <a href="https://jena.apache.org/index.html">Apache Jena</a> SPARQL processor <a href="https://jena.apache.org/documentation/query/index.html">ARQ</a>, which provides access to a syntax tree of a SPARQL query.</p> </div> <div class="paragraph"> <p>The goal of this work is to bridge the query interoperability gap between the two famous, yet fairly disconnected, graph communities: Semantic Web (which relies on the RDF data model) and Graph database (which relies on property graph data model).</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The foundational research work on SPARQL-Gremlin compiler (aka Gremlinator) can be found in the <a href="https://arxiv.org/pdf/1801.02911.pdf">Gremlinator paper</a>. This paper presents the graph query language semantics of SPARQL and Gremlin, and a formal mapping between SPARQL pattern matching graph patterns and Gremlin traversals. </td> </tr> </table> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><dependency></span> <span class="tag"><groupId></span>org.apache.tinkerpop<span class="tag"></groupId></span> <span class="tag"><artifactId></span>sparql-gremlin<span class="tag"></artifactId></span> <span class="tag"><version></span>3.7.3<span class="tag"></version></span> <span class="tag"></dependency></span></code></pre> </div> </div> <div class="paragraph"> <p>The SPARQL-Gremlin compiler converts <a href="https://en.wikipedia.org/wiki/SPARQL">SPARQL</a> queries into Gremlin so that they can be executed across any TinkerPop-enabled graph system. To use this compiler in the Gremlin Console, first install and activate the "tinkerpop.sparql" plugin:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">gremlin> :install org.apache.tinkerpop sparql-gremlin 3.7.3 ==>Loaded: [org.apache.tinkerpop, sparql-gremlin, 3.7.3] gremlin> :plugin use tinkerpop.sparql ==>tinkerpop.sparql activated</code></pre> </div> </div> <div class="paragraph"> <p>Installing this plugin will download appropriate dependencies and import certain classes to the console so that they may be used as follows:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-1" type="radio" name="radio-set-1729797246-1" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-1" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-2" type="radio" name="radio-set-1729797246-1" class="tab-selector-2" /> <label for="tab-1729797246-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> graph = TinkerFactory.createModern() ==>tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>] gremlin> g = traversal(SparqlTraversalSource).withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(1)</b> ==>sparqltraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age } ORDER BY ASC(?age)</span><span class="delimiter">"""</span></span>) <span class="comment">//</span>// <b class="conum">(2)</b> ==>[<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<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">graph = TinkerFactory.createModern() g = traversal(SparqlTraversalSource).withEmbedded(graph) <span class="comment">//</span>// <b class="conum">(1)</b> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age } ORDER BY ASC(?age)</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>Define <code>g</code> as a <code>TraversalSource</code> that uses the <code>SparqlTraversalSource</code> - by default, the <code>traversal()</code> method usually returns a <code>GraphTraversalSource</code> which includes the standard Gremlin starts steps like <code>V()</code> or <code>E()</code>. In this case, the <code>SparqlTraversalSource</code> enables starts steps that are specific to SPARQL only - in this case the <code>sparql()</code> start step.</p> </li> <li> <p>Execute a SPARQL query against the TinkerGraph instance. The <code>SparqlTraversalSource</code> uses a <a href="#traversalstrategy">TraversalStrategy</a> to transparently converts that SPARQL query into a standard Gremlin traversal and then when finally iterated, executes that against the TinkerGraph.</p> </li> </ol> </div> <div class="sect2"> <h3 id="prefixes">Prefixes</h3> <div class="paragraph"> <p>The SPARQL-Gremlin compiler supports the following prefixes to traverse the graph:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 50%;"> <col style="width: 50%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">Prefix</th> <th class="tableblock halign-left valign-top">Purpose</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>v:<id|label|<name>></code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">access to vertex id, label or property value</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>e:<label></code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">out-edge traversal</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><code>p:<name></code></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">property traversal</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>Note that element IDs and labels are treated like normal properties, hence they can be accessed using the same pattern:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-3" type="radio" name="radio-set-1729797246-3" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-3" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-4" type="radio" name="radio-set-1729797246-3" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?id ?label WHERE { ?element v:name ?name . ?element v:id ?id . ?element v:label ?label .}</span><span class="delimiter">"""</span></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">name</span>:vadas,<span class="key">id</span>:<span class="integer">2</span>,<span class="key">label</span>:person] ==>[<span class="key">name</span>:lop,<span class="key">id</span>:<span class="integer">3</span>,<span class="key">label</span>:software] ==>[<span class="key">name</span>:josh,<span class="key">id</span>:<span class="integer">4</span>,<span class="key">label</span>:person] ==>[<span class="key">name</span>:ripple,<span class="key">id</span>:<span class="integer">5</span>,<span class="key">label</span>:software] ==>[<span class="key">name</span>:peter,<span class="key">id</span>:<span class="integer">6</span>,<span class="key">label</span>:person]</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?id ?label WHERE { ?element v:name ?name . ?element v:id ?id . ?element v:label ?label .}</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect2"> <h3 id="supported-queries">Supported Queries</h3> <div class="paragraph"> <p>The SPARQL-Gremlin compiler currently supports translation of the SPARQL 1.0 specification, especially <code>SELECT</code> queries, though there is an on-going effort to cover the entire SPARQL 1.1 query feature spectrum. The supported SPARQL query types are:</p> </div> <div class="ulist"> <ul> <li> <p>Union</p> </li> <li> <p>Optional</p> </li> <li> <p>Order-By</p> </li> <li> <p>Group-By</p> </li> <li> <p>STAR-shaped or <em>neighbourhood queries</em></p> </li> <li> <p>Query modifiers, such as:</p> <div class="ulist"> <ul> <li> <p>Filter with <em>restrictions</em></p> </li> <li> <p>Count</p> </li> <li> <p>LIMIT</p> </li> <li> <p>OFFSET</p> </li> </ul> </div> </li> </ul> </div> </div> <div class="sect2"> <h3 id="limitations">Limitations</h3> <div class="paragraph"> <p>The current implementation of SPARQL-Gremlin compiler (i.e. SPARQL-Gremlin) does not support the following cases:</p> </div> <div class="ulist"> <ul> <li> <p>SPARQL queries with variables in the predicate position are not currently covered, with an exception of the following case:</p> </li> </ul> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { ?x ?y ?z . }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="ulist"> <ul> <li> <p>A SPARQL Union query with un-balanced patterns, i.e. a gremlin union traversal can only be generated if the input SPARQL query has the same number of patterns on both the side of the union operator. For instance, the following SPARQL query cannot be mapped, since a union is executed between different number of graph patterns (two patterns <code>union</code> 1 pattern).</p> </li> </ul> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { {?person e:created ?software . ?person v:name "josh" .} UNION {?software v:lang "java" .} }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="ulist"> <ul> <li> <p>A non-Group key variable cannot be projected in a SPARQL query. This is a SPARQL language limitation rather than that of Gremlin/TinkerPop. Apache Jena throws the exception "Non-group key variable in SELECT" if this occurs. For instance, in a SPARQL query with GROUP-BY clause, only the variable on which the grouping is declared, can be projected. The following query is valid:</p> </li> </ul> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name .} GROUP BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>Whereas, the following SPARQL query will be invalid:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?person WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name .} GROUP BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="ulist"> <ul> <li> <p>In a SPARQL query with an ORDER-BY clause, the ordering occurs with respect to the first projected variable in the query. It is possible to choose any number of variable to be projected, however, the first variable in the selection will be the ordering decider. For instance, in the query:</p> </li> </ul> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name . } ORDER BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>the result set will be ordered according to the <code>?name</code> variable (in ascending order by default) despite having passed <code>?age</code> in the order by. Whereas, for the following query:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name . } ORDER BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>the result set will be ordered according to the <code>?age</code> (as it is the first projected variable). Finally, for the select all case (<code>SELECT *</code>):</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name . } ORDER BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> <div class="paragraph"> <p>the the variable encountered first will be the ordering decider, i.e. since we have <code>?person</code> encountered first, the result set will be ordered according to the <code>?person</code> variable (which are vertex id).</p> </div> <div class="ulist"> <ul> <li> <p>In the current implementation, <code>OPTIONAL</code> clause doesn’t work under nesting with <code>UNION</code> clause (i.e. multiple optional clauses with in a union clause) and <code>ORDER-By</code> clause (i.e. declaring ordering over triple patterns within optional clauses). Everything else with SPARQL <code>OPTIONAL</code> works just fine.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="examples">Examples</h3> <div class="paragraph"> <p>The following section presents examples of SPARQL queries that are currently covered by the SPARQL-Gremlin compiler.</p> </div> <div class="sect3"> <h4 id="_select_all">Select All</h4> <div class="paragraph"> <p>Select all vertices in the graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-5" type="radio" name="radio-set-1729797246-5" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-5" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-6" type="radio" name="radio-set-1729797246-5" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"""</span></span>) ==>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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_match_constant_values">Match Constant Values</h4> <div class="paragraph"> <p>Select all vertices with the label <code>person</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-7" type="radio" name="radio-set-1729797246-7" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-7" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-8" type="radio" name="radio-set-1729797246-7" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { ?person v:label "person" .}</span><span class="delimiter">"""</span></span>) ==>v[<span class="integer">1</span>] ==>v[<span class="integer">2</span>] ==>v[<span class="integer">4</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { ?person v:label "person" .}</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_select_specific_elements">Select Specific Elements</h4> <div class="paragraph"> <p>Select the values of the properties <code>name</code> and <code>age</code> for each <code>person</code> vertex.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-9" type="radio" name="radio-set-1729797246-9" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-9" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-10" type="radio" name="radio-set-1729797246-9" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . }</span><span class="delimiter">"""</span></span>) ==>[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_pattern_matching">Pattern Matching</h4> <div class="paragraph"> <p>Select only those persons who created a project.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-11" type="radio" name="radio-set-1729797246-11" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-11" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-12" type="radio" name="radio-set-1729797246-11" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . ?person e:created ?project . }</span><span class="delimiter">"""</span></span>) ==>[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . ?person e:created ?project . }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_filtering">Filtering</h4> <div class="paragraph"> <p>Select only those persons who are older than 30.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-13" type="radio" name="radio-set-1729797246-13" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-13" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-14" type="radio" name="radio-set-1729797246-13" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . FILTER (?age > 30) }</span><span class="delimiter">"""</span></span>) ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?age WHERE { ?person v:label "person" . ?person v:name ?name . ?person v:age ?age . FILTER (?age > 30) }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_deduplication">Deduplication</h4> <div class="paragraph"> <p>Select the distinct names of the created projects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-15" type="radio" name="radio-set-1729797246-15" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-15" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-16" type="radio" name="radio-set-1729797246-15" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT DISTINCT ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person e:created ?project . ?project v:name ?name . FILTER (?age > 30)}</span><span class="delimiter">"""</span></span>) ==>ripple ==>lop</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT DISTINCT ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person e:created ?project . ?project v:name ?name . FILTER (?age > 30)}</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_multiple_filters">Multiple Filters</h4> <div class="paragraph"> <p>Select the distinct names of all Java projects.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-17" type="radio" name="radio-set-1729797246-17" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-17" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-18" type="radio" name="radio-set-1729797246-17" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT DISTINCT ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person e:created ?project . ?project v:name ?name . ?project v:lang ?lang . FILTER (?age > 30 && ?lang = "java") }</span><span class="delimiter">"""</span></span>) ==>ripple ==>lop</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT DISTINCT ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person e:created ?project . ?project v:name ?name . ?project v:lang ?lang . FILTER (?age > 30 && ?lang = "java") }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_union">Union</h4> <div class="paragraph"> <p>Select all persons who have developed a software in java using union.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-19" type="radio" name="radio-set-1729797246-19" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-19" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-20" type="radio" name="radio-set-1729797246-19" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { {?person e:created ?software .} UNION {?software v:lang "java" .} }</span><span class="delimiter">"""</span></span>) ==>[<span class="key">software</span>:v[<span class="integer">3</span>],<span class="key">person</span>:v[<span class="integer">1</span>]] ==>[<span class="key">software</span>:v[<span class="integer">3</span>]] ==>[<span class="key">software</span>:v[<span class="integer">5</span>],<span class="key">person</span>:v[<span class="integer">4</span>]] ==>[<span class="key">software</span>:v[<span class="integer">3</span>],<span class="key">person</span>:v[<span class="integer">4</span>]] ==>[<span class="key">software</span>:v[<span class="integer">5</span>]] ==>[<span class="key">software</span>:v[<span class="integer">3</span>],<span class="key">person</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT * WHERE { {?person e:created ?software .} UNION {?software v:lang "java" .} }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_optional">Optional</h4> <div class="paragraph"> <p>Return the names of the persons who have created a software in java and optionally python.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?person WHERE { ?person v:label "person" . ?person e:created ?software . ?software v:lang "java" . OPTIONAL {?software v:lang "python" . }}</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_order_by">Order By</h4> <div class="paragraph"> <p>Select all vertices with the label <code>person</code> and order them by their age.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-21" type="radio" name="radio-set-1729797246-21" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-21" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-22" type="radio" name="radio-set-1729797246-21" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name . } ORDER BY (?age)</span><span class="delimiter">"""</span></span>) ==>[<span class="key">age</span>:<span class="integer">27</span>,<span class="key">name</span>:vadas] ==>[<span class="key">age</span>:<span class="integer">29</span>,<span class="key">name</span>:marko] ==>[<span class="key">age</span>:<span class="integer">32</span>,<span class="key">name</span>:josh] ==>[<span class="key">age</span>:<span class="integer">35</span>,<span class="key">name</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age ?name WHERE { ?person v:label "person" . ?person v:age ?age . ?person v:name ?name . } ORDER BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_group_by">Group By</h4> <div class="paragraph"> <p>Select all vertices with the label <code>person</code> and group them by their age.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-23" type="radio" name="radio-set-1729797246-23" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-23" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-24" type="radio" name="radio-set-1729797246-23" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age WHERE { ?person v:label "person" . ?person v:age ?age . } GROUP BY (?age)</span><span class="delimiter">"""</span></span>) ==>[<span class="integer">32</span>:[<span class="integer">32</span>],<span class="integer">35</span>:[<span class="integer">35</span>],<span class="integer">27</span>:[<span class="integer">27</span>],<span class="integer">29</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.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age WHERE { ?person v:label "person" . ?person v:age ?age . } GROUP BY (?age)</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_mixedcomplexaggregation_based_queries">Mixed/complex/aggregation-based queries</h4> <div class="paragraph"> <p>Count the number of projects which have been created by persons under the age of 30 and group them by age. Return only the top two.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="groovy">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT (COUNT(?project) as ?p) WHERE { ?person v:label "person" . ?person v:age ?age . FILTER (?age < 30) ?person e:created ?project . } GROUP BY (?age) LIMIT 2</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_meta_property_access">Meta-Property Access</h4> <div class="paragraph"> <p>Accessing the Meta-Property of a graph element. Meta-Property can be perceived as the reified statements in an RDF graph.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-25" type="radio" name="radio-set-1729797246-25" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-25" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-26" type="radio" name="radio-set-1729797246-25" class="tab-selector-2" /> <label for="tab-1729797246-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> g = traversal(SparqlTraversalSource).withEmbedded(graph) ==>sparqltraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">14</span>], standard] gremlin> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?startTime WHERE { ?person v:name "daniel" . ?person p:location ?location . ?location v:value ?name . ?location v:startTime ?startTime }</span><span class="delimiter">"""</span></span>) ==>[<span class="key">name</span>:spremberg,<span class="key">startTime</span>:<span class="integer">1982</span>] ==>[<span class="key">name</span>:kaiserslautern,<span class="key">startTime</span>:<span class="integer">2005</span>] ==>[<span class="key">name</span>:aachen,<span class="key">startTime</span>:<span class="integer">2009</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(SparqlTraversalSource).withEmbedded(graph) g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?name ?startTime WHERE { ?person v:name "daniel" . ?person p:location ?location . ?location v:value ?name . ?location v:startTime ?startTime }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> <div class="sect3"> <h4 id="_star_shaped_queries">STAR-shaped queries</h4> <div class="paragraph"> <p>STAR-shaped queries are the queries that form/follow a star-shaped execution plan. These in terms of graph traversals can be perceived as path queries or neighborhood queries. For instance, getting all the information about a specific <code>person</code> or <code>software</code>.</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-27" type="radio" name="radio-set-1729797246-27" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-27" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-28" type="radio" name="radio-set-1729797246-27" class="tab-selector-2" /> <label for="tab-1729797246-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> g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age ?software ?lang ?name WHERE { ?person v:name "josh" . ?person v:age ?age . ?person e:created ?software . ?software v:lang ?lang . ?software v:name ?name . }</span><span class="delimiter">"""</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">g.sparql(<span class="string"><span class="delimiter">"""</span><span class="content">SELECT ?age ?software ?lang ?name WHERE { ?person v:name "josh" . ?person v:age ?age . ?person e:created ?software . ?software v:lang ?lang . ?software v:name ?name . }</span><span class="delimiter">"""</span></span>)</code></pre> </div> </div> </div> </div> </section> </div> </div> <div class="sect2"> <h3 id="sparql-with-gremlin">With Gremlin</h3> <div class="paragraph"> <p>The <code>sparql()</code>-step takes a SPARQL query and returns a result. That result can be further processed by standard Gremlin steps as shown below:</p> </div> <section class="tabs tabs-2"> <input id="tab-1729797246-29" type="radio" name="radio-set-1729797246-29" class="tab-selector-1" checked="checked" /> <label for="tab-1729797246-29" class="tab-label-1">console (groovy)</label> <input id="tab-1729797246-30" type="radio" name="radio-set-1729797246-29" class="tab-selector-2" /> <label for="tab-1729797246-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> g = traversal(SparqlTraversalSource).withEmbedded(graph) ==>sparqltraversalsource[tinkergraph[<span class="key">vertices</span>:<span class="integer">6</span> <span class="key">edges</span>:<span class="integer">6</span>], standard] gremlin> g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age }</span><span class="delimiter">"</span></span>) ==>[<span class="key">name</span>:marko,<span class="key">age</span>:<span class="integer">29</span>] ==>[<span class="key">name</span>:vadas,<span class="key">age</span>:<span class="integer">27</span>] ==>[<span class="key">name</span>:josh,<span class="key">age</span>:<span class="integer">32</span>] ==>[<span class="key">name</span>:peter,<span class="key">age</span>:<span class="integer">35</span>] gremlin> g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age }</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>) ==>marko ==>vadas ==>josh ==>peter gremlin> g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"</span></span>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>) ==>vadas ==>josh gremlin> g.withSack(<span class="float">1.0f</span>).sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"</span></span>). repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>).inV()). times(<span class="integer">2</span>). sack() ==><span class="float">1.0</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 = traversal(SparqlTraversalSource).withEmbedded(graph) g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age }</span><span class="delimiter">"</span></span>) g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT ?name ?age WHERE { ?person v:name ?name . ?person v:age ?age }</span><span class="delimiter">"</span></span>).select(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>) g.sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"</span></span>).out(<span class="string"><span class="delimiter">"</span><span class="content">knows</span><span class="delimiter">"</span></span>).values(<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>) g.withSack(<span class="float">1.0f</span>).sparql(<span class="string"><span class="delimiter">"</span><span class="content">SELECT * WHERE { }</span><span class="delimiter">"</span></span>). repeat(outE().sack(mult).by(<span class="string"><span class="delimiter">"</span><span class="content">weight</span><span class="delimiter">"</span></span>).inV()). times(<span class="integer">2</span>). sack()</code></pre> </div> </div> </div> </div> </section> <div class="paragraph"> <p>Mixing SPARQL with Gremlin steps introduces some interesting possibilities for complex traversals.</p> </div> </div> </div> </div> <h1 id="conclusion" class="sect0">Conclusion</h1> <div class="openblock partintro"> <div class="content"> <span class="image left"><img src="../images/tinkerpop-character.png" alt="tinkerpop character" width="100"></span> The world that we know, you and me, is but a subset of the world that Gremlin has weaved within The TinkerPop. Gremlin has constructed a fully connected graph and only the subset that makes logical sense to our traversing thoughts is the fragment we have come to know and have come to see one another within. But there are many more out there, within other webs of logics unfathomed. From any thought, every other thought, we come to realize that which is — The TinkerPop. </div> </div> <h1 id="acknowledgements" class="sect0">Acknowledgements</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p><span class="image left"><img src="../images/yourkit-logo.png" alt="yourkit logo" width="200"></span> YourKit supports the TinkerPop open source project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. YourKit’s leading software products: <a href="http://www.yourkit.com/java/profiler/index.jsp">YourKit Java Profiler</a> and <a href="http://www.yourkit.com/.net/profiler/index.jsp">YourKit .NET Profiler</a></p> </div> <div class="paragraph"> <p><span class="image right"><img src="../images/ketrina-tinkerpop3.png" alt="ketrina tinkerpop3" width="150"></span> <a href="http://ketrinayim.tumblr.com">Ketrina Yim</a> — Designing Gremlin and his friends for TinkerPop was one of my first major projects as a freelancer, and it’s delightful to see them on the Web and all over the documentation! Drawing and tweaking the characters over time is like watching them grow up. They’ve gone from sketches on paper to full-color logos, and from logos to living characters that cheerfully greet visitors to the TinkerPop website. And it’s been a great time all throughout!</p> </div> <div class="paragraph"> <p>…​in the beginning.</p> </div> </div> </div> </div> <div id="footer"> <div id="footer-text"> Last updated 2024-10-24 12:31:42 -0700 </div> </div> </body> </html>