diff --git a/sqlg-doc/docs/2.1.4/.gitignore b/sqlg-doc/docs/2.1.4/.gitignore new file mode 100644 index 000000000..999ea03b0 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/.gitignore @@ -0,0 +1 @@ +apidocs/ diff --git a/sqlg-doc/docs/2.1.4/asciidoctor-default.css b/sqlg-doc/docs/2.1.4/asciidoctor-default.css new file mode 100644 index 000000000..4f0ba722c --- /dev/null +++ b/sqlg-doc/docs/2.1.4/asciidoctor-default.css @@ -0,0 +1,2119 @@ +/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ +/* Uncomment @import statement to use as custom stylesheet */ +/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block +} + +audio, video { + display: inline-block +} + +audio:not([controls]) { + display: none; + height: 0 +} + +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +a { + background: none +} + +a:focus { + outline: thin dotted +} + +a:active, a:hover { + outline: 0 +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, strong { + font-weight: bold +} + +dfn { + font-style: italic +} + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +mark { + background: #ff0; + color: #000 +} + +code, kbd, pre, samp { + font-family: monospace; + font-size: 1em +} + +pre { + white-space: pre-wrap +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019" +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 0 +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: .35em .625em .75em +} + +legend { + border: 0; + padding: 0 +} + +button, input, select, textarea { + font-family: inherit; + font-size: 100%; + margin: 0 +} + +button, input { + line-height: normal +} + +button, select { + text-transform: none +} + +button, html input[type="button"], input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +input[type="checkbox"], input[type="radio"] { + box-sizing: border-box; + padding: 0 +} + +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0 +} + +textarea { + overflow: auto; + vertical-align: top +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +*, *::before, *::after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +html, body { + font-size: 100% +} + +body { + background: #fff; + color: rgba(0, 0, 0, .8); + padding: 0; + margin: 0; + font-family: "Noto Serif", "DejaVu Serif", serif; + font-weight: 400; + font-style: normal; + line-height: 1; + position: relative; + cursor: auto; + tab-size: 4; + word-wrap: anywhere; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased +} + +a:hover { + cursor: pointer +} + +img, object, embed { + max-width: 100%; + height: auto +} + +object, embed { + height: 100% +} + +img { + -ms-interpolation-mode: bicubic +} + +.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 +} + +img, object, svg { + display: inline-block; + vertical-align: middle +} + +textarea { + height: auto; + min-height: 50px +} + +select { + width: 100% +} + +.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { + line-height: 1.45; + color: #7a2518; + font-weight: 400; + margin-top: 0; + margin-bottom: .25em +} + +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 +} + +a { + color: #2156a5; + text-decoration: underline; + line-height: inherit +} + +a:hover, a:focus { + color: #1d4b8f +} + +a img { + border: 0 +} + +p { + font-family: inherit; + font-weight: 400; + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + text-rendering: optimizeLegibility +} + +p aside { + font-size: .875em; + line-height: 1.35; + font-style: italic +} + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-weight: 300; + font-style: normal; + color: #ba3925; + text-rendering: optimizeLegibility; + margin-top: 1em; + margin-bottom: .5em; + line-height: 1.0125em +} + +h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { + font-size: 60%; + color: #e99b8f; + line-height: 0 +} + +h1 { + font-size: 2.125em +} + +h2 { + font-size: 1.6875em +} + +h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.375em +} + +h4, h5 { + font-size: 1.125em +} + +h6 { + font-size: 1em +} + +hr { + border: solid #dddddf; + border-width: 1px 0 0; + clear: both; + margin: 1.25em 0 1.1875em; + height: 0 +} + +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: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + font-weight: 400; + color: rgba(0, 0, 0, .9) +} + +ul, ol, dl { + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + list-style-position: outside; + font-family: inherit +} + +ul, ol { + margin-left: 1.5em +} + +ul li ul, ul li ol { + margin-left: 1.25em; + margin-bottom: 0; + font-size: 1em +} + +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 +} + +ol li ul, ol li ol { + margin-left: 1.25em; + margin-bottom: 0 +} + +dl dt { + margin-bottom: .3125em; + font-weight: bold +} + +dl dd { + margin-bottom: 1.25em +} + +abbr, acronym { + text-transform: uppercase; + font-size: 90%; + color: rgba(0, 0, 0, .8); + border-bottom: 1px dotted #ddd; + cursor: help +} + +abbr { + text-transform: none +} + +blockquote { + margin: 0 0 1.25em; + padding: .5625em 1.25em 0 1.1875em; + border-left: 1px solid #ddd +} + +blockquote, blockquote p { + line-height: 1.6; + color: rgba(0, 0, 0, .85) +} + +@media screen and (min-width: 768px) { + h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + line-height: 1.2 + } + + h1 { + font-size: 2.75em + } + + h2 { + font-size: 2.3125em + } + + h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.6875em + } + + h4 { + font-size: 1.4375em + } +} + +table { + background: #fff; + margin-bottom: 1.25em; + border: solid 1px #dedede; + word-wrap: normal +} + +table thead, table tfoot { + background: #f7f8f7 +} + +table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { + padding: .5em .625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8); + text-align: left +} + +table tr th, table tr td { + padding: .5625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8) +} + +table tr.even, table tr.alt { + background: #f8f8f7 +} + +table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { + line-height: 1.6 +} + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + line-height: 1.2; + word-spacing: -.05em +} + +h1 strong, h2 strong, h3 strong, #toctitle strong, .sidebarblock > .content > .title strong, h4 strong, h5 strong, h6 strong { + font-weight: 400 +} + +.center { + margin-left: auto; + margin-right: auto +} + +.stretch { + width: 100% +} + +.clearfix::before, .clearfix::after, .float-group::before, .float-group::after { + content: " "; + display: table +} + +.clearfix::after, .float-group::after { + clear: both +} + +:not(pre).nobreak { + word-wrap: normal +} + +:not(pre).nowrap { + white-space: nowrap +} + +:not(pre).pre-wrap { + white-space: pre-wrap +} + +:not(pre):not([class^=L]) > code { + font-size: .9375em; + font-style: normal !important; + letter-spacing: 0; + padding: .1em .5ex; + word-spacing: -.15em; + background: #f7f7f8; + -webkit-border-radius: 4px; + border-radius: 4px; + line-height: 1.45; + text-rendering: optimizeSpeed +} + +pre { + color: rgba(0, 0, 0, .9); + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + line-height: 1.45; + text-rendering: optimizeSpeed +} + +pre code, pre pre { + color: inherit; + font-size: inherit; + line-height: inherit +} + +pre > code { + display: block +} + +pre.nowrap, pre.nowrap pre { + white-space: pre; + word-wrap: normal +} + +em em { + font-style: normal +} + +strong strong { + font-weight: 400 +} + +.keyseq { + color: rgba(51, 51, 51, .8) +} + +kbd { + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + display: inline-block; + color: rgba(0, 0, 0, .8); + font-size: .65em; + line-height: 1.45; + background: #f7f7f7; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset; + box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset; + margin: 0 .15em; + padding: .2em .5em; + vertical-align: middle; + position: relative; + top: -.1em; + white-space: nowrap +} + +.keyseq kbd:first-child { + margin-left: 0 +} + +.keyseq kbd:last-child { + margin-right: 0 +} + +.menuseq, .menuref { + color: #000 +} + +.menuseq b:not(.caret), .menuref { + font-weight: inherit +} + +.menuseq { + word-spacing: -.02em +} + +.menuseq b.caret { + font-size: 1.25em; + line-height: .8 +} + +.menuseq i.caret { + font-weight: bold; + text-align: center; + width: .45em +} + +b.button::before, b.button::after { + position: relative; + top: -1px; + font-weight: 400 +} + +b.button::before { + content: "["; + padding: 0 3px 0 2px +} + +b.button::after { + content: "]"; + padding: 0 2px 0 3px +} + +p a > code:hover { + color: rgba(0, 0, 0, .9) +} + +#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: .9375em; + padding-right: .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 +} + +#content { + margin-top: 1.25em +} + +#content::before { + content: none +} + +#header > h1:first-child { + color: rgba(0, 0, 0, .85); + margin-top: 2.25rem; + margin-bottom: 0 +} + +#header > h1:first-child + #toc { + margin-top: 8px; + border-top: 1px solid #dddddf +} + +#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { + border-bottom: 1px solid #dddddf; + padding-bottom: 8px +} + +#header .details { + border-bottom: 1px solid #dddddf; + line-height: 1.45; + padding-top: .25em; + padding-bottom: .25em; + padding-left: .25em; + color: rgba(0, 0, 0, .6); + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -ms-flex-flow: row wrap; + -webkit-flex-flow: row wrap; + flex-flow: row wrap +} + +#header .details span:first-child { + margin-left: -.125em +} + +#header .details span.email a { + color: rgba(0, 0, 0, .85) +} + +#header .details br { + display: none +} + +#header .details br + span::before { + content: "\00a0\2013\00a0" +} + +#header .details br + span.author::before { + content: "\00a0\22c5\00a0"; + color: rgba(0, 0, 0, .85) +} + +#header .details br + span#revremark::before { + content: "\00a0|\00a0" +} + +#header #revnumber { + text-transform: capitalize +} + +#header #revnumber::after { + content: "\00a0" +} + +#content > h1:first-child:not([class]) { + color: rgba(0, 0, 0, .85); + border-bottom: 1px solid #dddddf; + padding-bottom: 8px; + margin-top: 0; + padding-top: 1rem; + margin-bottom: 1.25rem +} + +#toc { + border-bottom: 1px solid #e7e7e9; + padding-bottom: .5em +} + +#toc > ul { + margin-left: .125em +} + +#toc ul.sectlevel0 > li > a { + font-style: italic +} + +#toc ul.sectlevel0 ul.sectlevel1 { + margin: .5em 0 +} + +#toc ul { + font-family: "Open Sans", "DejaVu Sans", sans-serif; + list-style-type: none +} + +#toc li { + line-height: 1.3334; + margin-top: .3334em; + list-style: none; +} + +#toc a { + text-decoration: none +} + +#toc a:active { + text-decoration: underline +} + +#toctitle { + color: #7a2518; + font-size: 1.2em +} + +@media screen and (min-width: 768px) { + #toctitle { + font-size: 1.375em + } + + body.toc2 { + padding-left: 15em; + padding-right: 0 + } + + #toc.toc2 { + margin-top: 0 !important; + background: #f8f8f7; + position: fixed; + width: 15em; + left: 0; + top: 0; + border-right: 1px solid #e7e7e9; + border-top-width: 0 !important; + border-bottom-width: 0 !important; + z-index: 1000; + padding: 1.25em 1em; + height: 100%; + overflow: auto + } + + #toc.toc2 #toctitle { + margin-top: 0; + margin-bottom: .8rem; + font-size: 1.2em + } + + #toc.toc2 > ul { + font-size: .9em; + margin-bottom: 0 + } + + #toc.toc2 ul ul { + margin-left: 0; + padding-left: 1em + } + + #toc.toc2 ul.sectlevel0 ul.sectlevel1 { + padding-left: 0; + margin-top: .5em; + margin-bottom: .5em + } + + body.toc2.toc-right { + padding-left: 0; + padding-right: 15em + } + + body.toc2.toc-right #toc.toc2 { + border-right-width: 0; + border-left: 1px solid #e7e7e9; + left: auto; + right: 0 + } +} + +@media 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: .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: #e0e0dc; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f8f8f7; + -webkit-border-radius: 4px; + border-radius: 4px +} + +#content #toc > :first-child { + margin-top: 0 +} + +#content #toc > :last-child { + margin-bottom: 0 +} + +#footer { + max-width: none; + background: rgba(0, 0, 0, .8); + padding: 1.25em +} + +#footer-text { + color: rgba(255, 255, 255, .8); + line-height: 1.44 +} + +#content { + margin-bottom: .625em +} + +.sect1 { + padding-bottom: .625em +} + +@media screen and (min-width: 768px) { + #content { + margin-bottom: 1.25em + } + + .sect1 { + padding-bottom: 1.25em + } +} + +.sect1:last-child { + padding-bottom: 0 +} + +.sect1 + .sect1 { + border-top: 1px solid #e7e7e9 +} + +#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; + z-index: 1001; + width: 1.5ex; + margin-left: -1.5ex; + display: block; + text-decoration: none !important; + visibility: hidden; + text-align: center; + font-weight: 400 +} + +#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; + display: block; + padding-top: .1em +} + +#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: #ba3925; + 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: #a53221 +} + +details, .audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { + margin-bottom: 1.25em +} + +details > summary:first-of-type { + cursor: pointer; + display: list-item; + outline: none; + margin-bottom: .75em +} + +.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { + text-rendering: optimizeLegibility; + text-align: left; + font-family: "Noto Serif", "DejaVu Serif", serif; + font-size: 1rem; + font-style: italic +} + +table.tableblock.fit-content > caption.title { + white-space: nowrap; + width: 0 +} + +.paragraph.lead > p, #preamble > .sectionbody > [class="paragraph"]:first-of-type p { + font-size: 1.21875em; + line-height: 1.6; + color: rgba(0, 0, 0, .85) +} + +table.tableblock #preamble > .sectionbody > [class="paragraph"]:first-of-type p { + font-size: inherit +} + +.admonitionblock > table { + border-collapse: separate; + 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; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + text-transform: uppercase +} + +.admonitionblock > table td.content { + padding-left: 1.125em; + padding-right: 1.25em; + border-left: 1px solid #dddddf; + color: rgba(0, 0, 0, .6); + word-wrap: anywhere +} + +.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: #fff; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.exampleblock > .content > :first-child { + margin-top: 0 +} + +.exampleblock > .content > :last-child { + margin-bottom: 0 +} + +.sidebarblock { + border-style: solid; + border-width: 1px; + border-color: #dbdbd6; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f3f3f2; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.sidebarblock > :first-child { + margin-top: 0 +} + +.sidebarblock > :last-child { + margin-bottom: 0 +} + +.sidebarblock > .content > .title { + color: #7a2518; + margin-top: 0; + text-align: center +} + +.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, .listingblock > .content > pre { + -webkit-border-radius: 4px; + border-radius: 4px; + overflow-x: auto; + padding: 1em; + font-size: .8125em +} + +@media screen and (min-width: 768px) { + .literalblock pre, .listingblock > .content > pre { + font-size: .90625em + } +} + +@media screen and (min-width: 1280px) { + .literalblock pre, .listingblock > .content > pre { + font-size: 1em + } +} + +.literalblock pre, .listingblock > .content > pre:not(.highlight), .listingblock > .content > pre[class="highlight"], .listingblock > .content > pre[class^="highlight "] { + background: #f7f7f8 +} + +.literalblock.output pre { + color: #f7f7f8; + background: rgba(0, 0, 0, .9) +} + +.listingblock > .content { + position: relative +} + +.listingblock code[data-lang]::before { + display: none; + content: attr(data-lang); + position: absolute; + font-size: .75em; + top: .425rem; + right: .5rem; + line-height: 1; + text-transform: uppercase; + color: inherit; + opacity: .5 +} + +.listingblock:hover code[data-lang]::before { + display: block +} + +.listingblock.terminal pre .command::before { + content: attr(data-prompt); + padding-right: .5em; + color: inherit; + opacity: .5 +} + +.listingblock.terminal pre .command:not([data-prompt])::before { + content: "$" +} + +.listingblock pre.highlightjs { + padding: 0 +} + +.listingblock pre.highlightjs > code { + padding: 1em; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.listingblock pre.prettyprint { + border-width: 0 +} + +.prettyprint { + background: #f7f7f8 +} + +pre.prettyprint .linenums { + line-height: 1.45; + margin-left: 2em +} + +pre.prettyprint li { + background: none; + list-style-type: inherit; + padding-left: 0 +} + +pre.prettyprint li code[data-lang]::before { + opacity: 1 +} + +pre.prettyprint li:not(:first-child) code[data-lang]::before { + display: none +} + +table.linenotable { + border-collapse: separate; + border: 0; + margin-bottom: 0; + background: none +} + +table.linenotable td[class] { + color: inherit; + vertical-align: top; + padding: 0; + line-height: inherit; + white-space: normal +} + +table.linenotable td.code { + padding-left: .75em +} + +table.linenotable td.linenos { + border-right: 1px solid currentColor; + opacity: .35; + padding-right: .5em +} + +pre.pygments .lineno { + border-right: 1px solid currentColor; + opacity: .35; + display: inline-block; + margin-right: .75em +} + +pre.pygments .lineno::before { + content: ""; + margin-right: -.125em +} + +.quoteblock { + margin: 0 1em 1.25em 1.5em; + display: table +} + +.quoteblock:not(.excerpt) > .title { + margin-left: -1.5em; + margin-bottom: .75em +} + +.quoteblock blockquote, .quoteblock p { + color: rgba(0, 0, 0, .85); + font-size: 1.15rem; + line-height: 1.75; + word-spacing: .1em; + letter-spacing: 0; + font-style: italic; + text-align: justify +} + +.quoteblock blockquote { + margin: 0; + padding: 0; + border: 0 +} + +.quoteblock blockquote::before { + content: "\201c"; + float: left; + font-size: 2.75em; + font-weight: bold; + line-height: .6em; + margin-left: -.6em; + color: #7a2518; + text-shadow: 0 1px 2px rgba(0, 0, 0, .1) +} + +.quoteblock blockquote > .paragraph:last-child p { + margin-bottom: 0 +} + +.quoteblock .attribution { + margin-top: .75em; + margin-right: .5ex; + text-align: right +} + +.verseblock { + margin: 0 1em 1.25em +} + +.verseblock pre { + font-family: "Open Sans", "DejaVu Sans", sans; + font-size: 1.15rem; + color: rgba(0, 0, 0, .85); + font-weight: 300; + text-rendering: optimizeLegibility +} + +.verseblock pre strong { + font-weight: 400 +} + +.verseblock .attribution { + margin-top: 1.25rem; + margin-left: .5ex +} + +.quoteblock .attribution, .verseblock .attribution { + font-size: .9375em; + line-height: 1.45; + font-style: italic +} + +.quoteblock .attribution br, .verseblock .attribution br { + display: none +} + +.quoteblock .attribution cite, .verseblock .attribution cite { + display: block; + letter-spacing: -.025em; + color: rgba(0, 0, 0, .6) +} + +.quoteblock.abstract blockquote::before, .quoteblock.excerpt blockquote::before, .quoteblock .quoteblock blockquote::before { + display: none +} + +.quoteblock.abstract blockquote, .quoteblock.abstract p, .quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p { + line-height: 1.6; + word-spacing: 0 +} + +.quoteblock.abstract { + margin: 0 1em 1.25em; + display: block +} + +.quoteblock.abstract > .title { + margin: 0 0 .375em; + font-size: 1.15em; + text-align: center +} + +.quoteblock.excerpt > blockquote, .quoteblock .quoteblock { + padding: 0 0 .25em 1em; + border-left: .25em solid #dddddf +} + +.quoteblock.excerpt, .quoteblock .quoteblock { + margin-left: 0 +} + +.quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p { + color: inherit; + font-size: 1.0625rem +} + +.quoteblock.excerpt .attribution, .quoteblock .quoteblock .attribution { + color: inherit; + font-size: .85rem; + text-align: left; + margin-right: 0 +} + +p.tableblock:last-child { + margin-bottom: 0 +} + +td.tableblock > .content { + margin-bottom: 1.25em; + word-wrap: anywhere +} + +td.tableblock > .content > :last-child { + margin-bottom: -1.25em +} + +table.tableblock, th.tableblock, td.tableblock { + border: 0 solid #dedede +} + +table.grid-all > * > tr > * { + border-width: 1px +} + +table.grid-cols > * > tr > * { + border-width: 0 1px +} + +table.grid-rows > * > tr > * { + border-width: 1px 0 +} + +table.frame-all { + border-width: 1px +} + +table.frame-ends { + border-width: 1px 0 +} + +table.frame-sides { + border-width: 0 1px +} + +table.frame-none > colgroup + * > :first-child > *, table.frame-sides > colgroup + * > :first-child > * { + border-top-width: 0 +} + +table.frame-none > :last-child > :last-child > *, table.frame-sides > :last-child > :last-child > * { + border-bottom-width: 0 +} + +table.frame-none > * > tr > :first-child, table.frame-ends > * > tr > :first-child { + border-left-width: 0 +} + +table.frame-none > * > tr > :last-child, table.frame-ends > * > tr > :last-child { + border-right-width: 0 +} + +table.stripes-all tr, table.stripes-odd tr:nth-of-type(odd), table.stripes-even tr:nth-of-type(even), table.stripes-hover tr:hover { + background: #f8f8f7 +} + +th.halign-left, td.halign-left { + text-align: left +} + +th.halign-right, td.halign-right { + text-align: right +} + +th.halign-center, td.halign-center { + text-align: center +} + +th.valign-top, td.valign-top { + vertical-align: top +} + +th.valign-bottom, td.valign-bottom { + vertical-align: bottom +} + +th.valign-middle, td.valign-middle { + vertical-align: middle +} + +table thead th, table tfoot th { + font-weight: bold +} + +tbody tr th { + background: #f7f8f7 +} + +tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { + color: rgba(0, 0, 0, .8); + font-weight: bold +} + +p.tableblock > code:only-child { + background: none; + padding: 0 +} + +p.tableblock { + font-size: 1em +} + +ol { + margin-left: 1.75em +} + +ul li ol { + margin-left: 1.5em +} + +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: .625em +} + +ul.checklist, ul.none, ol.none, ul.no-bullet, ol.no-bullet, ol.unnumbered, ul.unstyled, ol.unstyled { + list-style-type: none +} + +ul.no-bullet, ol.no-bullet, ol.unnumbered { + margin-left: .625em +} + +ul.unstyled, ol.unstyled { + margin-left: 0 +} + +ul.checklist { + margin-left: .625em +} + +ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child { + width: 1.25em; + font-size: .8em; + position: relative; + bottom: .125em +} + +ul.checklist li > p:first-child > input[type="checkbox"]:first-child { + margin-right: .25em +} + +ul.inline { + display: -ms-flexbox; + display: -webkit-box; + display: flex; + -ms-flex-flow: row wrap; + -webkit-flex-flow: row wrap; + flex-flow: row wrap; + list-style: none; + margin: 0 0 .625em -1.25em +} + +ul.inline > li { + margin-left: 1.25em +} + +.unstyled dl dt { + font-weight: 400; + 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, td.hdlist2 { + vertical-align: top; + padding: 0 .625em +} + +td.hdlist1 { + font-weight: bold; + padding-bottom: 1.25em +} + +td.hdlist2 { + word-wrap: anywhere +} + +.literalblock + .colist, .listingblock + .colist { + margin-top: -.5em +} + +.colist td:not([class]):first-child { + padding: .4em .75em 0; + line-height: 1; + vertical-align: top +} + +.colist td:not([class]):first-child img { + max-width: none +} + +.colist td:not([class]):last-child { + padding: .25em 0 +} + +.thumb, .th { + line-height: 0; + display: inline-block; + border: solid 4px #fff; + -webkit-box-shadow: 0 0 0 1px #ddd; + box-shadow: 0 0 0 1px #ddd +} + +.imageblock.left { + margin: .25em .625em 1.25em 0 +} + +.imageblock.right { + margin: .25em 0 1.25em .625em +} + +.imageblock > .title { + margin-bottom: 0 +} + +.imageblock.thumb, .imageblock.th { + border-width: 6px +} + +.imageblock.thumb > .title, .imageblock.th > .title { + padding: 0 .125em +} + +.image.left, .image.right { + margin-top: .25em; + margin-bottom: .25em; + display: inline-block; + line-height: 0 +} + +.image.left { + margin-right: .625em +} + +.image.right { + margin-left: .625em +} + +a.image { + text-decoration: none; + display: inline-block +} + +a.image object { + pointer-events: none +} + +sup.footnote, sup.footnoteref { + font-size: .875em; + position: static; + vertical-align: super +} + +sup.footnote a, sup.footnoteref a { + text-decoration: none +} + +sup.footnote a:active, sup.footnoteref a:active { + text-decoration: underline +} + +#footnotes { + padding-top: .75em; + padding-bottom: .75em; + margin-bottom: .625em +} + +#footnotes hr { + width: 20%; + min-width: 6.25em; + margin: -.25em 0 .75em; + border-width: 1px 0 0 +} + +#footnotes .footnote { + padding: 0 .375em 0 .225em; + line-height: 1.3334; + font-size: .875em; + margin-left: 1.2em; + margin-bottom: .2em +} + +#footnotes .footnote a:first-of-type { + font-weight: bold; + text-decoration: none; + margin-left: -1.05em +} + +#footnotes .footnote:last-of-type { + margin-bottom: 0 +} + +#content #footnotes { + margin-top: -.625em; + margin-bottom: 0; + padding: .75em 0 +} + +.gist .file-data > table { + border: 0; + 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: #00fafa +} + +.black { + color: #000 +} + +.black-background { + background: #000 +} + +.blue { + color: #0000bf +} + +.blue-background { + background: #0000fa +} + +.fuchsia { + color: #bf00bf +} + +.fuchsia-background { + background: #fa00fa +} + +.gray { + color: #606060 +} + +.gray-background { + background: #7d7d7d +} + +.green { + color: #006000 +} + +.green-background { + background: #007d00 +} + +.lime { + color: #00bf00 +} + +.lime-background { + background: #00fa00 +} + +.maroon { + color: #600000 +} + +.maroon-background { + background: #7d0000 +} + +.navy { + color: #000060 +} + +.navy-background { + background: #00007d +} + +.olive { + color: #606000 +} + +.olive-background { + background: #7d7d00 +} + +.purple { + color: #600060 +} + +.purple-background { + background: #7d007d +} + +.red { + color: #bf0000 +} + +.red-background { + background: #fa0000 +} + +.silver { + color: #909090 +} + +.silver-background { + background: #bcbcbc +} + +.teal { + color: #006060 +} + +.teal-background { + background: #007d7d +} + +.white { + color: #bfbfbf +} + +.white-background { + background: #fafafa +} + +.yellow { + color: #bfbf00 +} + +.yellow-background { + background: #fafa00 +} + +span.icon > .fa { + cursor: default +} + +a span.icon > .fa { + cursor: inherit +} + +.admonitionblock td.icon [class^="fa icon-"] { + font-size: 2.5em; + text-shadow: 1px 1px 2px rgba(0, 0, 0, .5); + cursor: default +} + +.admonitionblock td.icon .icon-note::before { + content: "\f05a"; + color: #19407c +} + +.admonitionblock td.icon .icon-tip::before { + content: "\f0eb"; + text-shadow: 1px 1px 2px rgba(155, 155, 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[data-value] { + display: inline-block; + color: #fff !important; + background: rgba(0, 0, 0, .8); + -webkit-border-radius: 50%; + border-radius: 50%; + text-align: center; + font-size: .75em; + width: 1.67em; + height: 1.67em; + line-height: 1.67em; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-style: normal; + font-weight: bold +} + +.conum[data-value] * { + color: #fff !important +} + +.conum[data-value] + b { + display: none +} + +.conum[data-value]::after { + content: attr(data-value) +} + +pre .conum[data-value] { + position: relative; + top: -.125em +} + +b.conum * { + color: inherit !important +} + +.conum:not([data-value]):empty { + display: none +} + +dt, th.tableblock, td.content, div.footnote { + text-rendering: optimizeLegibility +} + +h1, h2, p, td.content, span.alt { + letter-spacing: -.01em +} + +p strong, td.content strong, div.footnote strong { + letter-spacing: -.005em +} + +p, blockquote, dt, td.content, span.alt { + font-size: 1.0625rem +} + +p { + margin-bottom: 1.25rem +} + +.sidebarblock p, .sidebarblock dt, .sidebarblock td.content, p.tableblock { + font-size: 1em +} + +.exampleblock > .content { + background: #fffef7; + border-color: #e0e0dc; + -webkit-box-shadow: 0 1px 4px #e0e0dc; + box-shadow: 0 1px 4px #e0e0dc +} + +.print-only { + display: none !important +} + +@page { + margin: 1.25cm .75cm +} + +@media print { + * { + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important + } + + html { + font-size: 80% + } + + a { + color: inherit !important; + text-decoration: underline !important + } + + a.bare, a[href^="#"], a[href^="mailto:"] { + text-decoration: none !important + } + + a[href^="http:"]:not(.bare)::after, a[href^="https:"]:not(.bare)::after { + content: "(" attr(href) ")"; + display: inline-block; + font-size: .875em; + padding-left: .25em + } + + abbr[title]::after { + content: " (" attr(title) ")" + } + + pre, blockquote, tr, img, object, svg { + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + svg { + max-width: 100% + } + + p, blockquote, dt, td.content { + font-size: 1em; + orphans: 3; + widows: 3 + } + + h2, h3, #toctitle, .sidebarblock > .content > .title { + page-break-after: avoid + } + + #header, #content, #footnotes, #footer { + max-width: none + } + + #toc, .sidebarblock, .exampleblock > .content { + background: none !important + } + + #toc { + border-bottom: 1px solid #dddddf !important; + padding-bottom: 0 !important + } + + body.book #header { + text-align: center + } + + body.book #header > h1:first-child { + border: 0 !important; + margin: 2.5em 0 1em + } + + body.book #header .details { + border: 0 !important; + display: block; + padding: 0 !important + } + + body.book #header .details span:first-child { + margin-left: 0 !important + } + + body.book #header .details br { + display: block + } + + body.book #header .details br + span::before { + content: none !important + } + + body.book #toc { + border: 0 !important; + text-align: left !important; + padding: 0 !important; + margin: 0 !important + } + + body.book #toc, body.book #preamble, body.book h1.sect0, body.book .sect1 > h2 { + page-break-before: always + } + + .listingblock code[data-lang]::before { + display: block + } + + #footer { + padding: 0 .9375em + } + + .hide-on-print { + display: none !important + } + + .print-only { + display: block !important + } + + .hide-for-print { + display: none !important + } + + .show-for-print { + display: inherit !important + } +} + +@media print, amzn-kf8 { + #header > h1:first-child { + margin-top: 1.25rem + } + + .sect1 { + padding: 0 !important + } + + .sect1 + .sect1 { + border: 0 + } + + #footer { + background: none + } + + #footer-text { + color: rgba(0, 0, 0, .6); + font-size: .9em + } +} + +@media amzn-kf8 { + #header, #content, #footnotes, #footer { + padding: 0 + } +} \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/flask.css b/sqlg-doc/docs/2.1.4/flask.css new file mode 100644 index 000000000..03abe3b46 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/flask.css @@ -0,0 +1,597 @@ +/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ + +/* Default font. */ +body { + font-family: Georgia,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Arial,Helvetica,sans-serif; +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} +h5 { + font-size: 1.0em; +} + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} +ul > li { color: #aaa; } +ul > li > * { color: black; } + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #527bbd; + font-weight: bold; + font-size: 1.1em; +} +#email { +} +#revnumber, #revdate, #revremark { +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +#footer-text { + float: left; + padding-bottom: 0.5em; +} +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + color: #606060; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid #dddddd; + border-left: 4px solid #f0f0f0; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid #dddddd; + border-left: 5px solid #f0f0f0; + background: #f8f8f8; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #f0f0f0; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > pre.content { + font-family: inherit; + font-size: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +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; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + +div.colist td { + padding-right: 0.5em; + padding-bottom: 0.3em; + vertical-align: top; +} +div.colist td img { + margin-top: 0.3em; +} + +@media print { + #footer-badges { display: none; } +} + +#toc { + margin-bottom: 2.5em; +} + +#toctitle { + color: #527bbd; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} + +span.aqua { color: aqua; } +span.black { color: black; } +span.blue { color: blue; } +span.fuchsia { color: fuchsia; } +span.gray { color: gray; } +span.green { color: green; } +span.lime { color: lime; } +span.maroon { color: maroon; } +span.navy { color: navy; } +span.olive { color: olive; } +span.purple { color: purple; } +span.red { color: red; } +span.silver { color: silver; } +span.teal { color: teal; } +span.white { color: white; } +span.yellow { color: yellow; } + +span.aqua-background { background: aqua; } +span.black-background { background: black; } +span.blue-background { background: blue; } +span.fuchsia-background { background: fuchsia; } +span.gray-background { background: gray; } +span.green-background { background: green; } +span.lime-background { background: lime; } +span.maroon-background { background: maroon; } +span.navy-background { background: navy; } +span.olive-background { background: olive; } +span.purple-background { background: purple; } +span.red-background { background: red; } +span.silver-background { background: silver; } +span.teal-background { background: teal; } +span.white-background { background: white; } +span.yellow-background { background: yellow; } + +span.big { font-size: 2em; } +span.small { font-size: 0.6em; } + +span.underline { text-decoration: underline; } +span.overline { text-decoration: overline; } +span.line-through { text-decoration: line-through; } + + +/* + * xhtml11 specific + * + * */ + +tt { + font-family: monospace; + font-size: inherit; + color: navy; +} + +div.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.tableblock > table { + border: 3px solid #527bbd; +} +thead, p.table.header { + font-weight: bold; + color: #527bbd; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +/* + * html5 specific + * + * */ + +.monospaced { + font-family: monospace; + font-size: inherit; + color: navy; +} + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #527bbd; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #527bbd; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #527bbd; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + +/* + * manpage specific + * + * */ + +body.manpage h1 { + padding-top: 0.5em; + padding-bottom: 0.5em; + border-top: 2px solid silver; + border-bottom: 2px solid silver; +} +body.manpage h2 { + border-style: none; +} +body.manpage div.sectionbody { + margin-left: 3em; +} + +@media print { + body.manpage div#toc { display: none; } +} + + +/* + * Theme specific overrides of the preceding (asciidoc.css) CSS. + * + */ +body { + font-family: Garamond, Georgia, serif; + font-size: 17px; + color: #3E4349; + line-height: 1.3em; +} +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Garmond, Georgia, serif; + font-weight: normal; + border-bottom-width: 0; + color: #3E4349; +} +div.title, caption.title { color: #596673; font-weight: bold; } +h1 { font-size: 240%; } +h2 { font-size: 180%; } +h3 { font-size: 150%; } +h4 { font-size: 130%; } +h5 { font-size: 115%; } +h6 { font-size: 100%; } +#header h1 { margin-top: 0; } +#toc { + color: #444444; + line-height: 1.5; + padding-top: 1.5em; +} +#toctitle { + font-size: 20px; +} +#toc a { + border-bottom: 1px dotted #999999; + color: #444444 !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +div.toclevel1 { margin-top: 0.2em; font-size: 16px; } +div.toclevel2 { margin-top: 0.15em; font-size: 14px; } +em, dt, td.hdlist1 { color: black; } +strong { color: #3E4349; } +a { color: #004B6B; text-decoration: none; border-bottom: 1px dotted #004B6B; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } +div.tableblock > table, table.tableblock { border: 3px solid #E8E8E8; } +th.tableblock, td.tableblock { border: 1px solid #E8E8E8; } +ul > li > * { color: #3E4349; } +pre, tt, .monospaced { font-family: Consolas,Menlo,'Deja Vu Sans Mono','Bitstream Vera Sans Mono',monospace; } +tt, .monospaced { font-size: 0.9em; color: black; +} +div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock > div.content { border-width: 0 0 0 3px; border-color: #E8E8E8; } +div.verseblock { border-left-width: 0; margin-left: 3em; } +div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;} +div.admonitionblock td.content { border-left: 3px solid #E8E8E8; } diff --git a/sqlg-doc/docs/2.1.4/img/bullet.png b/sqlg-doc/docs/2.1.4/img/bullet.png new file mode 100644 index 000000000..22ea54375 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/bullet.png differ diff --git a/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-32px.png b/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-32px.png new file mode 100644 index 000000000..8b25551a9 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-32px.png differ diff --git a/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-Light-32px.png b/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-Light-32px.png new file mode 100644 index 000000000..628da97c7 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/github/PNG/GitHub-Mark-Light-32px.png differ diff --git a/sqlg-doc/docs/2.1.4/img/github/SVG/law.svg b/sqlg-doc/docs/2.1.4/img/github/SVG/law.svg new file mode 100644 index 000000000..0b144363d --- /dev/null +++ b/sqlg-doc/docs/2.1.4/img/github/SVG/law.svg @@ -0,0 +1,12 @@ + + + + law + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/img/github/SVG/mark-github.svg b/sqlg-doc/docs/2.1.4/img/github/SVG/mark-github.svg new file mode 100644 index 000000000..af1bfa1f9 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/img/github/SVG/mark-github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/img/hr.gif b/sqlg-doc/docs/2.1.4/img/hr.gif new file mode 100644 index 000000000..bdb4168de Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/hr.gif differ diff --git a/sqlg-doc/docs/2.1.4/img/nav-bg.gif b/sqlg-doc/docs/2.1.4/img/nav-bg.gif new file mode 100644 index 000000000..474396561 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/nav-bg.gif differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/E_created.png b/sqlg-doc/docs/2.1.4/img/sqlg/E_created.png new file mode 100644 index 000000000..b7acd733e Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/E_created.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/E_knows.png b/sqlg-doc/docs/2.1.4/img/sqlg/E_knows.png new file mode 100644 index 000000000..9ed879837 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/E_knows.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/V_Person_name_index.png b/sqlg-doc/docs/2.1.4/img/sqlg/V_Person_name_index.png new file mode 100644 index 000000000..5fb31a3a6 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/V_Person_name_index.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/V_person.png b/sqlg-doc/docs/2.1.4/img/sqlg/V_person.png new file mode 100644 index 000000000..1702afd52 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/V_person.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/V_software.png b/sqlg-doc/docs/2.1.4/img/sqlg/V_software.png new file mode 100644 index 000000000..361967ccd Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/V_software.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/bulkAddEdgesMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/bulkAddEdgesMemory.png new file mode 100644 index 000000000..a93143429 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/bulkAddEdgesMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/continent.png b/sqlg-doc/docs/2.1.4/img/sqlg/continent.png new file mode 100644 index 000000000..f16b103d2 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/continent.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/edges.png b/sqlg-doc/docs/2.1.4/img/sqlg/edges.png new file mode 100644 index 000000000..999ee9725 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/edges.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/fleet.png b/sqlg-doc/docs/2.1.4/img/sqlg/fleet.png new file mode 100644 index 000000000..4e4a95a9c Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/fleet.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/normalBatchModeMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/normalBatchModeMemory.png new file mode 100644 index 000000000..59a6a6990 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/normalBatchModeMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/postgresql_composite_index.png b/sqlg-doc/docs/2.1.4/img/sqlg/postgresql_composite_index.png new file mode 100644 index 000000000..c64baecf1 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/postgresql_composite_index.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/public.png b/sqlg-doc/docs/2.1.4/img/sqlg/public.png new file mode 100644 index 000000000..85a0bb57d Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/public.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/schemas.png b/sqlg-doc/docs/2.1.4/img/sqlg/schemas.png new file mode 100644 index 000000000..9fc4110bf Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/schemas.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology.png b/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology.png new file mode 100644 index 000000000..b24de73f9 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology_uml.png b/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology_uml.png new file mode 100644 index 000000000..392931ab9 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/sqlg_topology_uml.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamBatchModeBulkEdgeMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamBatchModeBulkEdgeMemory.png new file mode 100644 index 000000000..eee652119 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamBatchModeBulkEdgeMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamWithLockAndEdgeCreationMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamWithLockAndEdgeCreationMemory.png new file mode 100644 index 000000000..4a86145cc Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamWithLockAndEdgeCreationMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeMemory.png new file mode 100644 index 000000000..7dc523f59 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeWithLockMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeWithLockMemory.png new file mode 100644 index 000000000..fee50d014 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamingBatchModeWithLockMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeAndEdgesMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeAndEdgesMemory.png new file mode 100644 index 000000000..4bcbf5ec6 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeAndEdgesMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeMemory.png b/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeMemory.png new file mode 100644 index 000000000..c14ca777b Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/streamingWithLockBatchModeMemory.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tableDefinition.png b/sqlg-doc/docs/2.1.4/img/sqlg/tableDefinition.png new file mode 100644 index 000000000..9ab117a95 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tableDefinition.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic-graph.xcf b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic-graph.xcf new file mode 100644 index 000000000..d3053ed8d Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic-graph.xcf differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic.png b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic.png new file mode 100644 index 000000000..64837ad33 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.dia b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.dia new file mode 100644 index 000000000..3c6b965f7 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.dia differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.png b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.png new file mode 100644 index 000000000..80b73aecc Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic1.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2-er.png b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2-er.png new file mode 100644 index 000000000..5efe54253 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2-er.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2.dia b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2.dia new file mode 100644 index 000000000..e9c8b970f Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-classic2.dia differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-er.png b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-er.png new file mode 100644 index 000000000..411efdf87 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-er.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-graph.png b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-graph.png new file mode 100644 index 000000000..a74877e98 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/tinkerpop-modern-graph.png differ diff --git a/sqlg-doc/docs/2.1.4/img/sqlg/vertices.png b/sqlg-doc/docs/2.1.4/img/sqlg/vertices.png new file mode 100644 index 000000000..c5f47516c Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/sqlg/vertices.png differ diff --git a/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.png b/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.png new file mode 100644 index 000000000..c9b33be77 Binary files /dev/null and b/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.png differ diff --git a/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.svg b/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.svg new file mode 100644 index 000000000..2010e9435 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/img/uml/topology Class Diagram.svg @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Schema + + + + vertexLabel : VertexLabel [*]{unique} + + + + + + + + + VertexLabel + + + + + + + EdgeLabel + + + + outIsOrdered : Boolean [1] + + outIsUnique : Boolean [1] + + inIsOrdered : Boolean [1] + + inIsUnique : Boolean [1] + + + + + + + + + Property + + + + + + + AbstractLabel + + + + + + + Index + + + + + + + IndexType + + + + UNIQUE + + NON_UNIQUE + + + + + + + schema_vertex + vertexLabel[*] + schema[1] + + + + + + + + out_edges + outEdgelabel[*] + vertexlabel[1..*] + + + + + + + + in_edges + inEdgelabel[*] + vertexlabel[1..*] + + + + + + + + + + + + + + + + abstractLabel_property + abstractLabel[1] + property[*] + + + + + + + property_index + index[*] + property[1..*] + + + + + + abstractLabel_index + abstractLabel[1] + index[*] + + + + + + + + abstractLabel_identifier + abstractLabel[1] + identifier[*] + + + + + + index_indexType + indexType[1] + index[*] + + + + + diff --git a/sqlg-doc/docs/2.1.4/include/architecture.adoc b/sqlg-doc/docs/2.1.4/include/architecture.adoc new file mode 100644 index 000000000..bfcaf396b --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/architecture.adoc @@ -0,0 +1,127 @@ +== Architecture + +TinkerPop's property graph semantics specifies that every vertex and edge has a single label. Modelling this in a RDBMS +is trivial. TinkerPop has no notion of cardinality nor of order. Every relationship between vertex labels is modelled as +`many to many` relationship with no specified order. + +This realizes itself as a classic `many to many` relationship in a RDBMS database. + +---- +VertexLabel <---- EdgeLabel ----> VertexLabel +---- + +=== Vertex tables +Every unique vertex label maps to a table. Vertex tables are prefixed with a `V_`. i.e. `V_Person`. The vertex table +stores the vertex's properties. + + +=== Edge tables +Every unique edge label maps to a table. Edge tables are prefixed with a `E_`. i.e. `E_friend`. The edge table stores +each edge's adjacent vertex ids and the edge properties. The column corresponding to each adjacent vertex id (`IN` and `OUT`) +has a foreign key to the adjacent vertex's table. The foreign key is optional, instead just an index on the adjacent vertex id +can be used. + +[NOTE] +By default, Sqlg will use an auto increment `ID` `bigint` for the primary key. You can however use the <> interface to define which properties to use as the primary key. + +[NOTE] +`sqlg.properties` `implement.foreign.keys = false` + +Edge foreign keys have a significant impact on performance. + +Edge foreign keys are enabled by default. + +From a rdbms' perspective each edge table is the classic `many to many` join table between vertices. + +=== TinkerPop-modern + +Taken from http://tinkerpop.apache.org/docs/current/reference/#intro[TinkerPop] + +image:sqlg/tinkerpop-modern-graph.png[image of tinkerpop-classic] + +.ER Diagram +image:sqlg/tinkerpop-modern-er.png[image of tinkerpop-classic] + +.V_person +image:sqlg/V_person.png[image of tinkerpop-classic] + +.V_software +image:sqlg/V_software.png[image of tinkerpop-classic] + +.E_knows +image:sqlg/E_knows.png[image of tinkerpop-classic] + +.E_created +image:sqlg/E_created.png[image of tinkerpop-classic] + +=== Namespacing and Schemas + +Many RDBMS databases have the notion of a `schema` as a namespace for tables. Sqlg supports schemas +for vertex labels. Distinct schemas for edge tables are unnecessary as edge tables are created in the schema of the adjacent `out` vertex. +By default schemas for vertex tables go into the underlying databases' default schema. For Postgresql, hsqldb and H2 this +is the `public` schema. + +To specify the schema for a label Sqlg uses the dot `.` notation. + +[source,java,options="nowrap"] +---- +@Test +public void testElementsInSchema() { + Vertex john = this.sqlgGraph.addVertex(T.label, "Manager", "name", "john"); # <1> + Vertex palace1 = this.sqlgGraph.addVertex(T.label, "continent.House", "name", "palace1"); # <2> + Vertex corrola = this.sqlgGraph.addVertex(T.label, "fleet.Car", "model", "corrola"); # <3> + palace1.addEdge("managedBy", john); + corrola.addEdge("owner", john); + this.sqlgGraph.tx().commit(); + assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Manager").count().next().intValue()); # <4> + assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("House").count().next().intValue()); # <5> + assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("continent.House").count().next().intValue()); <6> + assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("Car").count().next().intValue()); + assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("fleet.Car").count().next().intValue()); + assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("managedBy").count().next().intValue()); + assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("owner").count().next().intValue()); +} +---- +<1> 'Manager' will be in the default 'public' schema. +<2> 'House' will be in the 'continent' schema. +<3> 'Car' will be in the 'fleet' schema. +<4> Vertices in the public schema do not need to be qualified with the schema. +<5> Vertices not in the public schema must be qualified with its schema. In this case 'House' will not be found. +<6> As 'House' is qualified with the 'continent' schema it will be found. + +Table `V_manager` is in the `public` (default) schema. + +Table `V_house` is in the `continent` schema. + +Table `V_car` is in the `fleet` schema. + +Table `E_managedBy` is in the `continent` schema as its `out` vertex `palace1` is in the `continent` schema. + +Table `E_owner` is in the `fleet` schema as its `out` vertex is in the `fleet`schema. + +.postgresql schemas +image:sqlg/schemas.png[image of tinkerpop-classic] +image:sqlg/continent.png[image of tinkerpop-classic] +image:sqlg/fleet.png[image of tinkerpop-classic] +image:sqlg/public.png[image of tinkerpop-classic] + +==== Edge label + +An edge label can have many different out vertex labels. +This means that its possible for a single edge label to be stored in multiple schemas and tables. +One for each distinct out vertex label. Gremlin queries will work as per normal. +However it is possible to target the edges per out vertex schema directly. + +.eg. +[source,java,options="nowrap"] +---- +@Test +public void testEdgeAcrossSchema() { + Vertex a = this.sqlgGraph.addVertex(T.label, "A.A"); + Vertex b = this.sqlgGraph.addVertex(T.label, "B.B"); + Vertex c = this.sqlgGraph.addVertex(T.label, "C.C"); + a.addEdge("specialEdge", b); + b.addEdge("specialEdge", c); + this.sqlgGraph.tx().commit(); + assertEquals(2, this.sqlgGraph.traversal().E().hasLabel("specialEdge").count().next().intValue()); # <1> + assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("A.specialEdge").count().next().intValue()); # <2> + assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("B.specialEdge").count().next().intValue()); # <3> +} +---- +<1> Query 'specialEdge' +<2> Query 'specialEdge' with, out vertex labels in the 'A' schema. +<3> Query 'specialEdge' with, out vertex labels in the 'B' schema. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/batchMode.adoc b/sqlg-doc/docs/2.1.4/include/batchMode.adoc new file mode 100644 index 000000000..99a66fe20 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/batchMode.adoc @@ -0,0 +1,257 @@ +== Batch Mode + +Sqlg supports 3 distinct batch modes. Normal, streaming and streaming with lock. Batch modes are only implemented on Postgresql. +Batch mode is activated on the transaction object itself. After every `commit` the batchMode needs to be reactivated. + +Sqlg introduces an extra method on the transaction, `flush()`. + +* In normal batch mode `flush()` will send all the data to Postgresql, assign id(s) and clear the cache. +* In streaming mode `flush()` will close the OutputStream that the data has been written to. +* In streaming mode with lock `flush()` will close the OutputStream that the data has been written to and assign id(s). + +The Postgresql 'https://www.postgresql.org/docs/current/static/sql-copy.html[copy]' command is used to bulk insert data. + +=== Normal batch mode + +In normal batch mode the standard TinkerPop modification api can be used. Normal batch mode caches all modifications in memory +and on `commit()` or `flush()` sends the modifications to the server. + +Because all modifications are held in memory it is important to call `commit()` or `flush()` to prevent an `OutOfMemoryError`. + +In batch mode vertices and edges returned from `Graph.addVertex` and `vertex.addEdge` respectively do *not* yet have their id(s) assigned to them. +This is because the new vertices and edges are cached in memory and are only sent to Postgresql on `commit()` or `flush()`. +After `commit()` or `flush()` the new vertices and edges have their id(s) assigned. + +The transaction must be manually placed in normal batch mode. i.e. `SqlgGraph.tx().normalBatchModeOn()` must occur before any batch processing. +After every `commit()` the transaction reverts to a regular transaction and must be placed in normal batch mode again +for batch processing to continue. + +Vertices and edges can be created and updated and removed as per normal making normal batch mode easy to use. + +[NOTE] +Sqlg does not query the cache. If a gremlin query is executed while in batch mode the batch is first flushed. +Take care not to query the graph while in batch mode as flushing often will defeat the purpose of batching in the first place. + +[source,java,options="nowrap"] +.custom api +---- +sqlgGraph.tx().normalBatchModeOn(); +sqlgGraph.tx().flush(); +---- + +Create 10 000 000 Persons each with a car. 20 000 000 vertices and 10 000 000 edges in total. + +[source,java,options="nowrap"] +---- +@Test +public void showNormalBatchMode() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + this.sqlgGraph.tx().normalBatchModeOn(); + for (int i = 1; i <= 10_000_000; i++) { + Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John" + i); + Vertex car = this.sqlgGraph.addVertex(T.label, "Car", "name", "Dodge" + i); + person.addEdge("drives", car); + if (i % 100_000 == 0) { # <1> + this.sqlgGraph.tx().flush(); # <1> + } + } + this.sqlgGraph.tx().commit(); + stopWatch.stop(); + System.out.println(stopWatch.toString()); +} +---- +<1> To preserve memory `commit` or `flush` every so often. + +.output without edge foreign keys +---- +Time taken: 0:05:48.889 +---- + +.output with edge foreign keys +---- +Time taken: 0:02:33.313 +---- + +.memory +image:sqlg/normalBatchModeMemory.png[image of tinkerpop-classic] + +=== Streaming batch mode + +Streaming batch writes any new vertex or edge immediately to Postgresql via its `stdin` api. I.e. the data is written +directly to a Postgresql jdbc driver OutputStream. + +Streaming batch mode does *not* use the `Graph.addVertex` method. Instead `SqlgGraph.streamVertex` is defined. + +The transaction must be placed in streaming batch mode manually before any streaming batch modification can happen. `SqlgGraph.tx().streamingBatchModeOn()` +After every `commit()` the transaction reverts to normal mode and must be placed into streaming batch mode again +for streaming batch mode to continue. + +The benefit of streaming mode is that the memory consumption is very low as nothing is cached. It is also somewhat faster than +the normal batch mode (+/- 25% faster). + +However the caveat is that, per transaction/thread only one label/table can be written between consecutive calls to `SqlgTransaction.flush()`. +Further it is not possible to assign an id to the vertex or element. As such the `SqlgGraph.streamVertex` method returns void. + +[source,java,options="nowrap"] +.custom api +---- +sqlgGraph.tx().streamingBatchModeOn(); +---- + +Create 10 000 000 Persons and 10 000 000 cars. + +[source,java,options="nowrap"] +---- +@Test +public void showStreamingBatchMode() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + //enable streaming mode + this.sqlgGraph.tx().streamingBatchModeOn(); + for (int i = 1; i <= 10_000_000; i++) { + this.sqlgGraph.streamVertex(T.label, "Person", "name", "John" + i); + } + this.sqlgGraph.tx().flush(); # <1> + for (int i = 1; i <= 10_000_000; i++) { + this.sqlgGraph.streamVertex(T.label, "Car", "name", "Dodge" + i); + } + this.sqlgGraph.tx().commit(); + stopWatch.stop(); + System.out.println(stopWatch.toString()); +} +---- +<1> flushing is needed before starting streaming Car. Only only one label/table can stream at a time. + +.output +---- +Time taken: 0:00:42.014 +---- + +.memory +image:sqlg/streamingBatchModeMemory.png[image of tinkerpop-classic] + +=== Bulk edge creation + +To create an edge via the normal api a handle to the `Vertex` is needed. +This is not always the case. In particula if the `SqlgGraph.streamVertex` api is used no handle to the `Vertex` is returned. + +For this scenario there is a bulk edge creation method. + +[source,java,options="nowrap"] +---- +public void bulkAddEdges(String outVertexLabel, String inVertexLabel, String edgeLabel, Pair idFields, Collection> uids) { +---- + +* `outLabel` and `inLabel` specifies the out and in vertex labels that the edges will be between. +* `edgeLabel` is the label of the edges to be created. +* `idFields` specifies the fields that uniquely identify the out and in vertex. +* `uids` are the actual unique identifies for each out/in vertex pairing. + +Sqlg will then first copy the `uids` into a temporary table. Then it joins the temporary table on the out and in vertex tables +to retrieve the in and out ids. +These ids are then inserted into the edge table. +All this happens on Postgresql, having minimal processing and memory impact on the java process. + +The unique identifiers still have to be kept in memory, but its is not necessary to have the actual out and in vertices in memory. + +[NOTE] +The unique identifiers do not need to be the vertices's id. It can be any property as long as it is unique. + +[source,java,options="nowrap"] +---- +@Test +public void showBulkEdgeCreation() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + int count = 0; + for (int i = 1; i <= 10; i++) { + List> identifiers = new ArrayList<>(); + this.sqlgGraph.tx().streamingBatchModeOn(); + for (int j = 1; j <= 1_000_000; j++) { + this.sqlgGraph.streamVertex(T.label, "Person", "name", "John" + count, "personUid", String.valueOf(count)); + } + this.sqlgGraph.tx().flush(); + for (int j = 1; j <= 1_000_000; j++) { + this.sqlgGraph.streamVertex(T.label, "Car", "name", "Dodge" + count, "carUid", String.valueOf(count)); + identifiers.add(Pair.of(String.valueOf(count), String.valueOf(count++))); + } + this.sqlgGraph.tx().flush(); + this.sqlgGraph.bulkAddEdges("Person", "Car", "drives", Pair.of("personUid", "carUid"), identifiers); + this.sqlgGraph.tx().commit(); + } + stopWatch.stop(); + System.out.println("Time taken: " + stopWatch.toString()); +} +---- + +.output (with edge foreign keys) +---- +Time taken: 0:10:03.397 +---- + +.output (without edge foreign keys) +---- +Time taken: 0:03:45.951 +---- + +.memory +image:sqlg/bulkAddEdgesMemory.png[image of tinkerpop-classic] + +=== Streaming with lock batch mode + +Streaming with lock batch mode is similar to streaming batch mode. The difference being that the label/table being written to is +locked. Locking the table ensures that no concurrent changes will occur on the table. This allows Sqlg to query the id sequence and +assigned ids to the elements. + +This means that the normal `Vertex vertex = graph.addVertex(...)` method can be used. This is useful if a pointer to the new vertices are needed. + +The transaction must be placed into streaming with lock batch mode manually before any streaming with lock batch modification can happen. +`SqlgGraph.tx().streamingWithLockBatchModeOn()` After every `commit()` the transaction reverts to normal mode and must +be placed into streaming batch mode again for streaming batch mode to continue. + +[source,java,options="nowrap"] +.custom api +---- +sqlgGraph.tx().streamingWithLockBatchModeOn(); +---- + +[source,java,options="nowrap"] +---- +@Test +public void showStreamingWithLockBulkEdgeCreation() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + int count = 0; + for (int i = 1; i <= 10; i++) { + List persons = new ArrayList<>(); + this.sqlgGraph.tx().streamingWithLockBatchModeOn(); + for (int j = 1; j <= 1_000_000; j++) { + Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John" + count); + persons.add(person); + } + this.sqlgGraph.tx().flush(); + List cars = new ArrayList<>(); + for (int j = 1; j <= 1_000_000; j++) { + Vertex car = this.sqlgGraph.addVertex(T.label, "Car", "name", "Dodge" + count++); + cars.add(car); + } + this.sqlgGraph.tx().flush(); + Iterator carIter = cars.iterator(); + for (Vertex person : persons) { + person.addEdge("drives", carIter.next()); + } + this.sqlgGraph.tx().commit(); + } + stopWatch.stop(); + System.out.println(stopWatch.toString()); +} +---- + +.output without edge foreign keys +---- +Time taken: 0:02:42.363 +---- + +.memory +image:sqlg/streamingBatchModeWithLockMemory.png[image of tinkerpop-classic] \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/dataTypes.adoc b/sqlg-doc/docs/2.1.4/include/dataTypes.adoc new file mode 100644 index 000000000..8bbf2ebaa --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/dataTypes.adoc @@ -0,0 +1,261 @@ +== Data types + +.Table Data types +|=== +|Java |Postgresql |HSQLDB |H2 |MariaDB | MSSqlServer + +|Boolean +|BOOLEAN +|BOOLEAN +|BOOLEAN +|BOOLEAN +|BIT + +|Byte +|*Not supported* +|TINYINT +|TINYINT +|TINYINT +|TINYINT + +|Short +|SMALLINT +|SMALLINT +|SMALLINT +|SMALLINT +|SMALLINT + +|Integer +|INTEGER +|INTEGER +|INT +|INTEGER +|INT + +|Long +|BIGINT +|BIGINT +|BIGINT +|BIGINT +|BIGINT + +|Float +|REAL +|*Not supported* +|REAL +|*Not supported* +|REAL + +|Double +|DOUBLE PRECISION +|DOUBLE +|DOUBLE +|DOUBLE +|DOUBLE PRECISION + +|String +|TEXT +|LONGVARCHAR +|VARCHAR +|LONGTEXT +|VARCHAR(2000) + +|String (fixed length) +|VARCHAR(x) +|VARCHAR(x) +|VARCHAR(x) +|VARCHAR(x) +|VARCHAR(x) + +|Boolean[] +|BOOLEAN[] +|BOOLEAN ARRAY DEFAULT ARRAY[] +|ARRAY +|BOOLEAN ARRAY DEFAULT ARRAY[] +|*Not supported* + +|Byte[] +|BYTEA +|LONGVARBINARY +|BINARY +|BLOB +|VARBINARY(max) + +|Short[] +|SMALLINT[] +|SMALLINT ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|Integer[] +|INTEGER[] +|INTEGER ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|Long[] +|BIGINT[] +|BIGINT ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|Float[] +|REAL[] +|*Not supported* +|ARRAY +|*Not supported* +|*Not supported* + +|Double[] +|DOUBLE PRECISION[] +|DOUBLE ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|String[] +|TEXT[] +|LONGVARCHAR ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.LocalDateTime +|TIMESTAMP +|TIMESTAMP +|TIMESTAMP +|DATETIME(3) +|DATETIME2(3) + +|java.time.LocalDate +|DATE +|DATE +|DATE +|DATE +|DATE + +|java.time.LocalTime +|TIME +|TIME +|TIME +|TIME +|TIME + +|java.time.ZonedDateTime +|TIMESTAMP, TEXT +|TIMESTAMP, LONGVARCHAR +|TIMESTAMP, VARCHAR +|DATETIME(3), TINYTEXT +|DATETIME2(3), VARCHAR(255) + +|java.time.Period +|INTEGER, INTEGER, INTEGER +|INTEGER, INTEGER, INTEGER +|INT, INT, INT +|INTEGER, INTEGER, INTEGER +|INT, INT, INT + +|java.time.Duration +|BIGINT, INTEGER +|BIGINT, INTEGER +|BIGINT, INT +|BIGINT, INTEGER +|BIGINT, INT + +|java.time.LocalDateTime[] +|TIMESTAMP[] +|TIMESTAMP ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.LocalDate[] +|DATE[] +|DATE ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.LocalTime[] +|TIME[] +|TIME ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.ZonedDateTime[] +|TIMESTAMP[], TEXT[] +|TIMESTAMP ARRAY DEFAULT ARRAY[], LONGVARCHAR ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.Period[] +|INTEGER[], INTEGER[], INTEGER[] +|INTEGER ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|java.time.Duration[] +|BIGINT[], INTEGER[] +|BIGINT ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[] +|ARRAY +|*Not supported* +|*Not supported* + +|com.fasterxml.jackson.databind.JsonNode +|JSONB +|LONGVARCHAR +|VARCHAR +|LONGTEXT +|VARCHAR(max) + +|com.fasterxml.jackson.databind.JsonNode[] +|JSONB[] +|ARRAY +|ARRAY +|*Not supported* +|*Not supported* + +|org.postgis.Point +|geometry(POINT) +|*Not supported* +|*Not supported* +|*Not supported* +|*Not supported* + +|org.umlg.sqlg.gis.GeographyPoint +|geography(POINT, 4326) +|*Not supported* +|*Not supported* +|*Not supported* +|*Not supported* + +|org.postgis.LineString +|geometry(LINESTRING) +|*Not supported* +|*Not supported* +|*Not supported* +|*Not supported* + +|org.postgis.Polygon +|geometry(POLYGON) +|*Not supported* +|*Not supported* +|*Not supported* +|*Not supported* + +|org.umlg.sqlg.gis.GeographyPolygon +|geography(POLYGON, 4326) +|*Not supported* +|*Not supported* +|*Not supported* +|*Not supported* +|=== + +[NOTE] +`java.time.LocalTime` drops the nano second precision. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gettingStarted.adoc b/sqlg-doc/docs/2.1.4/include/gettingStarted.adoc new file mode 100644 index 000000000..7fffd3e30 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gettingStarted.adoc @@ -0,0 +1,190 @@ +== Getting Started + +=== Maven coordinates + +.Postgresql + + org.umlg + sqlg-postgres + 2.1.4 + + +.HSQLDB + + org.umlg + sqlg-hsqldb + 2.1.4 + + +.H2 + + org.umlg + sqlg-h2 + 2.1.4 + + +.MariaDB + + org.umlg + sqlg-mariadb + 2.1.4 + + +.MySQL + + org.umlg + sqlg-mysql + 2.1.4 + + +.MSSqlServer + + org.umlg + sqlg-mssqlserver + 2.1.4 + + +This will include `gremlin-groovy`. If you have no need for that then use the following coordinates. + +.Postgresql + + org.umlg + sqlg-postgres-dialect + 2.1.4 + + +.HSQLDB + + org.umlg + sqlg-hsqldb-dialect + 2.1.4 + + +.H2 + + org.umlg + sqlg-h2-dialect + 2.1.4 + + +.MariaDB + + org.umlg + sqlg-mariadb-dialect + 2.1.4 + + +.MySQL + + org.umlg + sqlg-mysql-dialect + 2.1.4 + + +.MSSqlServer + + org.umlg + sqlg-mssqlserver-dialect + 2.1.4 + + +=== Start + +`SqlgGraph` is a singleton that can be shared among multiple threads. You instantiate `SqlgGraph` using the standard +TinkerPop static constructors. + +* `Graph g = SqlgGraph.open(final Configuration configuration)` +* `Graph g = SqlgGraph.open(final String pathToSqlgProperties)` + +The configuration object requires the following properties. + +.Postgresql + jdbc.url=jdbc:postgresql://localhost:5432/yourdb + jdbc.username=postgres + jdbc.password=****** + +.HSQLDB + jdbc.url=jdbc:hsqldb:file:/tmp/yourdb + jdbc.username=SA + jdbc.password= + +.H2 + jdbc.url=jdbc:h2:file:target/tmp/yourdb + jdbc.username=SA + jdbc.password= + +.MariaDB + jdbc.url=jdbc:mariadb://localhost:3306/?useSSL=false + jdbc.username=mariadb + jdbc.password=mariadb + +.MySQL + jdbc.url=jdbc:mysql://localhost:3306/?useSSL=false + jdbc.username=mysql + jdbc.password=mysql + +.MSSqlServer + jdbc.url=jdbc:sqlserver://localhost:1433;databaseName=yourdb; + jdbc.username=SA + jdbc.password=***** + +In the case of Postgresql and MSSqlServer the database must already exist. + +Once you have access to the graph you can use it as per normal. +[source,java,options="nowrap"] +---- +@Test +public void useAsPerNormal() { + Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John"); + Vertex address = this.sqlgGraph.addVertex(T.label, "Address", "street", "13th"); + person.addEdge("livesAt", address, "since", LocalDate.of(2010, 1, 21)); + this.sqlgGraph.tx().commit(); # <1> + List addresses = this.sqlgGraph.traversal().V().hasLabel("Person").out("livesAt").toList(); + assertEquals(1, addresses.size()); +} +---- +<1> It is very important to always commit or rollback the transaction. +If you do not, connections to the database will remain open and eventually +the connection pool with run out of connections. + +=== Gremlin Console + +.Postgresql +pieter@pieter-Precision-7510:~/Downloads/tinkerpop-console/apache-tinkerpop-gremlin-console-3.4.10-bin/apache-tinkerpop-gremlin-console-3.4.10/bin/$ ./gremlin.sh + + \,,,/ + (o o) +-----oOOo-(3)-oOOo----- +plugin activated: tinkerpop.server +plugin activated: tinkerpop.utilities +plugin activated: tinkerpop.tinkergraph +gremlin> :install org.umlg sqlg-postgres 2.1.4 +==>Loaded: [org.umlg, sqlg-postgres, 2.1.4] - restart the console to use [sqlg.postgres] +gremlin> :x +pieter@pieter-Precision-7510:~/Downloads/tinkerpop-console/apache-tinkerpop-gremlin-console-3.4.10-bin/apache-tinkerpop-gremlin-console-3.4.10/bin/$ ./gremlin.sh + + \,,,/ + (o o) +-----oOOo-(3)-oOOo----- +plugin activated: tinkerpop.server +plugin activated: tinkerpop.utilities +plugin activated: tinkerpop.tinkergraph +gremlin> :plugin list +==>tinkerpop.server[active] +==>tinkerpop.gephi +==>tinkerpop.utilities[active] +==>tinkerpop.sugar +==>tinkerpop.credentials +==>sqlg.postgres +==>tinkerpop.tinkergraph[active] +gremlin> :plugin use sqlg.postgres +==>sqlg.postgres activated +gremlin> graph = SqlgGraph.open('/pathTo/sqlg.properties') +==>sqlggraph[SqlGraph] (jdbc:postgresql://localhost:5432/sqlgraphdb) (user = postgres) +gremlin> g = graph.traversal() +==>sqlggraphtraversalsource[sqlggraph[SqlGraph] (jdbc:postgresql://localhost:5432/sqlgraphdb) (user = postgres), standard] +gremlin> graph.io(GraphSONIo.build(GraphSONVersion.V3_0)).readGraph("/pathTo/grateful-dead-v3d0.json") +==>null +gremlin> g.V().count() +==>808 +gremlin> \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/predicates.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/predicates.adoc new file mode 100644 index 000000000..6e1c936de --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/predicates.adoc @@ -0,0 +1,199 @@ +=== Predicates + +TinkerPop's http://tinkerpop.apache.org/javadocs/current/full/org/apache/tinkerpop/gremlin/process/traversal/Compare.html[Compare] and +http://tinkerpop.apache.org/javadocs/current/full/org/apache/tinkerpop/gremlin/process/traversal/Contains.html[Contains] predicates are optimized +to execute on the database. + +==== Compare predicate + +[source,java,options="nowrap"] +---- +@Test +public void showComparePredicates() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b1.addEdge("bc", c2); + b2.addEdge("bc", c3); + b2.addEdge("bc", c4); + this.sqlgGraph.tx().commit(); + + List result = this.sqlgGraph.traversal() + .V().hasLabel("A") + .out().has("name", P.eq("b1")) + .out().has("name", P.eq("c2")) <1> + .values("name") + .toList(); + for (String name : result) { + System.out.println(name); + } +} +---- +<1> The `P` predicates will resolve on the database as a `sql` `where` clause. + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE + ( "public"."V_B"."name" = ?) AND ( "public"."V_C"."name" = ?) +---- + +The same pattern is used for all the +http://tinkerpop.apache.org/javadocs/current/full/org/apache/tinkerpop/gremlin/process/traversal/Compare.html[Compare] predicates. + +==== Contains predicate + +Sqlg's implementation of http://tinkerpop.apache.org/javadocs/current/full/org/apache/tinkerpop/gremlin/process/traversal/Contains.html[Contains] +is slightly more complex. + +For Postgresql, MSSqlServer and HSQLDB a join onto a `values expression` is used. + +For H2 and MariaDB a regular `in` clause is used. + +[source,java,options="nowrap"] +---- +@Test +public void showContainsPredicate() { + List numbers = new ArrayList<>(10000); + for (int i = 0; i < 10000; i++) { + this.sqlgGraph.addVertex(T.label, "A", "number", i); + numbers.add(i); + } + this.sqlgGraph.tx().commit(); + + List persons = this.sqlgGraph.traversal().V() + .hasLabel("A") + .has("number", P.within(numbers)) + .toList(); + + assertEquals(10000, persons.size()); +} +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."number" AS "alias2" +FROM + "public"."V_A" INNER JOIN + (VALUES (0::INTEGER), (1::INTEGER), ... (9998::INTEGER), (9999::INTEGER)) as tmp1(within) on "public"."V_A"."number" = tmp1.within +---- + +This pattern makes `P.within` and `p.without` very fast even with millions of values being passed into the query. +For the case of there being only one value Sqlg will use an `equals` instead of a values statement or an `in` statement. + +==== Text predicate + +[NOTE] +Sqlg assumes a case-sensitive collation. +MSSqlServer does not default to a case-sensitive collation. +Create the database with `CREATE DATABASE sqlgraphdb COLLATE sql_latin1_general_cp1_cs_as` + +Sqlg includes its own Text predicate for full text queries. + +* Text.contains (case sensitive string contains) +* Text.ncontains (case sensitive string does not contain) +* Text.containsCIS (case insensitive string contains) +* Text.ncontainsCIS (case insensitive string does not contain) +* Text.startsWith (case sensitive string starts with) +* Text.nstartsWith (case sensitive string does not start with) +* Text.endsWith (case sensitive string ends with) +* Text.nendsWith (case sensitive string does not end with) + +[source,java,options="nowrap"] +---- +@Test +public void showTextPredicate() { + Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John XXX Doe"); + Vertex peter = this.sqlgGraph.addVertex(T.label, "Person", "name", "Peter YYY Snow"); + this.sqlgGraph.tx().commit(); + + List persons = this.sqlgGraph.traversal().V() + .hasLabel("Person") + .has("name", Text.contains("XXX")).toList(); + + assertEquals(1, persons.size()); + assertEquals(john, persons.get(0)); +} +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."ID" AS "alias1", + "public"."V_Person"."name" AS "alias2" +FROM + "public"."V_Person" +WHERE + ( "public"."V_Person"."name" like ?) +---- + +==== Full text search + +Full text search is supported on postgresql. + +This is shown under <> + +==== DateTime queries + +LocalDateTime, LocalDate and LocalTime queries are supported. + +[source,java,options="nowrap"] +---- +@Test +public void showSearchOnLocalDateTime() { + LocalDateTime born1 = LocalDateTime.of(1990, 1, 1, 1, 1, 1); + LocalDateTime born2 = LocalDateTime.of(1990, 1, 1, 1, 1, 2); + LocalDateTime born3 = LocalDateTime.of(1990, 1, 1, 1, 1, 3); + Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John", "born", born1); + Vertex peter = this.sqlgGraph.addVertex(T.label, "Person", "name", "Peter", "born", born2); + Vertex paul = this.sqlgGraph.addVertex(T.label, "Person", "name", "Paul", "born", born3); + this.sqlgGraph.tx().commit(); + + List persons = this.sqlgGraph.traversal().V().hasLabel("Person") + .has("born", P.eq(born1)) + .toList(); + assertEquals(1, persons.size()); + assertEquals(john, persons.get(0)); + + persons = this.sqlgGraph.traversal().V().hasLabel("Person") + .has("born", P.between(LocalDateTime.of(1990, 1, 1, 1, 1, 1), LocalDateTime.of(1990, 1, 1, 1, 1, 3))) + .toList(); + //P.between is inclusive to exclusive + assertEquals(2, persons.size()); + assertTrue(persons.contains(john)); + assertTrue(persons.contains(peter)); +} +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."ID" AS "alias1", + "public"."V_Person"."born" AS "alias2", + "public"."V_Person"."name" AS "alias3" +FROM + "public"."V_Person" +WHERE + ( "public"."V_Person"."born" >= ?) AND ( "public"."V_Person"."born" < ?) +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/andStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/andStep.adoc new file mode 100644 index 000000000..d3e242ea2 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/andStep.adoc @@ -0,0 +1,82 @@ +==== And Step + +link:{tinkerpop-docs}#and-step[`And Step`]s are folded into the <> or <>. + +[source,java,options="nowrap"] +---- +@Test +public void showAndStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1", "surname", "x", "address", "y"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2", "surname", "x", "address", "y"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3", "surname", "x", "address", "y"); + Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4", "surname", "x", "address", "y"); + Vertex c5 = this.sqlgGraph.addVertex(T.label, "C", "name", "c5", "surname", "x", "address", "y"); + Vertex c6 = this.sqlgGraph.addVertex(T.label, "C", "name", "c6", "surname", "x", "address", "y"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b2.addEdge("bc", c2); + b2.addEdge("bc", c3); + b2.addEdge("bc", c4); + b2.addEdge("bc", c5); + b2.addEdge("bc", c6); + this.sqlgGraph.tx().commit(); + + GraphTraversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .out() + .out() + .and( + __.has("name", "c1"), + __.has("surname", "x"), + __.has("address", "y") + ); + + System.out.println(traversal); + traversal.hasNext(); + System.out.println(traversal); + List c = traversal.toList(); + assertEquals(1, c.size()); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-and-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex), AndStep([[HasStep([name.eq(c1)])], [HasStep([surname.eq(x)])], [HasStep([address.eq(y)])]])] +---- + +[[anchor-after-optimization-and-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]] +---- + +This example is similar to the <> example except for an additional `And Step` which in turn contains three `Has Step` s. +It shows the two `Vertex Step` s the `And Step` and the `Has Step` s being folded into the `SqlgGraphStep`. + +The link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] will generate the following `sql` to retrieve the data. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."address" AS "alias2", + "public"."V_C"."surname" AS "alias3", + "public"."V_C"."name" AS "alias4" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE +(("public"."V_C"."name" = ?) AND ("public"."V_C"."surname" = ?) AND ("public"."V_C"."address" = ?) +) <1> +---- + +<1> The `And Step` realizes itself as a sql `where` clause. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/chooseStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/chooseStep.adoc new file mode 100644 index 000000000..6d7d5cbb5 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/chooseStep.adoc @@ -0,0 +1,72 @@ +==== Choose Step + +link:{tinkerpop-docs}#choose-step[`Choose Step`] + +[source,java,options="nowrap"] +---- +@Test +public void showChooseStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("A") + .choose(__.out(), __.out()) + .path().by("name"); + + printTraversalForm(traversal); + + List paths = traversal.toList(); + for (Path path : paths) { + System.out.println(path); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-choose-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), ChooseStep([VertexStep(OUT,vertex), HasNextStep],{false=[[IdentityStep, EndStep]], true=[[VertexStep(OUT,vertex), EndStep]]}), PathStep([value(name)])] +---- + +[[anchor-after-optimization-choose-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], PathStep([value(name)])] +---- + +.output +---- +[a1, b1] +[a1, b2] +[a2] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" +DEBUG 2018-08-12 19:31:50,944 [main] org.umlg.sqlg.strategy.SqlgSqlExecutor: +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" LEFT JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" +WHERE + ("public"."E_ab"."public.A__O" IS NULL) +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/dropStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/dropStep.adoc new file mode 100644 index 000000000..bcf8af2d3 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/dropStep.adoc @@ -0,0 +1,172 @@ +==== Drop Step + +link:{tinkerpop-docs}#drop-step[`Drop Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testsDropStepTrivial() { + this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a3"); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal().V().hasLabel("A").drop(); + printTraversalForm(traversal); + + traversal.iterate(); + this.sqlgGraph.tx().commit(); + + assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-drop-step-trivial]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), DropStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-drop-step-trivial]] +.After optimization +---- +[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier] +---- + +.sql +[source,sql,options="nowrap"] +---- +TRUNCATE ONLY "public"."V_A" <1> +---- + +<1> As vertex label 'A' has no in or out edges nor are there any predicates the `TRUNCATE` command is used. + +[source,java,options="nowrap"] +---- +@Test +public void testsDropStepWithHas() { + this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a3"); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .has("name", P.within("a1", "a2")) + .drop(); + printTraversalForm(traversal); + + traversal.iterate(); + this.sqlgGraph.tx().commit(); + + assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-drop-step-with-has]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A), name.within([a1, a2])]), DropStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-drop-step-with-has]] +.After optimization +---- +[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier] +---- + +.sql +[source,sql,options="nowrap"] +---- +WITH todelete AS ( +SELECT + "public"."V_A"."ID" AS "alias1" +FROM + "public"."V_A" +WHERE + ( "public"."V_A"."name" in (?, ?)) +) +DELETE FROM "public"."V_A" a USING todelete +WHERE a."ID" = todelete."alias1" <1> +---- + +<1> `DELETE` with a where clause. + +[source,java,options="nowrap"] +---- +@Test +public void testDropStepWithEdges() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + a1.addEdge("ab", b3); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal().V().hasLabel("A").out().drop(); + printTraversalForm(traversal); + + traversal.iterate(); + this.sqlgGraph.tx().commit(); + + assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("B").count().next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-drop-step-with-edges]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), DropStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-drop-step-with-edges]] +.After optimization +---- +[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier] +---- + +.sql +[source,sql,options="nowrap"] +---- +SET CONSTRAINTS ALL DEFERRED <1> + +WITH todelete AS ( +SELECT + "public"."V_B"."ID" AS "alias1" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" +) +DELETE FROM "public"."V_B" a USING todelete +WHERE a."ID" = todelete."alias1" <2> + +WITH todelete AS ( +SELECT + "public"."E_ab"."ID" AS "alias1" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" LEFT JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" +WHERE + ("public"."V_B"."ID" IS NULL) AND + ("public"."E_ab"."public.B__I" IS NOT NULL) +) +DELETE FROM "public"."E_ab" a USING todelete +WHERE a."ID" = todelete."alias1" <3> + +SET CONSTRAINTS ALL IMMEDIATE <4> +---- + +<1> On postgresql we defer (disable) the foreign key constraints. +<2> Delete the 'B' vertices first. As the edge constraints are disabled this is possible. +<3> Delete the edges. +<4>. Enable the foreign key constraints. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/graphStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/graphStep.adoc new file mode 100644 index 000000000..71619b3ea --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/graphStep.adoc @@ -0,0 +1,71 @@ +==== Graph Step + +The link:{tinkerpop-docs}#graph-step[Graph Step] is the start of any traversal. +Sqlg optimizes the graph step by analysing subsequent steps and if possible folding them into a few steps as possible. +Often into only one step, `SqlgGraphStepCompiled`. + +[source,java,options="nowrap"] +---- +@Test +public void showGraphStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b2.addEdge("bc", c2); + this.sqlgGraph.tx().commit(); + + GraphTraversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .out() + .out(); + System.out.println(traversal); + traversal.hasNext(); + System.out.println(traversal); + List c = traversal.toList(); + assertEquals(2, c.size()); +} +---- + +[options="nowrap"] +[[anchor-before-optimization]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex)] +---- + +[[anchor-after-optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]] +---- + +The <> output shows the steps that will execute if no optimization is performed. +Without optimization the query `this.sqlgGraph.traversal().V().hasLabel("A").out().out()` will +first get the `A` s, then for each `A` the `B` s and then for each `B` the `C` s. In the above example unoptimized it +would be at least five round trips to the db. Optimized it is only one trip to the db. + +For an embedded db like HSQLDB this is still ok but for a database server like postgresql the performance impact is +significant. + +<> there is only one link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] step. +All the steps have been folded into one step. + +The link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] will generate the following `sql` to retrieve the data. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/hasStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/hasStep.adoc new file mode 100644 index 000000000..2538d3865 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/hasStep.adoc @@ -0,0 +1,146 @@ +==== Has Step + +link:{tinkerpop-docs}#has-step[`Has Step`]s are folded into the <> or <>. + +[source,java,options="nowrap"] +---- +@Test +public void showHasStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b2.addEdge("bc", c2); + this.sqlgGraph.tx().commit(); + + GraphTraversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .out().has("name", "b1") + .out(); + System.out.println(traversal); + traversal.hasNext(); + System.out.println(traversal); + List c = traversal.toList(); + assertEquals(1, c.size()); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-has-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), HasStep([name.eq(b1)]), VertexStep(OUT,vertex)] +---- + +[[anchor-after-optimization-has-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]] +---- + +This example is similar to the <> example except for an additional `HasStep`. +It shows the two `Vertex Step` s and the `Has Step` being folded into the `SqlgGraphStep`. + +The link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] will generate the following `sql` to retrieve the data. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE + ( "public"."V_B"."name" = ?) <1> +---- + +<1> The `Has Step` realizes itself as a sql `where` clause. + +==== Or Step + +link:{tinkerpop-docs}#or-step[`Or Step`]s are folded into the <> or <>. + +[source,java,options="nowrap"] +---- +@Test +public void showOrStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4"); + Vertex c5 = this.sqlgGraph.addVertex(T.label, "C", "name", "c5"); + Vertex c6 = this.sqlgGraph.addVertex(T.label, "C", "name", "c6"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b2.addEdge("bc", c2); + b2.addEdge("bc", c3); + b2.addEdge("bc", c4); + b2.addEdge("bc", c5); + b2.addEdge("bc", c6); + this.sqlgGraph.tx().commit(); + + GraphTraversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .out() + .out() + .or( + __.has("name", "c1"), + __.has("name", "c3"), + __.has("name", "c6") + ); + + System.out.println(traversal); + traversal.hasNext(); + System.out.println(traversal); + List c = traversal.toList(); + assertEquals(3, c.size()); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-or-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex), OrStep([[HasStep([name.eq(c1)])], [HasStep([name.eq(c3)])], [HasStep([name.eq(c6)])]])] +---- + +[[anchor-after-optimization-or-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]] +---- + +This example is similar to the <> example except for an additional `Or Step` which in turn contains three `Has Step` s. +It shows the two `Vertex Step` s the `Or Step` and the `Has Step` s being folded into the `SqlgGraphStep`. + +The link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] will generate the following `sql` to retrieve the data. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE +(("public"."V_C"."name" = ?) OR ("public"."V_C"."name" = ?) OR ("public"."V_C"."name" = ?) +) <1> +---- + +<1> The `Or Step` realizes itself as a sql `where` clause. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/limitStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/limitStep.adoc new file mode 100644 index 000000000..fa77bd5cf --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/limitStep.adoc @@ -0,0 +1,61 @@ +==== Limit Step + +link:{tinkerpop-docs}#limit-step[`Limit Step`] + +Sqlg optimizes `.limit(x)` + +[source,java,options="nowrap"] +---- +@Test +public void testLimitOnVertexLabels() { + for (int i = 0; i < 100; i++) { + this.sqlgGraph.addVertex(T.label, "Person", "name", "person" + i); + } + this.sqlgGraph.tx().commit(); + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("Person") + .order().by("name") + .limit(3) + .values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-limit-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), OrderGlobalStep([[value(name), incr]]), RangeGlobalStep(0,3), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-limit-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], PropertiesStep([name],value)] +---- + +.output +---- +person0 +person1 +person10 +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."ID" AS "alias1", + "public"."V_Person"."name" AS "alias2" +FROM + "public"."V_Person" +ORDER BY + "alias2" ASC +LIMIT 3 OFFSET 0 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/notStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/notStep.adoc new file mode 100644 index 000000000..5553cc00b --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/notStep.adoc @@ -0,0 +1,3 @@ +==== Not Step + +link:{tinkerpop-docs}#not-step[`Not Step`]s are folded into the <> or <>. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/optionalStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/optionalStep.adoc new file mode 100644 index 000000000..96c9f2826 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/optionalStep.adoc @@ -0,0 +1,103 @@ +==== Optional Step + +link:{tinkerpop-docs}#optional-step[`Optional Step`] + +Sqlg optimizes the OptionalStep. + +[source,java,options="nowrap"] +---- +@Test +public void showOptionalStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b1.addEdge("bc", c2); + b1.addEdge("bc", c3); + this.sqlgGraph.tx().commit(); + + List paths = this.sqlgGraph.traversal() + .V().hasLabel("A") + .optional( + __.out().optional( + __.out() + ) + ) + .path().by("name") + .toList(); + for (Path path : paths) { + System.out.println(path); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-optional-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OptionalStep([VertexStep(OUT,vertex), OptionalStep([VertexStep(OUT,vertex)])]), PathStep([value(name)])] +---- + +[[anchor-after-optimization-optional-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], PathStep([value(name)])] +---- + +.output +---- +[a1, b1, c3] +[a1, b1, c2] +[a1, b1, c1] +[a2] +[a1, b2] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4", + "public"."V_B"."ID" AS "alias5", + "public"."V_B"."name" AS "alias6" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" <1> + +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" LEFT JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" +WHERE + ("public"."E_ab"."public.A__O" IS NULL) <2> + +SELECT + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" LEFT JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" +WHERE + ("public"."E_bc"."public.B__O" IS NULL) <3> +---- +<1> Get the 'C's +<2> Get the 'A's that do not have 'B's +<3> Get the 'B's that do not have 'C's \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/orderStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/orderStep.adoc new file mode 100644 index 000000000..efe92ec22 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/orderStep.adoc @@ -0,0 +1,66 @@ +==== Order Step + +link:{tinkerpop-docs}#order-step[`Order Step`] + +Sqlg optimizes the OrderGlobalStep if the data that the order applies to can be retrieved in one sql statement. +If not then order the ordering occurs in java via the OrderGlobalStep as per normal. + +[source,java,options="nowrap"] +---- +@Test +public void testOrderBy() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "a"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "b"); + Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "c"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "a"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "b"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "c"); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal().V().hasLabel("A") + .order().by("name", Order.incr).by("surname", Order.decr); + printTraversalForm(traversal); + + List vertices = traversal.toList(); + for (Vertex v : vertices) { + System.out.println(v.value("name") + " " + v.value("surname")); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-order-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OrderGlobalStep([[value(name), incr], [value(surname), decr]])] +---- + +[[anchor-after-optimization-order-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel]] +---- + +.output +---- +a c +a b +a a +b c +b b +b a +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."surname" AS "alias2", + "public"."V_A"."name" AS "alias3" +FROM + "public"."V_A" +ORDER BY + "alias3" ASC, + "alias2" DESC +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/rangeStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/rangeStep.adoc new file mode 100644 index 000000000..3002c946d --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/rangeStep.adoc @@ -0,0 +1,61 @@ +==== Range Step + +link:{tinkerpop-docs}#range-step[`Range Step`] + +Sqlg optimizes the `RangeGlobalStep` + +[source,java,options="nowrap"] +---- +@Test +public void testRangeOnVertexLabels() { + for (int i = 0; i < 100; i++) { + this.sqlgGraph.addVertex(T.label, "Person", "name", "person" + i); + } + this.sqlgGraph.tx().commit(); + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("Person") + .order().by("name") + .range(1, 4) + .values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-range-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), OrderGlobalStep([[value(name), incr]]), RangeGlobalStep(1,4), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-range-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], PropertiesStep([name],value)] +---- + +.output +---- +person1 +person10 +person11 +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."ID" AS "alias1", + "public"."V_Person"."name" AS "alias2" +FROM + "public"."V_Person" +ORDER BY + "alias2" ASC +LIMIT 3 OFFSET 1 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/countStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/countStep.adoc new file mode 100644 index 000000000..eebd89a1a --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/countStep.adoc @@ -0,0 +1,41 @@ +===== Count Step + +link:{tinkerpop-docs}#count-step[`Count Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testCount() { + this.sqlgGraph.addVertex(T.label, "A", "name", "a"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a"); + this.sqlgGraph.addVertex(T.label, "A", "name", "a"); + this.sqlgGraph.tx().commit(); + DefaultTraversal traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().count(); + printTraversalForm(traversal); + Assert.assertEquals(4, traversal.next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-count-step]] +.Before optimization +---- +[GraphStep(vertex,[]), CountGlobalStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-count-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([count],value), SqlgCountGlobalStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + COUNT(1) +FROM + "public"."V_A" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByCountStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByCountStep.adoc new file mode 100644 index 000000000..dc914dbad --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByCountStep.adoc @@ -0,0 +1,121 @@ +===== Group By and Count Step + +[source,java,options="nowrap"] +---- +@Test +public void testGroupByCount() { + this.sqlgGraph.addVertex(T.label, "A", "name", "a", "age", 1); + this.sqlgGraph.addVertex(T.label, "A", "name", "a", "age", 2); + this.sqlgGraph.addVertex(T.label, "A", "name", "b", "age", 3); + this.sqlgGraph.tx().commit(); + DefaultTraversal> traversal = (DefaultTraversal>) this.sqlgGraph.traversal().V().hasLabel("A") + .group().by("name").by(__.count()); + List> result = traversal.toList(); + Assert.assertEquals(1, result.size()); + Assert.assertTrue(result.get(0).containsKey("a")); + Assert.assertTrue(result.get(0).containsKey("b")); + Assert.assertEquals(2L, result.get(0).get("a"), 0); + Assert.assertEquals(1L, result.get(0).get("b"), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbycount-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), GroupStep(value(name),[CountGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbycount-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + COUNT(1) AS "count", + "public"."V_A"."name" AS "alias1" +FROM + "public"."V_A" +GROUP BY + "public"."V_A"."name" +---- + +[source,java,options="nowrap"] +---- +@Test +public void testDuplicatePathGroupCountQuery() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1", "age", 1); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 1); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 2); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 3); + Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 3); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 1); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 2); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 3); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + a1.addEdge("ab", b3); + a1.addEdge("ab", b4); + a1.addEdge("ac", c1); + a1.addEdge("ac", c2); + a1.addEdge("ac", c3); + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V(a1).out("ab", "ac").group().by("name").by(__.count()); + Assert.assertEquals(2, traversal.getSteps().size()); + Assert.assertTrue(traversal.getSteps().get(0) instanceof SqlgGraphStep); + Assert.assertTrue(traversal.getSteps().get(1) instanceof SqlgGroupStep); + Map result = traversal.next(); + Assert.assertEquals(1, result.size()); + Assert.assertTrue(result.containsKey("b")); + Assert.assertEquals(7, result.get("b"), 0); + Assert.assertFalse(traversal.hasNext()); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbycount2-step]] +.Before optimization +---- +[GraphStep(vertex,[v[public.A:::1]]), VertexStep(OUT,[ab, ac],vertex), GroupStep(value(name),[CountGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbycount2-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + COUNT(1) AS "count", + "public"."V_C"."name" AS "alias1" +FROM + "public"."V_A" INNER JOIN + "public"."E_ac" ON "public"."V_A"."ID" = "public"."E_ac"."public.A__O" INNER JOIN + "public"."V_C" ON "public"."E_ac"."public.C__I" = "public"."V_C"."ID" +WHERE + ( "public"."V_A"."ID" = ?) +GROUP BY + "public"."V_C"."name"; + +SELECT + COUNT(1) AS "count", + "public"."V_B"."name" AS "alias1" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" +WHERE + ( "public"."V_A"."ID" = ?) +GROUP BY + "public"."V_B"."name"; +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByJoinStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByJoinStep.adoc new file mode 100644 index 000000000..a8109bb9a --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByJoinStep.adoc @@ -0,0 +1,69 @@ +===== Group By with join + +[source,java,options="nowrap"] +---- +@Test +public void testGroupOverOnePropertyWithJoin() { + Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "A"); + Vertex address1 = this.sqlgGraph.addVertex(T.label, "Address", "name", "A", "year", 2); + Vertex address2 = this.sqlgGraph.addVertex(T.label, "Address", "name", "A", "year", 4); + Vertex address3 = this.sqlgGraph.addVertex(T.label, "Address", "name", "C", "year", 6); + Vertex address4 = this.sqlgGraph.addVertex(T.label, "Address", "name", "D", "year", 8); + Vertex address5 = this.sqlgGraph.addVertex(T.label, "Address", "name", "D", "year", 7); + Vertex address6 = this.sqlgGraph.addVertex(T.label, "Address", "name", "D", "year", 6); + person.addEdge("livesAt", address1); + person.addEdge("livesAt", address2); + person.addEdge("livesAt", address3); + person.addEdge("livesAt", address4); + person.addEdge("livesAt", address5); + person.addEdge("livesAt", address6); + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) this.sqlgGraph.traversal() + .V().hasLabel("Person") + .out("livesAt") + .group() + .by("name") + .by(__.values("year").max()); + + printTraversalForm(traversal); + + Map result = traversal.next(); + Assert.assertFalse(traversal.hasNext()); + Assert.assertEquals(3, result.size()); + Assert.assertTrue(result.containsKey("A")); + Assert.assertTrue(result.containsKey("C")); + Assert.assertTrue(result.containsKey("D")); + Assert.assertEquals(4, result.get("A"), 0); + Assert.assertEquals(6, result.get("C"), 0); + Assert.assertEquals(8, result.get("D"), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbyjoin-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), VertexStep(OUT,[livesAt],vertex), GroupStep(value(name),[PropertiesStep([year],value), MaxGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbyjoin-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + MAX("public"."V_Address"."year") AS "alias1", + "public"."V_Address"."name" AS "alias2" +FROM + "public"."V_Person" INNER JOIN + "public"."E_livesAt" ON "public"."V_Person"."ID" = "public"."E_livesAt"."public.Person__O" INNER JOIN + "public"."V_Address" ON "public"."E_livesAt"."public.Address__I" = "public"."V_Address"."ID" +GROUP BY + "public"."V_Address"."name" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMaxStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMaxStep.adoc new file mode 100644 index 000000000..1979f5ae0 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMaxStep.adoc @@ -0,0 +1,135 @@ +===== Group By and Max Step + +[source,java,options="nowrap"] +---- + @Test + public void testGroupByLabelMax() { + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 10); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 20); + this.sqlgGraph.addVertex(T.label, "Person", "name", "C", "age", 100); + this.sqlgGraph.addVertex(T.label, "Person", "name", "D", "age", 40); + + this.sqlgGraph.addVertex(T.label, "Dog", "name", "A", "age", 10); + this.sqlgGraph.addVertex(T.label, "Dog", "name", "B", "age", 200); + this.sqlgGraph.addVertex(T.label, "Dog", "name", "C", "age", 30); + this.sqlgGraph.addVertex(T.label, "Dog", "name", "D", "age", 40); + + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().group().by(T.label).by(__.values("age").max()); + printTraversalForm(traversal); + + Map result = traversal.next(); + Assert.assertFalse(traversal.hasNext()); + Assert.assertEquals(2, result.size()); + Assert.assertTrue(result.containsKey("Person")); + Assert.assertTrue(result.containsKey("Dog")); + Assert.assertEquals(100, result.get("Person"), 0); + Assert.assertEquals(200, result.get("Dog"), 0); + } +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbymax-step]] +.Before optimization +---- +[GraphStep(vertex,[]), GroupStep(label,[PropertiesStep([age],value), MaxGlobalStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbymax-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + MAX("public"."V_Person"."age") AS "alias1" +FROM + "public"."V_Person" + +SELECT + MAX("public"."V_Dog"."age") AS "alias1" +FROM + "public"."V_Dog" +---- + +[source,java,options="nowrap"] +---- +@Test +public void testGroupOverTwoPropertiesWithValues() { + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "surname", "C", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "surname", "D", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "surname", "C", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "surname", "E", "age", 4); + this.sqlgGraph.addVertex(T.label, "Person", "name", "C", "surname", "E", "age", 5); + this.sqlgGraph.tx().commit(); + + DefaultTraversal, Integer>> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person") + ., Integer>group() + .by(__.values("name", "surname").fold()) + .by(__.values("age").max()); + + printTraversalForm(traversal); + + Map, Integer> result = traversal.next(); + Assert.assertTrue(result.containsKey(Arrays.asList("A", "C")) || result.containsKey(Arrays.asList("C", "A"))); + Assert.assertTrue(result.containsKey(Arrays.asList("B", "D")) || result.containsKey(Arrays.asList("D", "B"))); + Assert.assertTrue(result.containsKey(Arrays.asList("B", "E")) || result.containsKey(Arrays.asList("E", "B"))); + Assert.assertTrue(result.containsKey(Arrays.asList("C", "E")) || result.containsKey(Arrays.asList("E", "C"))); + Assert.assertEquals(4, result.size()); + Assert.assertFalse(traversal.hasNext()); + + if (result.containsKey(Arrays.asList("A", "C"))) { + Assert.assertEquals(3, result.get(Arrays.asList("A", "C")), 0); + } else { + Assert.assertEquals(3, result.get(Arrays.asList("C", "A")), 0); + } + if (result.containsKey(Arrays.asList("B", "D"))) { + Assert.assertEquals(2, result.get(Arrays.asList("B", "D")), 0); + } else { + Assert.assertEquals(2, result.get(Arrays.asList("D", "B")), 0); + } + if (result.containsKey(Arrays.asList("B", "E"))) { + Assert.assertEquals(4, result.get(Arrays.asList("B", "E")), 0); + } else { + Assert.assertEquals(4, result.get(Arrays.asList("E", "B")), 0); + } + if (result.containsKey(Arrays.asList("C", "E"))) { + Assert.assertEquals(5, result.get(Arrays.asList("C", "E")), 0); + } else { + Assert.assertEquals(5, result.get(Arrays.asList("E", "C")), 0); + } +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbymax2-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep([PropertiesStep([name, surname],value), FoldStep],[PropertiesStep([age],value), MaxGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbymax2-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."surname" AS "alias1", + "public"."V_Person"."name" AS "alias2", + MAX("public"."V_Person"."age") AS "alias3" +FROM + "public"."V_Person" +GROUP BY + "public"."V_Person"."name", + "public"."V_Person"."surname" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMeanStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMeanStep.adoc new file mode 100644 index 000000000..9fe74f11e --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMeanStep.adoc @@ -0,0 +1,50 @@ +===== Group By and Mean Step + +[source,java,options="nowrap"] +---- +@Test +public void testGroupOverOnePropertyMean() { + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4); + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) sqlgGraph.traversal() + .V().hasLabel("Person") + .group().by("name").by(__.values("age").mean()); + printTraversalForm(traversal); + Map result = traversal.next(); + Assert.assertFalse(traversal.hasNext()); + Assert.assertTrue(result.containsKey("A")); + Assert.assertTrue(result.containsKey("B")); + Assert.assertEquals(2.0, result.get("A"), 0D); + Assert.assertEquals(3.0, result.get("B"), 0D); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbymean-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), MeanGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbymean-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."name" AS "alias1", + AVG("public"."V_Person"."age") AS "alias2", COUNT(1) AS "alias2_weight" +FROM + "public"."V_Person" +GROUP BY + "public"."V_Person"."name" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMinStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMinStep.adoc new file mode 100644 index 000000000..30c2f438b --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupByMinStep.adoc @@ -0,0 +1,50 @@ +===== Group By and Min Step + +[source,java,options="nowrap"] +---- +@Test +public void testGroupOverOnePropertyMin() { + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4); + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) sqlgGraph.traversal() + .V().hasLabel("Person") + .group().by("name").by(__.values("age").min()); + printTraversalForm(traversal); + Map result = traversal.next(); + Assert.assertFalse(traversal.hasNext()); + Assert.assertTrue(result.containsKey("A")); + Assert.assertTrue(result.containsKey("B")); + Assert.assertEquals(1, result.get("A"), 0); + Assert.assertEquals(2, result.get("B"), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbymin-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), MinGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbymin-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."name" AS "alias1", + MIN("public"."V_Person"."age") AS "alias2" +FROM + "public"."V_Person" +GROUP BY + "public"."V_Person"."name" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupBySumStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupBySumStep.adoc new file mode 100644 index 000000000..45df76311 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/groupBySumStep.adoc @@ -0,0 +1,50 @@ +===== Group By and Sum Step + +[source,java,options="nowrap"] +---- +@Test +public void testGroupOverOnePropertySum() { + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4); + this.sqlgGraph.tx().commit(); + + DefaultTraversal> traversal = (DefaultTraversal) sqlgGraph.traversal() + .V().hasLabel("Person") + .group().by("name").by(__.values("age").sum()); + printTraversalForm(traversal); + Map result = traversal.next(); + Assert.assertFalse(traversal.hasNext()); + Assert.assertTrue(result.containsKey("A")); + Assert.assertTrue(result.containsKey("B")); + Assert.assertEquals(4, result.get("A"), 0L); + Assert.assertEquals(6, result.get("B"), 0L); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-groupbysum-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), SumGlobalStep])] +---- + +[options="nowrap"] +[[anchor-after-optimization-groupbysum-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_Person"."name" AS "alias1", + SUM("public"."V_Person"."age") AS "alias2" +FROM + "public"."V_Person" +GROUP BY + "public"."V_Person"."name" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/maxStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/maxStep.adoc new file mode 100644 index 000000000..9c6cc351a --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/maxStep.adoc @@ -0,0 +1,42 @@ +===== Max Step + +link:{tinkerpop-docs}#max-step[`Max Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testMax() { + this.sqlgGraph.addVertex(T.label, "Person", "age", 1, "x", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 2, "x", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 3, "x", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 0, "x", 1); + this.sqlgGraph.tx().commit(); + + DefaultTraversal traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").max(); + printTraversalForm(traversal); + Assert.assertEquals(3, traversal.next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-max-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MaxGlobalStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-max-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgMaxGlobalStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + MAX("public"."V_Person"."age") AS "alias1" +FROM + "public"."V_Person" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/meanStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/meanStep.adoc new file mode 100644 index 000000000..ed4813f00 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/meanStep.adoc @@ -0,0 +1,42 @@ +===== Mean Step + +link:{tinkerpop-docs}#mean-step[`Mean Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testMean() { + this.sqlgGraph.addVertex(T.label, "Person", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "age", 0); + this.sqlgGraph.tx().commit(); + DefaultTraversal traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").mean(); + printTraversalForm(traversal); + Double d = traversal.next(); + Assert.assertEquals(1.5, d, 0D); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-mean-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MeanGlobalStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-mean-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgAvgGlobalStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + AVG("public"."V_Person"."age") AS "alias1", COUNT(1) AS "alias1_weight" +FROM + "public"."V_Person" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/minStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/minStep.adoc new file mode 100644 index 000000000..8879b76ce --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/minStep.adoc @@ -0,0 +1,42 @@ +===== Min Step + +link:{tinkerpop-docs}#min-step[`Min Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testMin() { + this.sqlgGraph.addVertex(T.label, "Person", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "age", 0); + this.sqlgGraph.tx().commit(); + + DefaultTraversal traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").min(); + Assert.assertEquals(0, traversal.next(), 0); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-min-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MinGlobalStep] +---- + + +[options="nowrap"] +[[anchor-after-optimization-min-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgMinGlobalStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + MIN("public"."V_Person"."age") AS "alias1" +FROM + "public"."V_Person" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/sumStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/sumStep.adoc new file mode 100644 index 000000000..cc01d2f12 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducing/sumStep.adoc @@ -0,0 +1,42 @@ +===== Sum Step + +link:{tinkerpop-docs}#sum-step[`Sum Step`] + +[source,java,options="nowrap"] +---- +@Test +public void testSum() { + this.sqlgGraph.addVertex(T.label, "Person", "age", 1); + this.sqlgGraph.addVertex(T.label, "Person", "age", 2); + this.sqlgGraph.addVertex(T.label, "Person", "age", 3); + this.sqlgGraph.addVertex(T.label, "Person", "age", 0); + this.sqlgGraph.tx().commit(); + + DefaultTraversal traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").sum(); + printTraversalForm(traversal); + Assert.assertEquals(6, traversal.next(), 0L); +} +---- + +[options="nowrap"] +[[anchor-before-optimization-sum-step]] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), SumGlobalStep] +---- + +[options="nowrap"] +[[anchor-after-optimization-sum-step]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgSumGlobalStep] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + SUM("public"."V_Person"."age") AS "alias1" +FROM + "public"."V_Person" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducingSteps.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/reducingSteps.adoc new file mode 100644 index 000000000..e69de29bb diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/repeatStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/repeatStep.adoc new file mode 100644 index 000000000..39d96379a --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/repeatStep.adoc @@ -0,0 +1,166 @@ +==== Repeat Step + +link:{tinkerpop-docs}#repeat-step[`Repeat Step`] + +Sqlg optimizes the `RepeatStep` so long as the `until` modulator is *not* present. +`RepeatStep` can be optimized with the modulator `emit` and `times`. + +===== Repeat Step with emit first + +[source,java,options="nowrap"] +---- +@Test +public void showRepeatStepEmitFirst() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b1.addEdge("bc", c2); + b1.addEdge("bc", c3); + this.sqlgGraph.tx().commit(); + + List paths = this.sqlgGraph.traversal().V().hasLabel("A") + .emit() + .times(2) + .repeat( + __.out() + ) + .path().by("name") + .toList(); + for (Path path : paths) { + System.out.println(path); + } +} +---- + +.output +---- +[a1, b1, c3] +[a1, b1, c2] +[a1, b1, c1] +[a1] +[a2] +[a1, b1] +[a1, b2] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4", + "public"."V_B"."ID" AS "alias5", + "public"."V_B"."name" AS "alias6", + "public"."E_ab"."ID" AS "alias7" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" <1> + +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" <2> + +SELECT + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4", + "public"."E_ab"."ID" AS "alias5" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" <3> +---- +<1> Get the 'A's to emit. +<2> Get the 'B's to emit. +<3> Get the 'C's to emit. + +===== Repeat Step with emit last + +[source,java,options="nowrap"] +---- +@Test +public void showRepeatStepEmitLast() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b1.addEdge("bc", c2); + b1.addEdge("bc", c3); + this.sqlgGraph.tx().commit(); + + List paths = this.sqlgGraph.traversal().V().hasLabel("A") + .repeat( + __.out() + ) + .emit() + .times(2) + .path().by("name") + .toList(); + for (Path path : paths) { + System.out.println(path); + } +} +---- + +.output +---- +[a1, b1, c3] +[a1, b1, c2] +[a1, b1, c1] +[a1, b1] +[a1, b2] +---- + +.sql +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4", + "public"."V_B"."ID" AS "alias5", + "public"."V_B"."name" AS "alias6", + "public"."E_ab"."ID" AS "alias7", + "public"."E_bc"."ID" AS "alias8" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" <1> + +SELECT + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2", + "public"."V_A"."ID" AS "alias3", + "public"."V_A"."name" AS "alias4", + "public"."E_ab"."ID" AS "alias5" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" <2> +---- +<1> Get the 'C's to emit. +<2> Get the 'B's to emit. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/vertexStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/vertexStep.adoc new file mode 100644 index 000000000..d3d341488 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy1/vertexStep.adoc @@ -0,0 +1,58 @@ +==== Vertex Step + +Consecutive link:{tinkerpop-docs}#vertex-steps[`Vertex Step`] are folded into the <>. + +[source,java,options="nowrap"] +---- +@Test +public void showVertexStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + b2.addEdge("bc", c2); + this.sqlgGraph.tx().commit(); + + GraphTraversal traversal = this.sqlgGraph.traversal().V() + .hasLabel("A") + .out() + .out(); + System.out.println(traversal); + traversal.hasNext(); + System.out.println(traversal); + List c = traversal.toList(); + assertEquals(2, c.size()); +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex)] +---- + +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]] +---- + +This example is the same as the <>. It shows the two `Vertex Steps` being folded into the `SqlgGraphStep`. + +The link:{apidocs}/org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`] will generate the following `sql` to retrieve the data. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/andStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/andStep.adoc new file mode 100644 index 000000000..2c8e99406 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/andStep.adoc @@ -0,0 +1,80 @@ +==== And Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2AndStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + a1.addEdge("ab", b1); + a1.addEdge("abb", b1); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + a2.addEdge("abb", b2); + Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + a3.addEdge("abbb", b3); + + Traversal traversal = this.sqlgGraph.traversal().V().hasLabel("A").and( + __.out("ab"), + __.out("abb") + ).values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), AndStep([[VertexStep(OUT,[ab],vertex)], [VertexStep(OUT,[abb],vertex)]]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-and-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgAndStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2),(3, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_abb" ON "public"."V_A"."ID" = "public"."E_abb"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_abb"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2),(3, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" +---- + +.output +---- +a1 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/chooseStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/chooseStep.adoc new file mode 100644 index 000000000..a49a509a2 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/chooseStep.adoc @@ -0,0 +1,54 @@ +==== Choose Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2ChooseStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "a3"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "a4"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V() + .hasLabel("A") + .choose( + v -> v.label().equals("A"), + __.out(), + __.in() + ).values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- + +---- + +[options="nowrap"] +[[anchor-after-optimization-choose-step-strategy2,after optimization]] +.After optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), ChooseStep([LambdaFilterStep(lambda), HasNextStep],{false=[[VertexStep(IN,vertex), EndStep]], true=[[VertexStep(OUT,vertex), EndStep]]}), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgChooseStepBarrier([LambdaFilterStep(lambda)],{false=[[SqlgVertexStep, EndStep]], true=[[SqlgVertexStep@[~gremlin.incidentToAdjacent], EndStep]]}), PropertiesStep([name],value)] +---- + +.output +---- +a4 +a3 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/localStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/localStep.adoc new file mode 100644 index 000000000..dcee9d9f3 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/localStep.adoc @@ -0,0 +1,104 @@ +==== Local Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2LocalStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + Vertex c11 = this.sqlgGraph.addVertex(T.label, "C", "name", "c11"); + Vertex c12 = this.sqlgGraph.addVertex(T.label, "C", "name", "c12"); + Vertex c13 = this.sqlgGraph.addVertex(T.label, "C", "name", "c13"); + Vertex c21 = this.sqlgGraph.addVertex(T.label, "C", "name", "c21"); + Vertex c22 = this.sqlgGraph.addVertex(T.label, "C", "name", "c22"); + Vertex c23 = this.sqlgGraph.addVertex(T.label, "C", "name", "c23"); + Vertex c31 = this.sqlgGraph.addVertex(T.label, "C", "name", "c31"); + Vertex c32 = this.sqlgGraph.addVertex(T.label, "C", "name", "c32"); + Vertex c33 = this.sqlgGraph.addVertex(T.label, "C", "name", "c33"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + a1.addEdge("ab", b3); + b1.addEdge("bc", c11); + b1.addEdge("bc", c12); + b1.addEdge("bc", c13); + b2.addEdge("bc", c21); + b2.addEdge("bc", c22); + b2.addEdge("bc", c23); + b3.addEdge("bc", c31); + b3.addEdge("bc", c32); + b3.addEdge("bc", c33); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V(a1) + .local( + __.out().limit(1).out() + ).values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[v[public.A:::1]]), LocalStep([VertexStep(OUT,vertex), RangeGlobalStep(0,1), VertexStep(OUT,vertex)]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-local-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], LocalStep([SqlgVertexStep@[sqlgPathOrderRangeLabel], SqlgVertexStep@[sqlgPathFakeLabel]]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- + +---- + +.output +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" +WHERE + ( "public"."V_A"."ID" = ?) + +SELECT + 1 as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" +WHERE + "public"."V_A"."ID" = 1 +ORDER BY + "index" +LIMIT 1 OFFSET 0 <1> + +SELECT + 1 as "index", + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE + "public"."V_B"."ID" = 1 +ORDER BY + "index" +---- + +<1> In this case the query is simple enough for the `LIMIT` to be executed on the database. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/notStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/notStep.adoc new file mode 100644 index 000000000..6098fbf85 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/notStep.adoc @@ -0,0 +1,63 @@ +==== Not Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2NotStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + a1.addEdge("ab", b1); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("A") + .not( + __.out() + ).values("name"); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), NotStep([VertexStep(OUT,vertex)]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-not-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgNotStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" +---- + +.output +---- +a2 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/optionalStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/optionalStep.adoc new file mode 100644 index 000000000..b43f2ad63 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/optionalStep.adoc @@ -0,0 +1,99 @@ +==== Optional Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2OptionalStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + b1.addEdge("bc", c1); + + + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("A") + .optional( + __.repeat( + __.out() + ).times(2) + ) + .values("name"); + printTraversalForm(traversal); + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OptionalStep([RepeatStep([VertexStep(OUT,vertex), RepeatEndStep],until(loops(2)),emit(false))]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-repeat-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgOptionalStepBarrier([SqlgRepeatStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel], SqlgRepeatEndStepBarrier],until(loops(2)),emit(false))]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" + +SELECT + 3 as "index", + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE + "public"."V_B"."ID" = 2 +ORDER BY + "index" + +SELECT + 4 as "index", + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" +WHERE + "public"."V_B"."ID" = 1 +ORDER BY + "index" +---- + +.output +---- +a2 +c1 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/orStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/orStep.adoc new file mode 100644 index 000000000..19d9d1036 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/orStep.adoc @@ -0,0 +1,100 @@ +==== Or Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2OrStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + a1.addEdge("ab", b1); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + a2.addEdge("abb", b2); + Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + a3.addEdge("abbb", b3); + Vertex a4 = this.sqlgGraph.addVertex(T.label, "A", "name", "a4"); + Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b4"); + a4.addEdge("abbbb", b4); + + + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("A") + .or( + __.out("ab"), + __.out("abb"), + __.out("abbb") + ).values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OrStep([[VertexStep(OUT,[ab],vertex)], [VertexStep(OUT,[abb],vertex)], [VertexStep(OUT,[abbb],vertex)]]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-or-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgOrStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2),(3, 3),(4, 4)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_abb" ON "public"."V_A"."ID" = "public"."E_abb"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_abb"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(2, 1),(3, 2),(4, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_abbb" ON "public"."V_A"."ID" = "public"."E_abbb"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_abbb"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(3, 1),(4, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" +---- + +.output +---- +a1 +a2 +a3 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/repeatStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/repeatStep.adoc new file mode 100644 index 000000000..c63f43f1d --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/repeatStep.adoc @@ -0,0 +1,177 @@ +==== Repeat Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2RepeatStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b4"); + Vertex b5 = this.sqlgGraph.addVertex(T.label, "B", "name", "b5"); + Vertex b6 = this.sqlgGraph.addVertex(T.label, "B", "name", "b6"); + Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1"); + Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2"); + Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3"); + Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4"); + Vertex x = this.sqlgGraph.addVertex(T.label, "X", "name", "hallo"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + a1.addEdge("ab", b3); + a2.addEdge("ab", b4); + a2.addEdge("ab", b5); + a2.addEdge("ab", b6); + + b1.addEdge("bx", x); + + b4.addEdge("bc", c1); + b4.addEdge("bc", c2); + b4.addEdge("bc", c3); + + c1.addEdge("cx", x); + + this.sqlgGraph.tx().commit(); + + Traversal t = this.sqlgGraph.traversal() + .V().hasLabel("A") + .repeat(__.out()) + .until(__.out().has("name", "hallo")) + .values("name"); + printTraversalForm(t); + + List names = t.toList(); + for (String name: names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), RepeatStep([VertexStep(OUT,vertex), RepeatEndStep],until([VertexStep(OUT,vertex), HasStep([name.eq(hallo)])]),emit(false)), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-repeat-step-strategy1,after optimization]] +.After optimization +---- +post-strategy:[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgRepeatStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel], SqlgRepeatEndStepBarrier],until([SqlgVertexStep@[sqlgPathFakeLabel]]),emit(false)), PropertiesStep([name],value)] +---- + + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" <1> + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" <2> + +SELECT + "index" as "index", + "public"."V_X"."ID" AS "alias1", + "public"."V_X"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bx" ON "public"."V_B"."ID" = "public"."E_bx"."public.B__O" INNER JOIN + "public"."V_X" ON "public"."E_bx"."public.X__I" = "public"."V_X"."ID" INNER JOIN + (VALUES(3, 1),(2, 2),(1, 3),(6, 4),(5, 5),(4, 6)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId" +WHERE + ( "public"."V_X"."name" = ?) +ORDER BY + "index" <3> + +SELECT + "index" as "index", + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" INNER JOIN + (VALUES(3, 1),(2, 2),(1, 3),(6, 4),(5, 5),(4, 6)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId" +WHERE + ( "public"."V_C"."name" = ?) +ORDER BY + "index" <4> + +SELECT + "index" as "index", + "public"."V_X"."ID" AS "alias1", + "public"."V_X"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bx" ON "public"."V_B"."ID" = "public"."E_bx"."public.B__O" INNER JOIN + "public"."V_X" ON "public"."E_bx"."public.X__I" = "public"."V_X"."ID" INNER JOIN + (VALUES(3, 3),(2, 4),(6, 5),(5, 6),(4, 7)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId" +ORDER BY + "index" <5> + +SELECT + "index" as "index", + "public"."V_C"."ID" AS "alias1", + "public"."V_C"."name" AS "alias2" +FROM + "public"."V_B" INNER JOIN + "public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN + "public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" INNER JOIN + (VALUES(3, 3),(2, 4),(6, 5),(5, 6),(4, 7)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId" +ORDER BY + "index" <6> + +SELECT + "index" as "index", + "public"."V_X"."ID" AS "alias1", + "public"."V_X"."name" AS "alias2" +FROM + "public"."V_C" INNER JOIN + "public"."E_cx" ON "public"."V_C"."ID" = "public"."E_cx"."public.C__O" INNER JOIN + "public"."V_X" ON "public"."E_cx"."public.X__I" = "public"."V_X"."ID" INNER JOIN + (VALUES(3, 7),(2, 8),(1, 9)) AS tmp ("tmpId", "index") ON "public"."V_C"."ID" = tmp."tmpId" +WHERE + ( "public"."V_X"."name" = ?) +ORDER BY + "index" <7> + +SELECT + "index" as "index", + "public"."V_X"."ID" AS "alias1", + "public"."V_X"."name" AS "alias2" +FROM + "public"."V_C" INNER JOIN + "public"."E_cx" ON "public"."V_C"."ID" = "public"."E_cx"."public.C__O" INNER JOIN + "public"."V_X" ON "public"."E_cx"."public.X__I" = "public"."V_X"."ID" INNER JOIN + (VALUES(3, 8),(2, 9)) AS tmp ("tmpId", "index") ON "public"."V_C"."ID" = tmp."tmpId" +ORDER BY + "index" <8> +---- + +<1> Get all the `A` s. +<2> Get all the `B` s for the incoming `A` s. This represent the first `out` iteration of the `repeat`. +<3> The `until` traversal executed for all the incoming `B` s going out to `X`. +<4> The `until` traversal executed for all the incoming `B` s going out to `C`. +<5> Get all the `X` for the incoming `B` s. This is the second `out` iteration of the `repeat`. +<6> Get all the `C` for the incoming `B` s. This is the second `out` iteration of the `repeat`. +<7> The `until` traversal executed for all the incoming `C` s going out to `X`. +<8> Get all the `X` for the incoming `C` s. This is the third `out` iteration of the `repeat`. + +.output +---- +b1 +c1 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/vertexStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/vertexStep.adoc new file mode 100644 index 000000000..1500da49c --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/vertexStep.adoc @@ -0,0 +1,78 @@ +==== Vertex Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2VertexStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + a1.addEdge("ab", b1); + a2.addEdge("ab", b2); + a3.addEdge("ab", b3); + this.sqlgGraph.tx().commit(); + + Traversal t = this.sqlgGraph.traversal() + .V().hasLabel("A") + .limit(2) + .out() + .values("name"); + printTraversalForm(t); + List result = t.toList(); + for (String name : result) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.output +---- +b1 +b2 +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), RangeGlobalStep(0,2), VertexStep(OUT,vertex), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-vertex-step,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], SqlgVertexStep@[sqlgPathFakeLabel], PropertiesStep([name],value)] +---- + +<> shows that there is a link:{apidocs}/org/umlg/sqlg/step/SqlgVertexStep.html[`SqlgVertexStep`] +after the `SqlgGraphStep`. The `SqlgVertexStep` will barrier the incoming `A` s and execute the next traversal for all +the incoming elements in one `sql` statement. + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" +LIMIT 2 OFFSET 0 <1> + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" <2> +---- + +<1> Get all the `A` s. +<2> For all the previously fetched `A` s get the `B` s. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/whereStep.adoc b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/whereStep.adoc new file mode 100644 index 000000000..8fe6cc825 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/gremlin/strategy2/whereStep.adoc @@ -0,0 +1,64 @@ +==== Where Step + +[source,java,options="nowrap"] +---- +@Test +public void testStrategy2WhereStep() { + Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1"); + Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2"); + Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1"); + Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2"); + Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3"); + a1.addEdge("ab", b1); + a1.addEdge("ab", b2); + a1.addEdge("ab", b3); + a2.addEdge("ab", b1); + this.sqlgGraph.tx().commit(); + + Traversal traversal = this.sqlgGraph.traversal() + .V().hasLabel("A") + .where( + __.out() + ).values("name"); + printTraversalForm(traversal); + + List names = traversal.toList(); + for (String name : names) { + System.out.println(name); + } +} +---- + +[options="nowrap"] +.Before optimization +---- +[GraphStep(vertex,[]), HasStep([~label.eq(A)]), TraversalFilterStep([VertexStep(OUT,vertex)]), PropertiesStep([name],value)] +---- + +[options="nowrap"] +[[anchor-after-optimization-where-step-strategy2,after optimization]] +.After optimization +---- +[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgTraversalFilterStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel]]), PropertiesStep([name],value)] +---- + +[source,sql,options="nowrap"] +---- +SELECT + "public"."V_A"."ID" AS "alias1", + "public"."V_A"."name" AS "alias2" +FROM + "public"."V_A" + +SELECT + "index" as "index", + "public"."V_B"."ID" AS "alias1", + "public"."V_B"."name" AS "alias2" +FROM + "public"."V_A" INNER JOIN + "public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN + "public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN + (VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId" +ORDER BY + "index" +---- diff --git a/sqlg-doc/docs/2.1.4/include/indexes.adoc b/sqlg-doc/docs/2.1.4/include/indexes.adoc new file mode 100644 index 000000000..fafe5a318 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/indexes.adoc @@ -0,0 +1,159 @@ +== Indexes + +=== Basic indexing + +Sqlg supports adding a unique or non-unique index to any property or properties. + +To add an index one has to use Sqlg's topology interface. + +[source,java,options="nowrap"] +---- +@Test +public void testIndex() { + VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", new HashMap() {{ + put("name", PropertyType.STRING); + }}); # <1> + Optional namePropertyOptional = personVertexLabel.getProperty("name"); + assertTrue(namePropertyOptional.isPresent()); + Index index = personVertexLabel.ensureIndexExists(IndexType.NON_UNIQUE, Collections.singletonList(namePropertyOptional.get())); $ <2> + this.sqlgGraph.tx().commit(); # <3> + + this.sqlgGraph.addVertex(T.label, "Person", "name", "John"); + List johns = this.sqlgGraph.traversal().V() + .hasLabel("Person") + .has("name", "John") + .toList(); # <4> + + /* This will execute the following sql. + SELECT + "public"."V_Person"."ID" AS "alias1", + "public"."V_Person"."name" AS "alias2" + FROM + "public"."V_Person" + WHERE + ( "public"."V_Person"."name" = ?) + */ # <5> + + assertEquals(1, johns.size()); +} +---- +<1> Create the 'Person' VertexLabel. +<2> On the 'Person' VertexLabel create a non unique index on the 'name' property. +<3> Index creation is transactional on Postgresql. +<4> The given gremlin query will use the index. +<5> The underlying RDBMS will use the index for the executed sql. + +.postgresql V_Person sql definition +image:sqlg/V_Person_name_index.png[image of tinkerpop-classic] + +==== Composite indexes + +It is possible to create composite indexes. + +[source,java,options="nowrap"] +---- +@Test +public void testCompositeIndex() { + VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", new HashMap() {{ + put("firstName", PropertyType.STRING); + put("lastName", PropertyType.STRING); + }}); # <1> + personVertexLabel.ensureIndexExists(IndexType.NON_UNIQUE, new ArrayList<>(personVertexLabel.getProperties().values())); # <2> + this.sqlgGraph.tx().commit(); + this.sqlgGraph.addVertex(T.label, "Person", "firstName", "John", "lastName", "Smith"); + List johnSmiths = this.sqlgGraph.traversal().V() + .hasLabel("Person") + .has("firstName", "John") + .has("lastName", "Smith") + .toList(); + assertEquals(1, johnSmiths.size()); +} +---- +<1> Create the 'Person' VertexLabel with 2 properties, 'firstName' and 'lastName'. +<2> Create a composite index on 'firstName' and 'lastName' + +.postgresql V_Person composite index sql definition +image:sqlg/postgresql_composite_index.png[image of tinkerpop-classic] + +Outside of creating the index Sqlg has no further direct interaction with the index. However gremlin queries with a +`HasStep` targeting a property with an index on it will translate to a sql `where` clause on that property and +the underlying RDBMS will utilize the index. + +[NOTE] +The index does not need to be created upfront. It can be added any time. + + +[[anchor-full-text-indexing, full text indexing]] +=== Full-text indexing + +On postgresql full text indexing is supported. + +[source,java,options="nowrap"] +---- +@Test +public void testFullTextIndex() { + Vertex v0 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "a fat cat sat on a mat and ate a fat rat"); + Vertex v1 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "fatal error"); + Vertex v2 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "error is not fatal"); + + VertexLabel vl = this.sqlgGraph.getTopology().getVertexLabel("public", "Sentence").get(); + vl.ensureIndexExists(IndexType.getFullTextGIN("english"), Collections.singletonList(vl.getProperty("name").get())); <1> + this.sqlgGraph.tx().commit(); + + List vts = this.sqlgGraph.traversal() + .V().hasLabel("Sentence") + .has("name", FullText.fullTextMatch("english", "fat & rat")) <2> + .toList(); + Assert.assertEquals(1, vts.size()); + Assert.assertTrue(vts.contains(v0)); +} +---- +<1> Create a full-text gin index. +<2> Query the full-text index using Sqlg's custom FullText predicate. + +=== Global unique indexing + +Global unique indexing is a way of specifying that multiple properties across different labels are unique. +For every `GlobalUniqueIndex` Sqlg maintains a separate table with a unique index defined on it. +Every property that partakes in the GlobalUniqueIndex will have its value duplicated in this table. +These tables are kept in the `gui_schema` + +[source,java,options="nowrap"] +---- +@Test +public void testPersonAndDogDoNotHaveTheSameName() { + Map properties = new HashMap() {{ + put("name", PropertyType.STRING); + }}; # <1> + VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", properties); # <2> + VertexLabel dogVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Dog", properties); # <3> + PropertyColumn personName = personVertexLabel.getProperty("name").get(); # <4> + PropertyColumn dogName = dogVertexLabel.getProperty("name").get(); # <5> + this.sqlgGraph.getTopology().ensureGlobalUniqueIndexExist(new HashSet() {{ + add(personName); + add(dogName); + }}); # <6> + this.sqlgGraph.tx().commit(); + + this.sqlgGraph.addVertex(T.label, "Person", "name", "Tyson"); # <7> + try { + //This will fail + this.sqlgGraph.addVertex(T.label, "Dog", "name", "Tyson"); # <8> + fail("Duplicate key violation suppose to prevent this from executing"); + } catch (RuntimeException e) { + //swallow + this.sqlgGraph.tx().rollback(); + } +} +---- +<1> A map of the properties to add. +<2> Create the 'Person' VertexLabel with its properties. +<3> Create the 'Dog' VertexLabel with its properties. +<4> Get the `PropertyColumn` for the 'name' property of 'Person'. +<5> Get the `PropertyColumn` for the 'name' property of 'Dog'. +<6> Create the `GlobalUniqueIndex` on the 'name' property of 'Person' and 'Dog'. This will ensure that 'Person's and 'Dog's do not have the same name. +<7> Add a 'Person' with the name "Tyson". +<8> Try to add a 'Dog' with the name "Tyson". This will fail as the `GlobalUniqueIndex' will prevent 'Person's and 'Dog's from having the same name. + + +GlobalUniqueIndexes do not support composite indexes. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/introduction.adoc b/sqlg-doc/docs/2.1.4/include/introduction.adoc new file mode 100644 index 000000000..49ad13b32 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/introduction.adoc @@ -0,0 +1,11 @@ +== Introduction + +Sqlg primary challenge is to reduce latency by combining TinkerPop +{tinkerpop-docs}#graph-traversal-steps[steps] into as few as possible database calls. +The fine-grained nature of graph traversals makes this crucial, +otherwise the database call latency has prohibitively high performance impact. + +Sqlg supports various bulk modes to reduce latency when modifying the graph. + +[NOTE] +Hsqldb and H2 do not suffer the same latency as Postgresql, MSSqlServer and MariaDB as it runs embedded in the jvm. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/license.adoc b/sqlg-doc/docs/2.1.4/include/license.adoc new file mode 100644 index 000000000..866c17635 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/license.adoc @@ -0,0 +1,3 @@ +== License + +image:github/SVG/law.svg[MIT] https://github.com/pietermartin/sqlg/blob/master/LICENSE[MIT] \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/limitations.adoc b/sqlg-doc/docs/2.1.4/include/limitations.adoc new file mode 100644 index 000000000..d3ba740a2 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/limitations.adoc @@ -0,0 +1,3 @@ +== Limitations + +Postgresql schema, table and column names can not be more than 63 characters long. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/multipleJvm.adoc b/sqlg-doc/docs/2.1.4/include/multipleJvm.adoc new file mode 100644 index 000000000..f69533c86 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/multipleJvm.adoc @@ -0,0 +1,19 @@ +== Multiple JVMs + +It is possible to run many Sqlg instances pointing to the same underlying database. These instances can be in the same jvm +but is primarily intended for separate jvm(s) pointing to the same underlying database. + +To make multiple graphs point to the same underlying database it is important to add in the `distributed` property to `sqlg.properties`. + +.sqlg.properties +---- +distributed = true +---- + +[NOTE] +Multiple JVMs is only supported for Postgresql. +Hsqldb and H2 are primarily intended to run embedded so multiple JVMs do not make sense for them. +Multiple JVM support has not been implemented for MariaDB and MSSqlServer. + +Postgresql's https://www.postgresql.org/docs/current/static/sql-notify.html[*notify*] mechanism is used to distribute the cached schema +across multiple JVMs. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/postgresqlPartitioning.adoc b/sqlg-doc/docs/2.1.4/include/postgresqlPartitioning.adoc new file mode 100644 index 000000000..beb39b8d4 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/postgresqlPartitioning.adoc @@ -0,0 +1,170 @@ +== Postgresql Partitioning + +Sqlg supports `postgresql` partitioning. To partition a table it needs to be created upfront using the `Topology` api. +Sqlg currently supports `RANGE`, `LIST` and `HASH` partitions. + +=== Range partitioning + +[source,java,options="nowrap"] +---- +@Test +public void testPartitioningRange() { + Schema publicSchema = this.sqlgGraph.getTopology().getPublicSchema(); # <1> + VertexLabel partitionedVertexLabel = publicSchema.ensurePartitionedVertexLabelExist( + "Measurement", + new LinkedHashMap() {{ + put("date", PropertyType.LOCALDATE); + put("temp", PropertyType.INTEGER); + }}, + ListOrderedSet.listOrderedSet(Collections.singletonList("date")), + PartitionType.RANGE, #<2> + "date"); # <3> + partitionedVertexLabel.ensureRangePartitionExists("measurement1", "'2016-07-01'", "'2016-08-01'"); # <4> + partitionedVertexLabel.ensureRangePartitionExists("measurement2", "'2016-08-01'", "'2016-09-01'"); # <5> + this.sqlgGraph.tx().commit(); + + LocalDate localDate1 = LocalDate.of(2016, 7, 1); + this.sqlgGraph.addVertex(T.label, "Measurement", "date", localDate1); + LocalDate localDate2 = LocalDate.of(2016, 8, 1); + this.sqlgGraph.addVertex(T.label, "Measurement", "date", localDate2); + this.sqlgGraph.tx().commit(); + + Assert.assertEquals(2, this.sqlgGraph.traversal().V().hasLabel("Measurement").count().next(), 0); + Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate1).count().next(), 0); + Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate2).count().next(), 0); + + Partition partition = this.sqlgGraph.getTopology().getPublicSchema().getVertexLabel("Measurement").get().getPartition("measurement1").get(); # <6> + partition.remove(); # <7> + this.sqlgGraph.tx().commit(); + + Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").count().next(), 0); + Assert.assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate1).count().next(), 0); + Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate2).count().next(), 0); + + Assert.assertEquals(1, this.sqlgGraph.topology().V().hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_PARTITION).count().next(), 0); # <8> +} +---- +<1> Get the 'public' schema object. +<2> Indicates a `RANGE` partition. +<3> Create a `VertexLabel` with a range partition on the `date` field. +<4> Create a named partition for the range '2016-07-01' to '2016-08-01'. +<5> Create a named partition for the range '2016-08-01' to '2016-09-01'. +<6> Using the `Topology` api get the `measurement1` partition. +<7> Remove the `measurement1` partition. +<8> Assert that `Sqlg`s topology only has one partition. + +=== List partitioning + +[source,java,options="nowrap"] +---- +//the partitionExpression 'left(lower(name), 1)' is to complex for the query planner to optimize. +//i.e. select * from Cities where name = 'asdasd' willscan all partitions. +@Test +public void testPartitioningList() { + Schema publicSchema = this.sqlgGraph.getTopology().getPublicSchema(); + VertexLabel partitionedVertexLabel = publicSchema.ensurePartitionedVertexLabelExist("Cities", + new LinkedHashMap() {{ + put("name", PropertyType.STRING); + put("population", PropertyType.LONG); + }}, + ListOrderedSet.listOrderedSet(Collections.singletonList("name")), + PartitionType.LIST, # <1> + "left(lower(name), 1)"); # <2> + partitionedVertexLabel.ensureListPartitionExists("Cities_a", "'a'"); # <3> + partitionedVertexLabel.ensureListPartitionExists("Cities_b", "'b'"); + partitionedVertexLabel.ensureListPartitionExists("Cities_c", "'c'"); + partitionedVertexLabel.ensureListPartitionExists("Cities_d", "'d'"); + this.sqlgGraph.tx().commit(); + + this.sqlgGraph.tx().normalBatchModeOn(); + for (int i = 0; i < 100; i++) { + this.sqlgGraph.addVertex(T.label, "Cities", "name", "aasbc", "population", 1000L); + } + this.sqlgGraph.addVertex(T.label, "Cities", "name", "basbc", "population", 1000L); + for (int i = 0; i < 100; i++) { + this.sqlgGraph.addVertex(T.label, "Cities", "name", "casbc", "population", 1000L); + } + this.sqlgGraph.addVertex(T.label, "Cities", "name", "dasbc", "population", 1000L); + this.sqlgGraph.tx().commit(); + + Assert.assertEquals(202, this.sqlgGraph.traversal().V().hasLabel("Cities").count().next(), 0); + Assert.assertEquals(100, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "aasbc").count().next(), 0); + Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "basbc").count().next(), 0); + Assert.assertEquals(100, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "casbc").count().next(), 0); + + Partition partition = this.sqlgGraph.getTopology().getPublicSchema().getVertexLabel("Cities").get().getPartition("Cities_a").get(); + partition.remove(); + this.sqlgGraph.tx().commit(); + + Assert.assertEquals(102, this.sqlgGraph.traversal().V().hasLabel("Cities").count().next(), 0); + Assert.assertEquals(3, this.sqlgGraph.topology().V().hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_PARTITION).count().next(), 0); +} +---- + +<1> Indicates a `LIST` partition. +<2> The partition expression. +<3> Create a named partition for the list entry 'a'. + +=== Hash partitioning + +[source,java,options="nowrap"] +---- +@Test +public void testPartitioningHash() { + VertexLabel vertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensurePartitionedVertexLabelExist( + "A", + new LinkedHashMap<>() {{ + put("uid1", PropertyType.INTEGER); + put("name", PropertyType.STRING); + put("surname", PropertyType.STRING); + }}, + ListOrderedSet.listOrderedSet(List.of("uid1")), + PartitionType.HASH, <1> + "\"uid1\"" <2> + ); + for (int i = 0; i < 10; i++) { + vertexLabel.ensureHashPartitionExists("hashPartition" + i, 10, i); <3> + } + this.sqlgGraph.tx().commit(); + for (int i = 0; i < 1000; i++) { + this.sqlgGraph.addVertex(T.label, "A", "uid1", i, "name", "name" + i, "surname", "surname" + i); + } + this.sqlgGraph.tx().commit(); + Assert.assertEquals(1000, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0); + + Connection connection = this.sqlgGraph.tx().getConnection(); + try (Statement s = connection.createStatement()) { + ResultSet rs = s.executeQuery("select tableoid::regclass as partition_name, count(*) from \"V_A\" group by 1 order by 1;"); <4> + int count = 0; + Map partitionDistributionCount = new HashMap<>(); + while (rs.next()) { + count++; + partitionDistributionCount.put(rs.getString(1), rs.getLong(2)); + } + Assert.assertEquals(10, count); <5> + Assert.assertEquals(10, partitionDistributionCount.size()); + for (int i = 0; i < 10; i++) { + Assert.assertTrue(partitionDistributionCount.containsKey("\"hashPartition" + i + "\"")); + } + Assert.assertEquals(100, partitionDistributionCount.get("\"hashPartition0\""), 0); + Assert.assertEquals(92, partitionDistributionCount.get("\"hashPartition1\""), 0); + Assert.assertEquals(103, partitionDistributionCount.get("\"hashPartition2\""), 0); + Assert.assertEquals(88, partitionDistributionCount.get("\"hashPartition3\""), 0); + Assert.assertEquals(113, partitionDistributionCount.get("\"hashPartition4\""), 0); + Assert.assertEquals(90, partitionDistributionCount.get("\"hashPartition5\""), 0); + Assert.assertEquals(119, partitionDistributionCount.get("\"hashPartition6\""), 0); + Assert.assertEquals(92, partitionDistributionCount.get("\"hashPartition7\""), 0); + Assert.assertEquals(100, partitionDistributionCount.get("\"hashPartition8\""), 0); + Assert.assertEquals(103, partitionDistributionCount.get("\"hashPartition9\""), 0); + } catch (SQLException throwables) { + Assert.fail(throwables.getMessage()); + } + +} +---- + +<1> Indicates a `HASH` partition. +<2> The partition expression. +<3> Create a named partition for the hash entry with it `modulus` and `remainder`. +<4> Fetch the partitions for the assertion +<5> Assert that there are as many partitions as the `modulus` diff --git a/sqlg-doc/docs/2.1.4/include/sqlgui.adoc b/sqlg-doc/docs/2.1.4/include/sqlgui.adoc new file mode 100644 index 000000000..ad5764df5 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/sqlgui.adoc @@ -0,0 +1,86 @@ +== Sqlg ui + +Sqlg includes a basic ui to visualize and delete/remove schema elements. + +[WARNING] +Sqlg' ui is very dangerous as it allows for deletion of schema elements including RDBMS schemas. Use with care and ideally do not expose to a wide audience. + +=== Startup + +The ui uses http://sparkjava.com[*Sparkjava*] as its web framework. + +There are two ways in which to start the ui. + +.Embedded + +To use a completely standalone Jetty you can run the following code, + + //SqlgUI.initialize(); <1> + SqlgUI.initialize(8181); + SqlgUI.set(sqlgGraph); + +<1> The default port is `4567` + +To use an existing embedded Jetty you can use the following setup. + +---- + //Define your filer +FilterHolder filterHolderSqlgUI = contextHandler.addFilter("spark.servlet.SparkFilter", "/sqlg/*", EnumSet.of(DispatcherType.REQUEST)); +filterHolderSqlgUI.setInitParameter("applicationClass", "org.umlg.sqlg.ui.SparkResources"); + +//Websocket servlet +ServletHolder websocketServletHolder = new ServletHolder(new SqlgWebsocketServlet()); +websocketServletHolder.setName("Sqlg-ui websocket servlet"); +contextHandler.addServlet(websocketServletHolder, "/sqlg/data/v1/websocket"); + +... + +SqlgUI.set(sqlgGraph); +---- + +.Webserver + + SparkFilter + spark.servlet.SparkFilter + + applicationClass + com.company.YourApplication + + + + SparkFilter + /* + + +[WARNING] +The webserver mode has not been tested, nor do I know how the websocket will be made to work. + +The ui is accessible at + +---- +http://ip:port/sqlg/v1/ +---- + +=== Authentication + +The ui uses cookie authentication. + +To define the users that are allowed to use the ui there must be a corresponding property in `sqlg.properties` + +---- +sqlg.ui.username.john=john_password +sqlg.ui.username.peter=peter_password +---- + +For the user to be allowed to do any editing there must be the following property in `sqlg.properties` + +---- +sqlg.ui.username.john.edit=true +sqlg.ui.username.peter.edit=true +---- + +There is one additional property which specifies how long the cookie remains valid for. + +---- +sqlg.ui.cookie.expiry=3600 +---- \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/supportedFeatures.adoc b/sqlg-doc/docs/2.1.4/include/supportedFeatures.adoc new file mode 100644 index 000000000..af43aa474 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/supportedFeatures.adoc @@ -0,0 +1,56 @@ +== TinkerPop supported features + +Sqlg version 2.1.4 runs on https://tinkerpop.apache.org[TinkerPop 3.4.10]. + +Sqlg passes TinkerPop's `StructureStandardSuite` and `ProcessStandardSuite` test suites. + +.Graph Features *not* implemented. + +- Computer +- ThreadedTransactions +- Variables + +.Vertex Features *not* implemented. + +- MultiProperties +- MetaProperties +- UserSuppliedIds +- NumericIds +- StringIds +- UuidIds +- CustomIds +- AnyIds + +.Edge Features **not** implemented. + +- UserSuppliedIds +- NumericIds +- StringIds +- UuidIds +- CustomIds +- AnyIds + +.Vertex property features *not* implemented. + +- AddProperty +- RemoveProperty +- UserSuppliedIds +- NumericIds +- StringIds +- UuidIds +- CustomIds +- AnyIds +- MapValues +- MixedListValues +- SerializableValues +- UniformListValues + +.Edge property feature *not* implemented. + +- MapValues +- MixedListValues +- SerializableValues +- UniformListValues + +[NOTE] +Sqlg supports user supplied ids but not quite as defined by TinkerPop. This is explained <>. \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/include/topology.adoc b/sqlg-doc/docs/2.1.4/include/topology.adoc new file mode 100644 index 000000000..03e4945dc --- /dev/null +++ b/sqlg-doc/docs/2.1.4/include/topology.adoc @@ -0,0 +1,331 @@ +[[anchor-topology]] +== Topology + +Sqlg stores the graph's topology information in the graph itself as a graph. +The topology is stored in the `sqlg_schema` schema. + +.UML diagram of Sqlg's topology. +image:uml/topology Class Diagram.png[image of Sqlg's topology] + +TinkerPop has no notion of schema or topology. However any TinkerPop graph has an implicit schema. +Sqlg manages the schema as a first class construct. + +Sqlg follows the normal TinkerPop semantics in that the schema does not need to be defined upfront. +Every graph modification first checks to see if the element's schema (label,name) exists. +If not, it will create the element's schema. For `Postgresql` this works well as it supports transactional schema creation/modification. + +[WARNING] +Hsqldb, H2 and MariaDb do not support transactional schema creation/modification. They will both silently commit the +transaction and continue. This breaks the user's transaction boundaries. For Hsqldb, H2 and MariaDb it is recommended to +create the schema upfront. + +It is possible to query and traverse the topology as a normal TinkerPop graph. +To query the topology the `TopologyStrategy` is used. To facilitate ease of use, `SqlgGraph.topology()` method is added to enable the strategy. +Being able to query the topology is helpful to understand a graph's structure. + +[source,java,options="nowrap"] +---- +@Test +public void showTopologyTraversals() { + Io.Builder builder = GraphSONIo.build(GraphSONVersion.V3_0); <1> + final GraphReader reader = sqlgGraph.io(builder).reader().create(); + try (final InputStream stream = AbstractGremlinTest.class.getResourceAsStream("/tinkerpop-modern-v3d0.json")) { + reader.readGraph(stream, sqlgGraph); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + System.out.println("//All vertex labels"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) # <2> + .forEachRemaining( + v -> System.out.println(v.value(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME)) + ); + + System.out.println("//All edge labels"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) + .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE) # <3> + .forEachRemaining( + v -> System.out.println(v.value(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME)) + ); + + System.out.println("//'person' properties"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) + .has(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME, "person") # <4> + .out(Topology.SQLG_SCHEMA_VERTEX_PROPERTIES_EDGE) # <5> + .forEachRemaining( + v -> { + System.out.print(v.value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : "); + System.out.println(v.value(Topology.SQLG_SCHEMA_PROPERTY_TYPE)); + } + ); + + System.out.println("//'software' properties"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) + .has(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME, "software") + .out(Topology.SQLG_SCHEMA_VERTEX_PROPERTIES_EDGE) + .forEachRemaining( + v -> { + System.out.print(v.value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : "); + System.out.println(v.value(Topology.SQLG_SCHEMA_PROPERTY_TYPE)); + } + ); + + System.out.println("//'created' properties"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) # <6> + .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE) # <7> + .has(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME, "created") # <8> + .out(Topology.SQLG_SCHEMA_EDGE_PROPERTIES_EDGE) # <9> + .forEachRemaining( + v -> { + System.out.print(v.value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : "); + System.out.println(v.value(Topology.SQLG_SCHEMA_PROPERTY_TYPE)); + } + ); + + System.out.println("//'knows' properties"); + sqlgGraph.topology().V() + .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) + .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE) + .has(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME, "knows") + .out(Topology.SQLG_SCHEMA_EDGE_PROPERTIES_EDGE) + .forEachRemaining( + v -> { + System.out.print(v.value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : "); + System.out.println(v.value(Topology.SQLG_SCHEMA_PROPERTY_TYPE)); + } + ); + +} +---- +<1> Use TinkerPop's i.o. infrastructure to load the modern graph. +<2> Find all VertexLabels, they are in `sqlg_schema.vertex` +<3> Traverse out on the `out_edges` edge to find all the edges. 'WARNING' this may produce duplicates as a single edge label +may have many different distinct out vertex labels. +<4> Find the `person` vertex. +<5> Traverse out on the `vertex_property` edge to find the 'person' vertex labels properties. +<6> Find all vertex labels. i.e. vertices in `sqlg_schema.vertex` +<7> Traverse the `out_edges` edge. +<8> Filter the out edges for only the 'created' edges. +<9> Traverse the `edge_properties` edge to find the 'created' edge's properties. + + +.output +---- +//All vertex labels +person +software +//All edge labels +knows +created +//'person' properties +name : STRING +age : INTEGER +//'software' properties +name : STRING +lang : STRING +//'created' properties +weight : DOUBLE +//'knows' properties +weight : DOUBLE +---- + + +=== Topology eager creation + +It is often useful to create the topology upfront. The topology creation api is accessed via the `Topology` object. +It is a singleton. `Topology topology = sqlgGraph.getTopology();` +To create new topology objects use the `ensureXXX` methods. They will return the a topology object representing the specific +topology element. i.e. `Schema`, `VertexLabel`, `EdgeLabel`, `PropertyColumn`, `Index` or `GlobalUniqueIndex` + +[NOTE] +The `ensureXXX` methods will create the topology object if it does not exists. +If it does exist it will simply return the relevant topology object. +On any topology object one can call `isCommitted` or `isUncommitted` to check the state of the object. +`committed` indicates that it already exists. `uncommitted` indicates that it has been created in the current active transaction. + +.eg +[source,java,options="nowrap"] +---- +@Test +public void createModernTopology() { + Topology topology = this.sqlgGraph.getTopology(); # <1> + VertexLabel personVertexLabel = topology.ensureVertexLabelExist("public", "person", new HashMap() {{ + put("name", PropertyType.STRING); + put("age", PropertyType.INTEGER); + }}); # <2> + VertexLabel softwareVertexLabel = topology.ensureVertexLabelExist("public", "software", new HashMap() {{ + put("name", PropertyType.STRING); + put("lang", PropertyType.STRING); + }}); + EdgeLabel createdEdgeLabel = personVertexLabel.ensureEdgeLabelExist("created", softwareVertexLabel, new HashMap() {{ + put("weight", PropertyType.DOUBLE); + }}); # <3> + EdgeLabel knowsEdgeLabel = personVertexLabel.ensureEdgeLabelExist("knows", personVertexLabel, new HashMap() {{ + put("weight", PropertyType.DOUBLE); + }}); + this.sqlgGraph.tx().commit(); # <4> +} +---- +<1> Get the `Topology` object. +<2> Create the 'person' VertexLabel. The `HashMap` defines the 'person''s properties. +<3> Create the 'created' EdgeLabel. The format is outVertexLabel.ensureEdgeLabelExist(name, inVertexLabel, properties) +<4> Be sure to commit the transaction. Postgresql and MSSqlServer supports transactional schema creation. Hsqldb,H2 and MariaDB do not. + +[source,java,options="nowrap"] +---- +@Test +public void generalTopologyCreationWithSchema() { + Schema schema = this.sqlgGraph.getTopology().ensureSchemaExist("Humans"); # <1> + VertexLabel personVertexLabel = schema.ensureVertexLabelExist("Person", new HashMap() {{ + put("name", PropertyType.STRING); + put("date", PropertyType.LOCALDATE); + }}); # <2> + this.sqlgGraph.tx().commit(); +} +---- +<1> Create the 'Humans' schema +<2> Create the 'Person' VertexLabel via the Schema object. + +Sqlg keeps an in-memory cache of the graphs entire topology. It is possible query this cache directly. + +[source,java,options="nowrap"] +---- +@Test +public void queryCache() { + loadModern(); + Optional publicSchema = this.sqlgGraph.getTopology().getSchema(this.sqlgGraph.getSqlDialect().getPublicSchema()); # <1> + assertTrue(publicSchema.isPresent()); + Schema publicSchemaViaShortCut = this.sqlgGraph.getTopology().getPublicSchema(); # <2> + Optional personVertexLabel = publicSchema.get().getVertexLabel("person"); # <3> + assertTrue(personVertexLabel.isPresent()); + Optional createEdgeLabel = personVertexLabel.get().getOutEdgeLabel("created"); # <4> + assertTrue(createEdgeLabel.isPresent()); + Optional knowsEdgeLabel = personVertexLabel.get().getOutEdgeLabel("knows"); # <5> + assertTrue(knowsEdgeLabel.isPresent()); + + Optional namePropertyColumn = personVertexLabel.get().getProperty("name"); # <6> + assertTrue(namePropertyColumn.isPresent()); + assertEquals(PropertyType.STRING, namePropertyColumn.get().getPropertyType()); # <7> + Optional agePropertyColumn = personVertexLabel.get().getProperty("age"); + assertTrue(agePropertyColumn.isPresent()); + assertEquals(PropertyType.INTEGER, agePropertyColumn.get().getPropertyType()); + Optional weightPropertyColumn = createEdgeLabel.get().getProperty("weight"); + assertTrue(weightPropertyColumn.isPresent()); + assertEquals(PropertyType.DOUBLE, weightPropertyColumn.get().getPropertyType()); +} +---- +<1> Get the 'public' schema object. +<2> Because the 'public' schema will always exist there is a shortcut method to get it. +<3> Use the 'Schema' object the get the 'person' VertexLabel +<4> Use the 'person' VertexLabel to get its 'created' out edge. +<5> Use the 'person' VertexLabel to get its 'knows' out edge. +<6> Use the 'person' VertexLabel to get its 'name' property. Properties are represented by the `PropertyColumn` class. +<7> On the `PropertyColumn` object one can get the `PropertyType`. PropertyType is an enum representing all data types supported by Sqlg. + +[[anchor-user-supplied-identifiers]] +=== User supplied identifiers + +You can define your own identifiers for a VertexLabel or EdgeLabel. This will result in Sqlg generating primary keys on the specified identifiers instead of using an auto generated sequence. + +.eg. +[source,java,options="nowrap"] +---- +@Test +public void testUserSuppliedIds() { + VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist( + "Person", + new LinkedHashMap<>() {{ + put("name", PropertyType.STRING); + put("surname", PropertyType.STRING); + put("nickname", PropertyType.STRING); + }}, + ListOrderedSet.listOrderedSet(Arrays.asList("name", "surname")) # <1> + ); + personVertexLabel.ensureEdgeLabelExist( + "marriedTo", + personVertexLabel, + new LinkedHashMap<>() {{ + put("place", PropertyType.STRING); + put("when", PropertyType.LOCALDATETIME); + }}, + ListOrderedSet.listOrderedSet(List.of("place", "when")) # <2> + ); + this.sqlgGraph.tx().commit(); + + Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John", "surname", "Longfellow", "nickname", "Longboy"); + Vertex sue = this.sqlgGraph.addVertex(T.label, "Person", "name", "Sue", "surname", "Pretty"); + john.addEdge("marriedTo", sue, "place", "Timbuck2", "when", LocalDateTime.now()); + this.sqlgGraph.tx().commit(); + + List marriedTo = this.sqlgGraph.traversal().V().hasLabel("Person") + .has("name", "John") + .out("marriedTo") + .toList(); + Assert.assertEquals(1, marriedTo.size()); + Assert.assertEquals(sue, marriedTo.get(0)); +} +---- +<1> Specify the `name` and `surname` properties as the primary key for the `Person` vertex label. +<2> Specify the `place` and `when` properties as the primary key for the `marriedTo` edge label. + +This will generate a table with `name` and `surname`, and `place` and `when` as composite primary keys. + +[source,sql,options="nowrap"] +---- +CREATE TABLE public."V_Person" +( + name text COLLATE pg_catalog."default" NOT NULL, + surname text COLLATE pg_catalog."default" NOT NULL, + nickname text COLLATE pg_catalog."default", + CONSTRAINT "V_Person_pkey" PRIMARY KEY (name, surname) +); + +CREATE TABLE public."E_marriedTo" +( + place text COLLATE pg_catalog."default" NOT NULL, + "when" timestamp without time zone NOT NULL, + "public.Person.name__I" text COLLATE pg_catalog."default", + "public.Person.surname__I" text COLLATE pg_catalog."default", + "public.Person.name__O" text COLLATE pg_catalog."default", + "public.Person.surname__O" text COLLATE pg_catalog."default", + CONSTRAINT "E_marriedTo_pkey" PRIMARY KEY (place, "when"), + CONSTRAINT "E_marriedTo_public.Person.name__I_public.Person.surname__I_fkey" FOREIGN KEY ("public.Person.name__I", "public.Person.surname__I") + REFERENCES public."V_Person" (name, surname) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + DEFERRABLE, + CONSTRAINT "E_marriedTo_public.Person.name__O_public.Person.surname__O_fkey" FOREIGN KEY ("public.Person.name__O", "public.Person.surname__O") + REFERENCES public."V_Person" (name, surname) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + DEFERRABLE +) +---- + +The gremlin query will execute the following sql, +[source,sql,options="nowrap"] +---- +SELECT + a2."alias1", a2."alias2", a2."alias3" +FROM ( +SELECT + "public"."E_marriedTo"."public.Person.name__I" AS "public.E_marriedTo.public.Person.name__I", + "public"."E_marriedTo"."public.Person.surname__I" AS "public.E_marriedTo.public.Person.surname__I" +FROM + "public"."V_Person" INNER JOIN + "public"."E_marriedTo" ON "public"."V_Person"."name" = "public"."E_marriedTo"."public.Person.name__O" AND "public"."V_Person"."surname" = "public"."E_marriedTo"."public.Person.surname__O" +WHERE + ( "public"."V_Person"."name" = ?) +) a1 INNER JOIN ( +SELECT + "public"."V_Person"."name" AS "alias1", + "public"."V_Person"."surname" AS "alias2", + "public"."V_Person"."nickname" AS "alias3" +FROM + "public"."V_Person" +) a2 ON a1."public.E_marriedTo.public.Person.name__I" = a2."alias1" AND a1."public.E_marriedTo.public.Person.surname__I" = a2."alias2" +---- diff --git a/sqlg-doc/docs/2.1.4/index.html b/sqlg-doc/docs/2.1.4/index.html new file mode 100644 index 000000000..1e9018c37 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/index.html @@ -0,0 +1,7794 @@ + + + + + + + + + + +Sqlg Documentation + + + + + +
+
+
+
+

Sqlg mark github is a implementation of Apache TinkerPop on a +RDBMS. +Currently Postgresql, HSQLDB, H2, +MariaDB, MySQL and +MSSqlServer are supported.

+
+
+

Sqlg has a github discussions page.

+
+
+
+
+

1. Introduction

+
+
+

Sqlg primary challenge is to reduce latency by combining TinkerPop +steps into as few as possible database calls. +The fine-grained nature of graph traversals makes this crucial, +otherwise the database call latency has prohibitively high performance impact.

+
+
+

Sqlg supports various bulk modes to reduce latency when modifying the graph.

+
+
+ + + + + +
+
Note
+
+Hsqldb and H2 do not suffer the same latency as Postgresql, MSSqlServer and MariaDB as it runs embedded in the jvm. +
+
+
+
+
+

2. License

+
+
+

MIT MIT

+
+
+
+
+

3. TinkerPop supported features

+
+
+

Sqlg version 2.1.4 runs on TinkerPop 3.4.10.

+
+
+

Sqlg passes TinkerPop’s StructureStandardSuite and ProcessStandardSuite test suites.

+
+
+
Graph Features not implemented.
+
    +
  • +

    Computer

    +
  • +
  • +

    ThreadedTransactions

    +
  • +
  • +

    Variables

    +
  • +
+
+
+
Vertex Features not implemented.
+
    +
  • +

    MultiProperties

    +
  • +
  • +

    MetaProperties

    +
  • +
  • +

    UserSuppliedIds

    +
  • +
  • +

    NumericIds

    +
  • +
  • +

    StringIds

    +
  • +
  • +

    UuidIds

    +
  • +
  • +

    CustomIds

    +
  • +
  • +

    AnyIds

    +
  • +
+
+
+
Edge Features not implemented.
+
    +
  • +

    UserSuppliedIds

    +
  • +
  • +

    NumericIds

    +
  • +
  • +

    StringIds

    +
  • +
  • +

    UuidIds

    +
  • +
  • +

    CustomIds

    +
  • +
  • +

    AnyIds

    +
  • +
+
+
+
Vertex property features not implemented.
+
    +
  • +

    AddProperty

    +
  • +
  • +

    RemoveProperty

    +
  • +
  • +

    UserSuppliedIds

    +
  • +
  • +

    NumericIds

    +
  • +
  • +

    StringIds

    +
  • +
  • +

    UuidIds

    +
  • +
  • +

    CustomIds

    +
  • +
  • +

    AnyIds

    +
  • +
  • +

    MapValues

    +
  • +
  • +

    MixedListValues

    +
  • +
  • +

    SerializableValues

    +
  • +
  • +

    UniformListValues

    +
  • +
+
+
+
Edge property feature not implemented.
+
    +
  • +

    MapValues

    +
  • +
  • +

    MixedListValues

    +
  • +
  • +

    SerializableValues

    +
  • +
  • +

    UniformListValues

    +
  • +
+
+
+ + + + + +
+
Note
+
+Sqlg supports user supplied ids but not quite as defined by TinkerPop. This is explained below. +
+
+
+
+
+

4. Limitations

+
+
+

Postgresql schema, table and column names can not be more than 63 characters long.

+
+
+
+
+

5. Getting Started

+
+
+

5.1. Maven coordinates

+
+
Postgresql
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-postgres</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
HSQLDB
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-hsqldb</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
H2
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-h2</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MariaDB
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mariadb</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MySQL
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mysql</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MSSqlServer
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mssqlserver</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+

This will include gremlin-groovy. If you have no need for that then use the following coordinates.

+
+
+
Postgresql
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-postgres-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
HSQLDB
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-hsqldb-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
H2
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-h2-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MariaDB
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mariadb-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MySQL
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mysql-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
MSSqlServer
+
+
<dependency>
+    <groupId>org.umlg</groupId>
+    <artifactId>sqlg-mssqlserver-dialect</artifactId>
+    <version>2.1.4</version>
+</dependency>
+
+
+
+
+

5.2. Start

+
+

SqlgGraph is a singleton that can be shared among multiple threads. You instantiate SqlgGraph using the standard +TinkerPop static constructors.

+
+
+
    +
  • +

    Graph g = SqlgGraph.open(final Configuration configuration)

    +
  • +
  • +

    Graph g = SqlgGraph.open(final String pathToSqlgProperties)

    +
  • +
+
+
+

The configuration object requires the following properties.

+
+
+
Postgresql
+
+
jdbc.url=jdbc:postgresql://localhost:5432/yourdb
+jdbc.username=postgres
+jdbc.password=******
+
+
+
+
HSQLDB
+
+
jdbc.url=jdbc:hsqldb:file:/tmp/yourdb
+jdbc.username=SA
+jdbc.password=
+
+
+
+
H2
+
+
jdbc.url=jdbc:h2:file:target/tmp/yourdb
+jdbc.username=SA
+jdbc.password=
+
+
+
+
MariaDB
+
+
jdbc.url=jdbc:mariadb://localhost:3306/?useSSL=false
+jdbc.username=mariadb
+jdbc.password=mariadb
+
+
+
+
MySQL
+
+
jdbc.url=jdbc:mysql://localhost:3306/?useSSL=false
+jdbc.username=mysql
+jdbc.password=mysql
+
+
+
+
MSSqlServer
+
+
jdbc.url=jdbc:sqlserver://localhost:1433;databaseName=yourdb;
+jdbc.username=SA
+jdbc.password=*****
+
+
+
+

In the case of Postgresql and MSSqlServer the database must already exist.

+
+
+

Once you have access to the graph you can use it as per normal.

+
+
+
+
@Test
+public void useAsPerNormal() {
+    Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John");
+    Vertex address = this.sqlgGraph.addVertex(T.label, "Address", "street", "13th");
+    person.addEdge("livesAt", address, "since", LocalDate.of(2010, 1, 21));
+    this.sqlgGraph.tx().commit(); # (1)
+    List<Vertex> addresses = this.sqlgGraph.traversal().V().hasLabel("Person").out("livesAt").toList();
+    assertEquals(1, addresses.size());
+}
+
+
+
+
    +
  1. +

    It is very important to always commit or rollback the transaction. +If you do not, connections to the database will remain open and eventually +the connection pool with run out of connections.

    +
  2. +
+
+
+
+

5.3. Gremlin Console

+
+
Postgresql
+

pieter@pieter-Precision-7510:~/Downloads/tinkerpop-console/apache-tinkerpop-gremlin-console-3.4.10-bin/apache-tinkerpop-gremlin-console-3.4.10/bin/$ ./gremlin.sh

+
+
+
+
         \,,,/
+         (o o)
+-----oOOo-(3)-oOOo-----
+plugin activated: tinkerpop.server
+plugin activated: tinkerpop.utilities
+plugin activated: tinkerpop.tinkergraph
+gremlin> :install org.umlg sqlg-postgres 2.1.4
+==>Loaded: [org.umlg, sqlg-postgres, 2.1.4] - restart the console to use [sqlg.postgres]
+gremlin> :x
+pieter@pieter-Precision-7510:~/Downloads/tinkerpop-console/apache-tinkerpop-gremlin-console-3.4.10-bin/apache-tinkerpop-gremlin-console-3.4.10/bin/$ ./gremlin.sh
+
+
+
+
+
         \,,,/
+         (o o)
+-----oOOo-(3)-oOOo-----
+plugin activated: tinkerpop.server
+plugin activated: tinkerpop.utilities
+plugin activated: tinkerpop.tinkergraph
+gremlin> :plugin list
+==>tinkerpop.server[active]
+==>tinkerpop.gephi
+==>tinkerpop.utilities[active]
+==>tinkerpop.sugar
+==>tinkerpop.credentials
+==>sqlg.postgres
+==>tinkerpop.tinkergraph[active]
+gremlin> :plugin use sqlg.postgres
+==>sqlg.postgres activated
+gremlin> graph = SqlgGraph.open('/pathTo/sqlg.properties')
+==>sqlggraph[SqlGraph] (jdbc:postgresql://localhost:5432/sqlgraphdb) (user = postgres)
+gremlin> g = graph.traversal()
+==>sqlggraphtraversalsource[sqlggraph[SqlGraph] (jdbc:postgresql://localhost:5432/sqlgraphdb) (user = postgres), standard]
+gremlin> graph.io(GraphSONIo.build(GraphSONVersion.V3_0)).readGraph("/pathTo/grateful-dead-v3d0.json")
+==>null
+gremlin> g.V().count()
+==>808
+gremlin>
+
+
+
+
+
+
+

6. Data types

+
+ + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Table Data types
JavaPostgresqlHSQLDBH2MariaDBMSSqlServer

Boolean

BOOLEAN

BOOLEAN

BOOLEAN

BOOLEAN

BIT

Byte

Not supported

TINYINT

TINYINT

TINYINT

TINYINT

Short

SMALLINT

SMALLINT

SMALLINT

SMALLINT

SMALLINT

Integer

INTEGER

INTEGER

INT

INTEGER

INT

Long

BIGINT

BIGINT

BIGINT

BIGINT

BIGINT

Float

REAL

Not supported

REAL

Not supported

REAL

Double

DOUBLE PRECISION

DOUBLE

DOUBLE

DOUBLE

DOUBLE PRECISION

String

TEXT

LONGVARCHAR

VARCHAR

LONGTEXT

VARCHAR(2000)

String (fixed length)

VARCHAR(x)

VARCHAR(x)

VARCHAR(x)

VARCHAR(x)

VARCHAR(x)

Boolean[]

BOOLEAN[]

BOOLEAN ARRAY DEFAULT ARRAY[]

ARRAY

BOOLEAN ARRAY DEFAULT ARRAY[]

Not supported

Byte[]

BYTEA

LONGVARBINARY

BINARY

BLOB

VARBINARY(max)

Short[]

SMALLINT[]

SMALLINT ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

Integer[]

INTEGER[]

INTEGER ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

Long[]

BIGINT[]

BIGINT ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

Float[]

REAL[]

Not supported

ARRAY

Not supported

Not supported

Double[]

DOUBLE PRECISION[]

DOUBLE ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

String[]

TEXT[]

LONGVARCHAR ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.LocalDateTime

TIMESTAMP

TIMESTAMP

TIMESTAMP

DATETIME(3)

DATETIME2(3)

java.time.LocalDate

DATE

DATE

DATE

DATE

DATE

java.time.LocalTime

TIME

TIME

TIME

TIME

TIME

java.time.ZonedDateTime

TIMESTAMP, TEXT

TIMESTAMP, LONGVARCHAR

TIMESTAMP, VARCHAR

DATETIME(3), TINYTEXT

DATETIME2(3), VARCHAR(255)

java.time.Period

INTEGER, INTEGER, INTEGER

INTEGER, INTEGER, INTEGER

INT, INT, INT

INTEGER, INTEGER, INTEGER

INT, INT, INT

java.time.Duration

BIGINT, INTEGER

BIGINT, INTEGER

BIGINT, INT

BIGINT, INTEGER

BIGINT, INT

java.time.LocalDateTime[]

TIMESTAMP[]

TIMESTAMP ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.LocalDate[]

DATE[]

DATE ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.LocalTime[]

TIME[]

TIME ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.ZonedDateTime[]

TIMESTAMP[], TEXT[]

TIMESTAMP ARRAY DEFAULT ARRAY[], LONGVARCHAR ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.Period[]

INTEGER[], INTEGER[], INTEGER[]

INTEGER ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

java.time.Duration[]

BIGINT[], INTEGER[]

BIGINT ARRAY DEFAULT ARRAY[], INTEGER ARRAY DEFAULT ARRAY[]

ARRAY

Not supported

Not supported

com.fasterxml.jackson.databind.JsonNode

JSONB

LONGVARCHAR

VARCHAR

LONGTEXT

VARCHAR(max)

com.fasterxml.jackson.databind.JsonNode[]

JSONB[]

ARRAY

ARRAY

Not supported

Not supported

org.postgis.Point

geometry(POINT)

Not supported

Not supported

Not supported

Not supported

org.umlg.sqlg.gis.GeographyPoint

geography(POINT, 4326)

Not supported

Not supported

Not supported

Not supported

org.postgis.LineString

geometry(LINESTRING)

Not supported

Not supported

Not supported

Not supported

org.postgis.Polygon

geometry(POLYGON)

Not supported

Not supported

Not supported

Not supported

org.umlg.sqlg.gis.GeographyPolygon

geography(POLYGON, 4326)

Not supported

Not supported

Not supported

Not supported

+
+ + + + + +
+
Note
+
+java.time.LocalTime drops the nano second precision. +
+
+
+
+
+

7. Architecture

+
+
+

TinkerPop’s property graph semantics specifies that every vertex and edge has a single label. Modelling this in a RDBMS +is trivial. TinkerPop has no notion of cardinality nor of order. Every relationship between vertex labels is modelled as +many to many relationship with no specified order.

+
+
+

This realizes itself as a classic many to many relationship in a RDBMS database.

+
+
+
+
VertexLabel <---- EdgeLabel ----> VertexLabel
+
+
+
+

7.1. Vertex tables

+
+

Every unique vertex label maps to a table. Vertex tables are prefixed with a V_. i.e. V_Person. The vertex table +stores the vertex’s properties.

+
+
+
+

7.2. Edge tables

+
+

Every unique edge label maps to a table. Edge tables are prefixed with a E_. i.e. E_friend. The edge table stores +each edge’s adjacent vertex ids and the edge properties. The column corresponding to each adjacent vertex id (IN and OUT) +has a foreign key to the adjacent vertex’s table. The foreign key is optional, instead just an index on the adjacent vertex id +can be used.

+
+
+ + + + + +
+
Note
+
+By default, Sqlg will use an auto increment ID bigint for the primary key. You can however use the topology interface to define which properties to use as the primary key. +
+
+
+ + + + + +
+
Note
+
+sqlg.properties implement.foreign.keys = false
+Edge foreign keys have a significant impact on performance.
+Edge foreign keys are enabled by default. +
+
+
+

From a rdbms' perspective each edge table is the classic many to many join table between vertices.

+
+
+
+

7.3. TinkerPop-modern

+
+

Taken from TinkerPop

+
+
+

image of tinkerpop-classic

+
+
+
ER Diagram
+

image of tinkerpop-classic

+
+
+
V_person
+

image of tinkerpop-classic

+
+
+
V_software
+

image of tinkerpop-classic

+
+
+
E_knows
+

image of tinkerpop-classic

+
+
+
E_created
+

image of tinkerpop-classic

+
+
+
+

7.4. Namespacing and Schemas

+
+

Many RDBMS databases have the notion of a schema as a namespace for tables. Sqlg supports schemas +for vertex labels. Distinct schemas for edge tables are unnecessary as edge tables are created in the schema of the adjacent out vertex. +By default schemas for vertex tables go into the underlying databases' default schema. For Postgresql, hsqldb and H2 this +is the public schema.

+
+
+

To specify the schema for a label Sqlg uses the dot . notation.

+
+
+
+
@Test
+public void testElementsInSchema() {
+    Vertex john = this.sqlgGraph.addVertex(T.label, "Manager", "name", "john"); # (1)
+    Vertex palace1 = this.sqlgGraph.addVertex(T.label, "continent.House", "name", "palace1"); # (2)
+    Vertex corrola = this.sqlgGraph.addVertex(T.label, "fleet.Car", "model", "corrola"); # (3)
+    palace1.addEdge("managedBy", john);
+    corrola.addEdge("owner", john);
+    this.sqlgGraph.tx().commit();
+    assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Manager").count().next().intValue()); # (4)
+    assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("House").count().next().intValue()); # (5)
+    assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("continent.House").count().next().intValue()); (6)
+    assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("Car").count().next().intValue());
+    assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("fleet.Car").count().next().intValue());
+    assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("managedBy").count().next().intValue());
+    assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("owner").count().next().intValue());
+}
+
+
+
+
    +
  1. +

    'Manager' will be in the default 'public' schema.

    +
  2. +
  3. +

    'House' will be in the 'continent' schema.

    +
  4. +
  5. +

    'Car' will be in the 'fleet' schema.

    +
  6. +
  7. +

    Vertices in the public schema do not need to be qualified with the schema.

    +
  8. +
  9. +

    Vertices not in the public schema must be qualified with its schema. In this case 'House' will not be found.

    +
  10. +
  11. +

    As 'House' is qualified with the 'continent' schema it will be found.

    +
  12. +
+
+
+

Table V_manager is in the public (default) schema.
+Table V_house is in the continent schema.
+Table V_car is in the fleet schema.
+Table E_managedBy is in the continent schema as its out vertex palace1 is in the continent schema.
+Table E_owner is in the fleet schema as its out vertex is in the `fleet`schema.

+
+
+
postgresql schemas
+

image of tinkerpop-classic +image of tinkerpop-classic +image of tinkerpop-classic +image of tinkerpop-classic

+
+
+

7.4.1. Edge label

+
+

An edge label can have many different out vertex labels. +This means that its possible for a single edge label to be stored in multiple schemas and tables. +One for each distinct out vertex label. Gremlin queries will work as per normal. +However it is possible to target the edges per out vertex schema directly.

+
+
+
eg.
+
+
@Test
+public void testEdgeAcrossSchema() {
+    Vertex a = this.sqlgGraph.addVertex(T.label, "A.A");
+    Vertex b = this.sqlgGraph.addVertex(T.label, "B.B");
+    Vertex c = this.sqlgGraph.addVertex(T.label, "C.C");
+    a.addEdge("specialEdge", b);
+    b.addEdge("specialEdge", c);
+    this.sqlgGraph.tx().commit();
+    assertEquals(2, this.sqlgGraph.traversal().E().hasLabel("specialEdge").count().next().intValue()); # (1)
+    assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("A.specialEdge").count().next().intValue()); # (2)
+    assertEquals(1, this.sqlgGraph.traversal().E().hasLabel("B.specialEdge").count().next().intValue()); # (3)
+}
+
+
+
+
    +
  1. +

    Query 'specialEdge'

    +
  2. +
  3. +

    Query 'specialEdge' with, out vertex labels in the 'A' schema.

    +
  4. +
  5. +

    Query 'specialEdge' with, out vertex labels in the 'B' schema.

    +
  6. +
+
+
+
+
+
+
+

8. Indexes

+
+
+

8.1. Basic indexing

+
+

Sqlg supports adding a unique or non-unique index to any property or properties.

+
+
+

To add an index one has to use Sqlg’s topology interface.

+
+
+
+
@Test
+public void testIndex() {
+    VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", new HashMap<String, PropertyType>() {{
+        put("name", PropertyType.STRING);
+    }}); # (1)
+    Optional<PropertyColumn> namePropertyOptional = personVertexLabel.getProperty("name");
+    assertTrue(namePropertyOptional.isPresent());
+    Index index = personVertexLabel.ensureIndexExists(IndexType.NON_UNIQUE, Collections.singletonList(namePropertyOptional.get())); $ (2)
+    this.sqlgGraph.tx().commit(); # (3)
+
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "John");
+    List<Vertex> johns = this.sqlgGraph.traversal().V()
+            .hasLabel("Person")
+            .has("name", "John")
+            .toList(); # (4)
+
+    /* This will execute the following sql.
+    SELECT
+        "public"."V_Person"."ID" AS "alias1",
+        "public"."V_Person"."name" AS "alias2"
+    FROM
+        "public"."V_Person"
+    WHERE
+        ( "public"."V_Person"."name" = ?)
+    */ # (5)
+
+    assertEquals(1, johns.size());
+}
+
+
+
+
    +
  1. +

    Create the 'Person' VertexLabel.

    +
  2. +
  3. +

    On the 'Person' VertexLabel create a non unique index on the 'name' property.

    +
  4. +
  5. +

    Index creation is transactional on Postgresql.

    +
  6. +
  7. +

    The given gremlin query will use the index.

    +
  8. +
  9. +

    The underlying RDBMS will use the index for the executed sql.

    +
  10. +
+
+
+
postgresql V_Person sql definition
+

image of tinkerpop-classic

+
+
+

8.1.1. Composite indexes

+
+

It is possible to create composite indexes.

+
+
+
+
@Test
+public void testCompositeIndex() {
+    VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", new HashMap<String, PropertyType>() {{
+        put("firstName", PropertyType.STRING);
+        put("lastName", PropertyType.STRING);
+    }}); # (1)
+    personVertexLabel.ensureIndexExists(IndexType.NON_UNIQUE, new ArrayList<>(personVertexLabel.getProperties().values())); # (2)
+    this.sqlgGraph.tx().commit();
+    this.sqlgGraph.addVertex(T.label, "Person", "firstName", "John", "lastName", "Smith");
+    List<Vertex> johnSmiths = this.sqlgGraph.traversal().V()
+            .hasLabel("Person")
+            .has("firstName", "John")
+            .has("lastName", "Smith")
+            .toList();
+    assertEquals(1, johnSmiths.size());
+}
+
+
+
+
    +
  1. +

    Create the 'Person' VertexLabel with 2 properties, 'firstName' and 'lastName'.

    +
  2. +
  3. +

    Create a composite index on 'firstName' and 'lastName'

    +
  4. +
+
+
+
postgresql V_Person composite index sql definition
+

image of tinkerpop-classic

+
+
+

Outside of creating the index Sqlg has no further direct interaction with the index. However gremlin queries with a +HasStep targeting a property with an index on it will translate to a sql where clause on that property and +the underlying RDBMS will utilize the index.

+
+
+ + + + + +
+
Note
+
+The index does not need to be created upfront. It can be added any time. +
+
+
+
+
+

8.2. Full-text indexing

+
+

On postgresql full text indexing is supported.

+
+
+
+
@Test
+public void testFullTextIndex() {
+    Vertex v0 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "a fat cat sat on a mat and ate a fat rat");
+    Vertex v1 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "fatal error");
+    Vertex v2 = this.sqlgGraph.addVertex(T.label, "Sentence", "name", "error is not fatal");
+
+    VertexLabel vl = this.sqlgGraph.getTopology().getVertexLabel("public", "Sentence").get();
+    vl.ensureIndexExists(IndexType.getFullTextGIN("english"), Collections.singletonList(vl.getProperty("name").get())); (1)
+    this.sqlgGraph.tx().commit();
+
+    List<Vertex> vts = this.sqlgGraph.traversal()
+            .V().hasLabel("Sentence")
+            .has("name", FullText.fullTextMatch("english", "fat & rat")) (2)
+            .toList();
+    Assert.assertEquals(1, vts.size());
+    Assert.assertTrue(vts.contains(v0));
+}
+
+
+
+
    +
  1. +

    Create a full-text gin index.

    +
  2. +
  3. +

    Query the full-text index using Sqlg’s custom FullText predicate.

    +
  4. +
+
+
+
+

8.3. Global unique indexing

+
+

Global unique indexing is a way of specifying that multiple properties across different labels are unique. +For every GlobalUniqueIndex Sqlg maintains a separate table with a unique index defined on it. +Every property that partakes in the GlobalUniqueIndex will have its value duplicated in this table. +These tables are kept in the gui_schema

+
+
+
+
@Test
+public void testPersonAndDogDoNotHaveTheSameName() {
+    Map<String, PropertyType> properties = new HashMap<String, PropertyType>() {{
+        put("name", PropertyType.STRING);
+    }}; # (1)
+    VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", properties); # (2)
+    VertexLabel dogVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Dog", properties); # (3)
+    PropertyColumn personName = personVertexLabel.getProperty("name").get(); # (4)
+    PropertyColumn dogName = dogVertexLabel.getProperty("name").get(); # (5)
+    this.sqlgGraph.getTopology().ensureGlobalUniqueIndexExist(new HashSet<PropertyColumn>() {{
+        add(personName);
+        add(dogName);
+    }}); # (6)
+    this.sqlgGraph.tx().commit();
+
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "Tyson"); # (7)
+    try {
+        //This will fail
+        this.sqlgGraph.addVertex(T.label, "Dog", "name", "Tyson"); # (8)
+        fail("Duplicate key violation suppose to prevent this from executing");
+    } catch (RuntimeException e) {
+        //swallow
+        this.sqlgGraph.tx().rollback();
+    }
+}
+
+
+
+
    +
  1. +

    A map of the properties to add.

    +
  2. +
  3. +

    Create the 'Person' VertexLabel with its properties.

    +
  4. +
  5. +

    Create the 'Dog' VertexLabel with its properties.

    +
  6. +
  7. +

    Get the PropertyColumn for the 'name' property of 'Person'.

    +
  8. +
  9. +

    Get the PropertyColumn for the 'name' property of 'Dog'.

    +
  10. +
  11. +

    Create the GlobalUniqueIndex on the 'name' property of 'Person' and 'Dog'. This will ensure that 'Person’s and 'Dog’s do not have the same name.

    +
  12. +
  13. +

    Add a 'Person' with the name "Tyson".

    +
  14. +
  15. +

    Try to add a 'Dog' with the name "Tyson". This will fail as the `GlobalUniqueIndex' will prevent 'Person’s and 'Dog’s from having the same name.

    +
  16. +
+
+
+

GlobalUniqueIndexes do not support composite indexes.

+
+
+
+
+
+

9. Multiple JVMs

+
+
+

It is possible to run many Sqlg instances pointing to the same underlying database. These instances can be in the same jvm +but is primarily intended for separate jvm(s) pointing to the same underlying database.

+
+
+

To make multiple graphs point to the same underlying database it is important to add in the distributed property to sqlg.properties.

+
+
+
sqlg.properties
+
+
distributed = true
+
+
+
+ + + + + +
+
Note
+
+Multiple JVMs is only supported for Postgresql. +Hsqldb and H2 are primarily intended to run embedded so multiple JVMs do not make sense for them. +Multiple JVM support has not been implemented for MariaDB and MSSqlServer. +
+
+
+

Postgresql’s notify mechanism is used to distribute the cached schema +across multiple JVMs.

+
+
+
+
+

10. Gremlin

+
+
+

Sqlg optimizes a gremlin traversal by analyzing the +steps and where possible combining them into custom Sqlg steps. This can +significantly reduce the number of database calls.

+
+
+

Sqlg has two strategies for optimizing TinkerPop steps.

+
+
+
    +
  • +

    Starting with the GraphStep, +consecutive optimizable steps are folded into SqlgGraphStep. This +stops at the first unoptimizable step after which the second strategy is used.

    +
  • +
  • +

    The second strategy is to barrier the incoming elements to the unoptimizable step. This means to exhaust the traversal/iterator +up to the step and cache all the incoming elements for this step. From here the step is executed for all the incoming +elements at once. This strategy effectively changes the semantics to a breath first retrieval.

    +
  • +
+
+
+ + + + + +
+
Note
+
+Optimizing gremlin is an ongoing task as gremlin is a large language. +
+
+
+ + + + + +
+
Note
+
+Turn sql logging on by setting log4j.logger.org.umlg.sqlg=debug +
+
+
+

10.1. Optimization (strategy 1)

+
+

The following steps are optimized. Steps are

+
+ +
+

The combined step will then in turn generate the sql statements to retrieve the data. +It attempts to retrieve the data in as few distinct sql statements as possible.

+
+
+

10.1.1. Graph Step

+
+

The Graph Step is the start of any traversal. +Sqlg optimizes the graph step by analysing subsequent steps and if possible folding them into a few steps as possible. +Often into only one step, SqlgGraphStepCompiled.

+
+
+
+
@Test
+public void showGraphStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b2.addEdge("bc", c2);
+    this.sqlgGraph.tx().commit();
+
+    GraphTraversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .out()
+            .out();
+    System.out.println(traversal);
+    traversal.hasNext();
+    System.out.println(traversal);
+    List<Vertex> c = traversal.toList();
+    assertEquals(2, c.size());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]]
+
+
+
+

The Before optimization output shows the steps that will execute if no optimization is performed. +Without optimization the query this.sqlgGraph.traversal().V().hasLabel("A").out().out() will +first get the A s, then for each A the B s and then for each B the C s. In the above example unoptimized it +would be at least five round trips to the db. Optimized it is only one trip to the db.

+
+
+

For an embedded db like HSQLDB this is still ok but for a database server like postgresql the performance impact is +significant.

+
+
+

After optimization there is only one SqlgGraphStep step. +All the steps have been folded into one step.

+
+
+

The SqlgGraphStep will generate the following sql to retrieve the data.

+
+
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+
+
+
+
+

10.1.2. Vertex Step

+
+

Consecutive Vertex Step are folded into the Graph Step.

+
+
+
+
@Test
+public void showVertexStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b2.addEdge("bc", c2);
+    this.sqlgGraph.tx().commit();
+
+    GraphTraversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .out()
+            .out();
+    System.out.println(traversal);
+    traversal.hasNext();
+    System.out.println(traversal);
+    List<Vertex> c = traversal.toList();
+    assertEquals(2, c.size());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]]
+
+
+
+

This example is the same as the Graph Step. It shows the two Vertex Steps being folded into the SqlgGraphStep.

+
+
+

The SqlgGraphStep will generate the following sql to retrieve the data.

+
+
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+
+
+
+
+

10.1.3. Has Step

+
+

Has Steps are folded into the Graph Step or Vertex Step.

+
+
+
+
@Test
+public void showHasStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b2.addEdge("bc", c2);
+    this.sqlgGraph.tx().commit();
+
+    GraphTraversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .out().has("name", "b1")
+            .out();
+    System.out.println(traversal);
+    traversal.hasNext();
+    System.out.println(traversal);
+    List<Vertex> c = traversal.toList();
+    assertEquals(1, c.size());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), HasStep([name.eq(b1)]), VertexStep(OUT,vertex)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]]
+
+
+
+

This example is similar to the Graph Step example except for an additional HasStep. +It shows the two Vertex Step s and the Has Step being folded into the SqlgGraphStep.

+
+
+

The SqlgGraphStep will generate the following sql to retrieve the data.

+
+
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	( "public"."V_B"."name" = ?) (1)
+
+
+
+
    +
  1. +

    The Has Step realizes itself as a sql where clause.

    +
  2. +
+
+
+
+

10.1.4. Or Step

+
+

Or Steps are folded into the Graph Step or Vertex Step.

+
+
+
+
@Test
+public void showOrStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4");
+    Vertex c5 = this.sqlgGraph.addVertex(T.label, "C", "name", "c5");
+    Vertex c6 = this.sqlgGraph.addVertex(T.label, "C", "name", "c6");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b2.addEdge("bc", c2);
+    b2.addEdge("bc", c3);
+    b2.addEdge("bc", c4);
+    b2.addEdge("bc", c5);
+    b2.addEdge("bc", c6);
+    this.sqlgGraph.tx().commit();
+
+    GraphTraversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .out()
+            .out()
+            .or(
+                    __.has("name", "c1"),
+                    __.has("name", "c3"),
+                    __.has("name", "c6")
+            );
+
+    System.out.println(traversal);
+    traversal.hasNext();
+    System.out.println(traversal);
+    List<Vertex> c = traversal.toList();
+    assertEquals(3, c.size());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex), OrStep([[HasStep([name.eq(c1)])], [HasStep([name.eq(c3)])], [HasStep([name.eq(c6)])]])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]]
+
+
+
+

This example is similar to the Graph Step example except for an additional Or Step which in turn contains three Has Step s. +It shows the two Vertex Step s the Or Step and the Has Step s being folded into the SqlgGraphStep.

+
+
+

The SqlgGraphStep will generate the following sql to retrieve the data.

+
+
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+(("public"."V_C"."name" = ?) OR ("public"."V_C"."name" = ?) OR ("public"."V_C"."name" = ?)
+) (1)
+
+
+
+
    +
  1. +

    The Or Step realizes itself as a sql where clause.

    +
  2. +
+
+
+
+

10.1.5. And Step

+
+

And Steps are folded into the Graph Step or Vertex Step.

+
+
+
+
@Test
+public void showAndStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1", "surname", "x", "address", "y");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2", "surname", "x", "address", "y");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3", "surname", "x", "address", "y");
+    Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4", "surname", "x", "address", "y");
+    Vertex c5 = this.sqlgGraph.addVertex(T.label, "C", "name", "c5", "surname", "x", "address", "y");
+    Vertex c6 = this.sqlgGraph.addVertex(T.label, "C", "name", "c6", "surname", "x", "address", "y");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b2.addEdge("bc", c2);
+    b2.addEdge("bc", c3);
+    b2.addEdge("bc", c4);
+    b2.addEdge("bc", c5);
+    b2.addEdge("bc", c6);
+    this.sqlgGraph.tx().commit();
+
+    GraphTraversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .out()
+            .out()
+            .and(
+                    __.has("name", "c1"),
+                    __.has("surname", "x"),
+                    __.has("address", "y")
+            );
+
+    System.out.println(traversal);
+    traversal.hasNext();
+    System.out.println(traversal);
+    List<Vertex> c = traversal.toList();
+    assertEquals(1, c.size());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), VertexStep(OUT,vertex), AndStep([[HasStep([name.eq(c1)])], [HasStep([surname.eq(x)])], [HasStep([address.eq(y)])]])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel]]
+
+
+
+

This example is similar to the Graph Step example except for an additional And Step which in turn contains three Has Step s. +It shows the two Vertex Step s the And Step and the Has Step s being folded into the SqlgGraphStep.

+
+
+

The SqlgGraphStep will generate the following sql to retrieve the data.

+
+
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."address" AS "alias2",
+	"public"."V_C"."surname" AS "alias3",
+	"public"."V_C"."name" AS "alias4"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+(("public"."V_C"."name" = ?) AND ("public"."V_C"."surname" = ?) AND ("public"."V_C"."address" = ?)
+) (1)
+
+
+
+
    +
  1. +

    The And Step realizes itself as a sql where clause.

    +
  2. +
+
+
+
+

10.1.6. Not Step

+
+

Not Steps are folded into the Graph Step or Vertex Step.

+
+
+
+

10.1.7. Repeat Step

+ +
+

Sqlg optimizes the RepeatStep so long as the until modulator is not present. +RepeatStep can be optimized with the modulator emit and times.

+
+
+
Repeat Step with emit first
+
+
+
@Test
+public void showRepeatStepEmitFirst() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b1.addEdge("bc", c2);
+    b1.addEdge("bc", c3);
+    this.sqlgGraph.tx().commit();
+
+    List<Path> paths = this.sqlgGraph.traversal().V().hasLabel("A")
+            .emit()
+            .times(2)
+            .repeat(
+                    __.out()
+            )
+            .path().by("name")
+            .toList();
+    for (Path path : paths) {
+        System.out.println(path);
+    }
+}
+
+
+
+
output
+
+
[a1, b1, c3]
+[a1, b1, c2]
+[a1, b1, c1]
+[a1]
+[a2]
+[a1, b1]
+[a1, b2]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4",
+	"public"."V_B"."ID" AS "alias5",
+	"public"."V_B"."name" AS "alias6",
+	"public"."E_ab"."ID" AS "alias7"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" (1)
+
+SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A" (2)
+
+SELECT
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4",
+	"public"."E_ab"."ID" AS "alias5"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" (3)
+
+
+
+
    +
  1. +

    Get the 'A’s to emit.

    +
  2. +
  3. +

    Get the 'B’s to emit.

    +
  4. +
  5. +

    Get the 'C’s to emit.

    +
  6. +
+
+
+
+
Repeat Step with emit last
+
+
+
@Test
+public void showRepeatStepEmitLast() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b1.addEdge("bc", c2);
+    b1.addEdge("bc", c3);
+    this.sqlgGraph.tx().commit();
+
+    List<Path> paths = this.sqlgGraph.traversal().V().hasLabel("A")
+            .repeat(
+                    __.out()
+            )
+            .emit()
+            .times(2)
+            .path().by("name")
+            .toList();
+    for (Path path : paths) {
+        System.out.println(path);
+    }
+}
+
+
+
+
output
+
+
[a1, b1, c3]
+[a1, b1, c2]
+[a1, b1, c1]
+[a1, b1]
+[a1, b2]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4",
+	"public"."V_B"."ID" AS "alias5",
+	"public"."V_B"."name" AS "alias6",
+	"public"."E_ab"."ID" AS "alias7",
+	"public"."E_bc"."ID" AS "alias8"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" (1)
+
+SELECT
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4",
+	"public"."E_ab"."ID" AS "alias5"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" (2)
+
+
+
+
    +
  1. +

    Get the 'C’s to emit.

    +
  2. +
  3. +

    Get the 'B’s to emit.

    +
  4. +
+
+
+
+
+

10.1.8. Optional Step

+ +
+

Sqlg optimizes the OptionalStep.

+
+
+
+
@Test
+public void showOptionalStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b1.addEdge("bc", c2);
+    b1.addEdge("bc", c3);
+    this.sqlgGraph.tx().commit();
+
+    List<Path> paths = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .optional(
+                    __.out().optional(
+                            __.out()
+                    )
+            )
+            .path().by("name")
+            .toList();
+    for (Path path : paths) {
+        System.out.println(path);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OptionalStep([VertexStep(OUT,vertex), OptionalStep([VertexStep(OUT,vertex)])]), PathStep([value(name)])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], PathStep([value(name)])]
+
+
+
+
output
+
+
[a1, b1, c3]
+[a1, b1, c2]
+[a1, b1, c1]
+[a2]
+[a1, b2]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4",
+	"public"."V_B"."ID" AS "alias5",
+	"public"."V_B"."name" AS "alias6"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" (1)
+
+SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A" LEFT JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O"
+WHERE
+	("public"."E_ab"."public.A__O" IS NULL) (2)
+
+SELECT
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" LEFT JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O"
+WHERE
+	("public"."E_bc"."public.B__O" IS NULL) (3)
+
+
+
+
    +
  1. +

    Get the 'C’s

    +
  2. +
  3. +

    Get the 'A’s that do not have 'B’s

    +
  4. +
  5. +

    Get the 'B’s that do not have 'C’s

    +
  6. +
+
+
+
+

10.1.9. Choose Step

+ +
+
+
@Test
+public void showChooseStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, Path> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .choose(__.out(), __.out())
+            .path().by("name");
+
+    printTraversalForm(traversal);
+
+    List<Path> paths = traversal.toList();
+    for (Path path : paths) {
+        System.out.println(path);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), ChooseStep([VertexStep(OUT,vertex), HasNextStep],{false=[[IdentityStep, EndStep]], true=[[VertexStep(OUT,vertex), EndStep]]}), PathStep([value(name)])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], PathStep([value(name)])]
+
+
+
+
output
+
+
[a1, b1]
+[a1, b2]
+[a2]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2",
+	"public"."V_A"."ID" AS "alias3",
+	"public"."V_A"."name" AS "alias4"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID"
+DEBUG 2018-08-12 19:31:50,944 [main] org.umlg.sqlg.strategy.SqlgSqlExecutor:
+SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A" LEFT JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O"
+WHERE
+	("public"."E_ab"."public.A__O" IS NULL)
+
+
+
+
+

10.1.10. Order Step

+ +
+

Sqlg optimizes the OrderGlobalStep if the data that the order applies to can be retrieved in one sql statement. +If not then order the ordering occurs in java via the OrderGlobalStep as per normal.

+
+
+
+
@Test
+public void testOrderBy() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "a");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "b");
+    Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a", "surname", "c");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "a");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "b");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "A", "name", "b", "surname", "c");
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V().hasLabel("A")
+            .order().by("name", Order.incr).by("surname", Order.decr);
+    printTraversalForm(traversal);
+
+    List<Vertex> vertices = traversal.toList();
+    for (Vertex v : vertices) {
+        System.out.println(v.value("name") + " " + v.value("surname"));
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OrderGlobalStep([[value(name), incr], [value(surname), decr]])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel]]
+
+
+
+
output
+
+
a c
+a b
+a a
+b c
+b b
+b a
+
+
+
+
sql
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."surname" AS "alias2",
+	"public"."V_A"."name" AS "alias3"
+FROM
+	"public"."V_A"
+ORDER BY
+	 "alias3" ASC,
+	 "alias2" DESC
+
+
+
+
+

10.1.11. Range Step

+ +
+

Sqlg optimizes the RangeGlobalStep

+
+
+
+
@Test
+public void testRangeOnVertexLabels() {
+    for (int i = 0; i < 100; i++) {
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "person" + i);
+    }
+    this.sqlgGraph.tx().commit();
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("Person")
+            .order().by("name")
+            .range(1, 4)
+            .values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), OrderGlobalStep([[value(name), incr]]), RangeGlobalStep(1,4), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], PropertiesStep([name],value)]
+
+
+
+
output
+
+
person1
+person10
+person11
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."ID" AS "alias1",
+	"public"."V_Person"."name" AS "alias2"
+FROM
+	"public"."V_Person"
+ORDER BY
+	 "alias2" ASC
+LIMIT 3 OFFSET 1
+
+
+
+
+

10.1.12. Limit Step

+ +
+

Sqlg optimizes .limit(x)

+
+
+
+
@Test
+public void testLimitOnVertexLabels() {
+    for (int i = 0; i < 100; i++) {
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "person" + i);
+    }
+    this.sqlgGraph.tx().commit();
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("Person")
+            .order().by("name")
+            .limit(3)
+            .values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), OrderGlobalStep([[value(name), incr]]), RangeGlobalStep(0,3), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], PropertiesStep([name],value)]
+
+
+
+
output
+
+
person0
+person1
+person10
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."ID" AS "alias1",
+	"public"."V_Person"."name" AS "alias2"
+FROM
+	"public"."V_Person"
+ORDER BY
+	 "alias2" ASC
+LIMIT 3 OFFSET 0
+
+
+
+
+

10.1.13. Drop Step

+ +
+
+
@Test
+public void testsDropStepTrivial() {
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a3");
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V().hasLabel("A").drop();
+    printTraversalForm(traversal);
+
+    traversal.iterate();
+    this.sqlgGraph.tx().commit();
+
+    assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), DropStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier]
+
+
+
+
sql
+
+
TRUNCATE ONLY "public"."V_A" (1)
+
+
+
+
    +
  1. +

    As vertex label 'A' has no in or out edges nor are there any predicates the TRUNCATE command is used.

    +
  2. +
+
+
+
+
@Test
+public void testsDropStepWithHas() {
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a3");
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .has("name", P.within("a1", "a2"))
+            .drop();
+    printTraversalForm(traversal);
+
+    traversal.iterate();
+    this.sqlgGraph.tx().commit();
+
+    assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A), name.within([a1, a2])]), DropStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier]
+
+
+
+
sql
+
+
WITH todelete AS (
+SELECT
+	"public"."V_A"."ID" AS "alias1"
+FROM
+	"public"."V_A"
+WHERE
+	( "public"."V_A"."name" in (?, ?))
+)
+DELETE FROM "public"."V_A" a USING todelete
+WHERE a."ID" = todelete."alias1" (1)
+
+
+
+
    +
  1. +

    DELETE with a where clause.

    +
  2. +
+
+
+
+
@Test
+public void testDropStepWithEdges() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    a1.addEdge("ab", b3);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, Vertex> traversal = this.sqlgGraph.traversal().V().hasLabel("A").out().drop();
+    printTraversalForm(traversal);
+
+    traversal.iterate();
+    this.sqlgGraph.tx().commit();
+
+    assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("B").count().next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), VertexStep(OUT,vertex), DropStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[]), SqlgDropStepBarrier]
+
+
+
+
sql
+
+
SET CONSTRAINTS ALL DEFERRED (1)
+
+WITH todelete AS (
+SELECT
+	"public"."V_B"."ID" AS "alias1"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID"
+)
+DELETE FROM "public"."V_B" a USING todelete
+WHERE a."ID" = todelete."alias1" (2)
+
+WITH todelete AS (
+SELECT
+	"public"."E_ab"."ID" AS "alias1"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" LEFT JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID"
+WHERE
+	("public"."V_B"."ID" IS NULL) AND
+	("public"."E_ab"."public.B__I" IS NOT NULL)
+)
+DELETE FROM "public"."E_ab" a USING todelete
+WHERE a."ID" = todelete."alias1" (3)
+
+SET CONSTRAINTS ALL IMMEDIATE (4)
+
+
+
+
    +
  1. +

    On postgresql we defer (disable) the foreign key constraints.

    +
  2. +
  3. +

    Delete the 'B' vertices first. As the edge constraints are disabled this is possible.

    +
  4. +
  5. +

    Delete the edges. +<4>. Enable the foreign key constraints.

    +
  6. +
+
+
+
+

10.1.14. Reducing Steps

+ +
+
Min Step
+ +
+
+
@Test
+public void testMin() {
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 0);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Integer> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").min();
+    Assert.assertEquals(0, traversal.next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MinGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgMinGlobalStep]
+
+
+
+
sql
+
+
SELECT
+	MIN("public"."V_Person"."age") AS "alias1"
+FROM
+	"public"."V_Person"
+
+
+
+
+
Max Step
+ +
+
+
@Test
+public void testMax() {
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 1, "x", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 2, "x", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 3, "x", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 0, "x", 1);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Integer> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").max();
+    printTraversalForm(traversal);
+    Assert.assertEquals(3, traversal.next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MaxGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgMaxGlobalStep]
+
+
+
+
sql
+
+
SELECT
+	MAX("public"."V_Person"."age") AS "alias1"
+FROM
+	"public"."V_Person"
+
+
+
+
+
Sum Step
+ +
+
+
@Test
+public void testSum() {
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 0);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Long> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").sum();
+    printTraversalForm(traversal);
+    Assert.assertEquals(6, traversal.next(), 0L);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), SumGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgSumGlobalStep]
+
+
+
+
sql
+
+
SELECT
+	SUM("public"."V_Person"."age") AS "alias1"
+FROM
+	"public"."V_Person"
+
+
+
+
+
Mean Step
+ +
+
+
@Test
+public void testMean() {
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "age", 0);
+    this.sqlgGraph.tx().commit();
+    DefaultTraversal<Vertex, Double> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person").values("age").mean();
+    printTraversalForm(traversal);
+    Double d = traversal.next();
+    Assert.assertEquals(1.5, d, 0D);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), PropertiesStep([age],value), MeanGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([age],value), SqlgAvgGlobalStep]
+
+
+
+
sql
+
+
SELECT
+	AVG("public"."V_Person"."age") AS "alias1", COUNT(1) AS "alias1_weight"
+FROM
+	"public"."V_Person"
+
+
+
+
+
Count Step
+ +
+
+
@Test
+public void testCount() {
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a");
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a");
+    this.sqlgGraph.tx().commit();
+    DefaultTraversal<Vertex, Long> traversal = (DefaultTraversal<Vertex, Long>) this.sqlgGraph.traversal().V().count();
+    printTraversalForm(traversal);
+    Assert.assertEquals(4, traversal.next(), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), CountGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgPropertiesStep([count],value), SqlgCountGlobalStep]
+
+
+
+
sql
+
+
SELECT
+	COUNT(1)
+FROM
+	"public"."V_A"
+
+
+
+
+
Group By
+
+

Group Step's are optimized with sql’s group by clause.

+
+
+
+
Group By and Min Step
+
+
+
@Test
+public void testGroupOverOnePropertyMin() {
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Map<String, Integer>> traversal = (DefaultTraversal) sqlgGraph.traversal()
+            .V().hasLabel("Person")
+            .<String, Integer>group().by("name").by(__.values("age").min());
+    printTraversalForm(traversal);
+    Map<String, Integer> result = traversal.next();
+    Assert.assertFalse(traversal.hasNext());
+    Assert.assertTrue(result.containsKey("A"));
+    Assert.assertTrue(result.containsKey("B"));
+    Assert.assertEquals(1, result.get("A"), 0);
+    Assert.assertEquals(2, result.get("B"), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), MinGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."name" AS "alias1",
+	MIN("public"."V_Person"."age") AS "alias2"
+FROM
+	"public"."V_Person"
+GROUP BY
+	"public"."V_Person"."name"
+
+
+
+
+
Group By and Max Step
+
+
+
    @Test
+    public void testGroupByLabelMax() {
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 10);
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 20);
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "C", "age", 100);
+        this.sqlgGraph.addVertex(T.label, "Person", "name", "D", "age", 40);
+
+        this.sqlgGraph.addVertex(T.label, "Dog", "name", "A", "age", 10);
+        this.sqlgGraph.addVertex(T.label, "Dog", "name", "B", "age", 200);
+        this.sqlgGraph.addVertex(T.label, "Dog", "name", "C", "age", 30);
+        this.sqlgGraph.addVertex(T.label, "Dog", "name", "D", "age", 40);
+
+        this.sqlgGraph.tx().commit();
+
+        DefaultTraversal<Vertex, Map<String, Integer>> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().<String, Integer>group().by(T.label).by(__.values("age").max());
+        printTraversalForm(traversal);
+
+        Map<String, Integer> result = traversal.next();
+        Assert.assertFalse(traversal.hasNext());
+        Assert.assertEquals(2, result.size());
+        Assert.assertTrue(result.containsKey("Person"));
+        Assert.assertTrue(result.containsKey("Dog"));
+        Assert.assertEquals(100, result.get("Person"), 0);
+        Assert.assertEquals(200, result.get("Dog"), 0);
+    }
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), GroupStep(label,[PropertiesStep([age],value), MaxGlobalStep]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	MAX("public"."V_Person"."age") AS "alias1"
+FROM
+	"public"."V_Person"
+
+SELECT
+	MAX("public"."V_Dog"."age") AS "alias1"
+FROM
+	"public"."V_Dog"
+
+
+
+
+
@Test
+public void testGroupOverTwoPropertiesWithValues() {
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "surname", "C", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "surname", "D", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "surname", "C", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "surname", "E", "age", 4);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "C", "surname", "E", "age", 5);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Map<List<String>, Integer>> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V().hasLabel("Person")
+            .<List<String>, Integer>group()
+            .by(__.values("name", "surname").fold())
+            .by(__.values("age").max());
+
+    printTraversalForm(traversal);
+
+    Map<List<String>, Integer> result = traversal.next();
+    Assert.assertTrue(result.containsKey(Arrays.asList("A", "C")) || result.containsKey(Arrays.asList("C", "A")));
+    Assert.assertTrue(result.containsKey(Arrays.asList("B", "D")) || result.containsKey(Arrays.asList("D", "B")));
+    Assert.assertTrue(result.containsKey(Arrays.asList("B", "E")) || result.containsKey(Arrays.asList("E", "B")));
+    Assert.assertTrue(result.containsKey(Arrays.asList("C", "E")) || result.containsKey(Arrays.asList("E", "C")));
+    Assert.assertEquals(4, result.size());
+    Assert.assertFalse(traversal.hasNext());
+
+    if (result.containsKey(Arrays.asList("A", "C"))) {
+        Assert.assertEquals(3, result.get(Arrays.asList("A", "C")), 0);
+    } else {
+        Assert.assertEquals(3, result.get(Arrays.asList("C", "A")), 0);
+    }
+    if (result.containsKey(Arrays.asList("B", "D"))) {
+        Assert.assertEquals(2, result.get(Arrays.asList("B", "D")), 0);
+    } else {
+        Assert.assertEquals(2, result.get(Arrays.asList("D", "B")), 0);
+    }
+    if (result.containsKey(Arrays.asList("B", "E"))) {
+        Assert.assertEquals(4, result.get(Arrays.asList("B", "E")), 0);
+    } else {
+        Assert.assertEquals(4, result.get(Arrays.asList("E", "B")), 0);
+    }
+    if (result.containsKey(Arrays.asList("C", "E"))) {
+        Assert.assertEquals(5, result.get(Arrays.asList("C", "E")), 0);
+    } else {
+        Assert.assertEquals(5, result.get(Arrays.asList("E", "C")), 0);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep([PropertiesStep([name, surname],value), FoldStep],[PropertiesStep([age],value), MaxGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."surname" AS "alias1",
+	"public"."V_Person"."name" AS "alias2",
+	MAX("public"."V_Person"."age") AS "alias3"
+FROM
+	"public"."V_Person"
+GROUP BY
+	"public"."V_Person"."name",
+	"public"."V_Person"."surname"
+
+
+
+
+
Group By and Sum Step
+
+
+
@Test
+public void testGroupOverOnePropertySum() {
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Map<String, Long>> traversal = (DefaultTraversal) sqlgGraph.traversal()
+            .V().hasLabel("Person")
+            .<String, Long>group().by("name").by(__.values("age").sum());
+    printTraversalForm(traversal);
+    Map<String, Long> result = traversal.next();
+    Assert.assertFalse(traversal.hasNext());
+    Assert.assertTrue(result.containsKey("A"));
+    Assert.assertTrue(result.containsKey("B"));
+    Assert.assertEquals(4, result.get("A"), 0L);
+    Assert.assertEquals(6, result.get("B"), 0L);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), SumGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."name" AS "alias1",
+	SUM("public"."V_Person"."age") AS "alias2"
+FROM
+	"public"."V_Person"
+GROUP BY
+	"public"."V_Person"."name"
+
+
+
+
+
Group By and Mean Step
+
+
+
@Test
+public void testGroupOverOnePropertyMean() {
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "A", "age", 3);
+    this.sqlgGraph.addVertex(T.label, "Person", "name", "B", "age", 4);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Map<String, Double>> traversal = (DefaultTraversal) sqlgGraph.traversal()
+            .V().hasLabel("Person")
+            .<String, Double>group().by("name").by(__.values("age").mean());
+    printTraversalForm(traversal);
+    Map<String, Double> result = traversal.next();
+    Assert.assertFalse(traversal.hasNext());
+    Assert.assertTrue(result.containsKey("A"));
+    Assert.assertTrue(result.containsKey("B"));
+    Assert.assertEquals(2.0, result.get("A"), 0D);
+    Assert.assertEquals(3.0, result.get("B"), 0D);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(Person)]), GroupStep(value(name),[PropertiesStep([age],value), MeanGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."name" AS "alias1",
+	AVG("public"."V_Person"."age") AS "alias2", COUNT(1) AS "alias2_weight"
+FROM
+	"public"."V_Person"
+GROUP BY
+	"public"."V_Person"."name"
+
+
+
+
+
Group By and Count Step
+
+
+
@Test
+public void testGroupByCount() {
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a", "age", 1);
+    this.sqlgGraph.addVertex(T.label, "A", "name", "a", "age", 2);
+    this.sqlgGraph.addVertex(T.label, "A", "name", "b", "age", 3);
+    this.sqlgGraph.tx().commit();
+    DefaultTraversal<Vertex, Map<Object, Long>> traversal = (DefaultTraversal<Vertex, Map<Object, Long>>) this.sqlgGraph.traversal().V().hasLabel("A")
+            .<Object, Long>group().by("name").by(__.count());
+    List<Map<Object, Long>> result = traversal.toList();
+    Assert.assertEquals(1, result.size());
+    Assert.assertTrue(result.get(0).containsKey("a"));
+    Assert.assertTrue(result.get(0).containsKey("b"));
+    Assert.assertEquals(2L, result.get(0).get("a"), 0);
+    Assert.assertEquals(1L, result.get(0).get("b"), 0);
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), GroupStep(value(name),[CountGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	COUNT(1) AS "count",
+	"public"."V_A"."name" AS "alias1"
+FROM
+	"public"."V_A"
+GROUP BY
+	"public"."V_A"."name"
+
+
+
+
+
@Test
+public void testDuplicatePathGroupCountQuery() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1", "age", 1);
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 1);
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 2);
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 3);
+    Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b", "age", 3);
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 1);
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 2);
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "b", "age", 3);
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    a1.addEdge("ab", b3);
+    a1.addEdge("ab", b4);
+    a1.addEdge("ac", c1);
+    a1.addEdge("ac", c2);
+    a1.addEdge("ac", c3);
+    this.sqlgGraph.tx().commit();
+
+    DefaultTraversal<Vertex, Map<String, Long>> traversal = (DefaultTraversal) this.sqlgGraph.traversal().V(a1).out("ab", "ac").group().by("name").by(__.count());
+    Assert.assertEquals(2, traversal.getSteps().size());
+    Assert.assertTrue(traversal.getSteps().get(0) instanceof SqlgGraphStep);
+    Assert.assertTrue(traversal.getSteps().get(1) instanceof SqlgGroupStep);
+    Map<String, Long> result = traversal.next();
+    Assert.assertEquals(1, result.size());
+    Assert.assertTrue(result.containsKey("b"));
+    Assert.assertEquals(7, result.get("b"), 0);
+    Assert.assertFalse(traversal.hasNext());
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[v[public.A:::1]]), VertexStep(OUT,[ab, ac],vertex), GroupStep(value(name),[CountGlobalStep])]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathTempFakeLabel], SqlgGroupStep]
+
+
+
+
sql
+
+
SELECT
+	COUNT(1) AS "count",
+	"public"."V_C"."name" AS "alias1"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ac" ON "public"."V_A"."ID" = "public"."E_ac"."public.A__O" INNER JOIN
+	"public"."V_C" ON "public"."E_ac"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	( "public"."V_A"."ID" = ?)
+GROUP BY
+	"public"."V_C"."name";
+
+SELECT
+	COUNT(1) AS "count",
+	"public"."V_B"."name" AS "alias1"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID"
+WHERE
+	( "public"."V_A"."ID" = ?)
+GROUP BY
+	"public"."V_B"."name";
+
+
+
+
+
+
+

10.2. Optimization (strategy 2)

+
+

The following steps are optimized. Steps are

+
+
+ +
+
+

The combined step will then in turn generate the sql statements to retrieve the data. +It attempts to retrieve the data in as few distinct sql statements as possible.

+
+
+

10.2.1. Vertex Step

+
+
+
@Test
+public void testStrategy2VertexStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    a1.addEdge("ab", b1);
+    a2.addEdge("ab", b2);
+    a3.addEdge("ab", b3);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> t = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .limit(2)
+            .out()
+            .values("name");
+    printTraversalForm(t);
+    List<String> result = t.toList();
+    for (String name : result) {
+        System.out.println(name);
+    }
+}
+
+
+
+
output
+
+
b1
+b2
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), RangeGlobalStep(0,2), VertexStep(OUT,vertex), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathOrderRangeLabel], SqlgVertexStep@[sqlgPathFakeLabel], PropertiesStep([name],value)]
+
+
+
+

after optimization shows that there is a SqlgVertexStep +after the SqlgGraphStep. The SqlgVertexStep will barrier the incoming A s and execute the next traversal for all +the incoming elements in one sql statement.

+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+LIMIT 2 OFFSET 0 (1)
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index" (2)
+
+
+
+
    +
  1. +

    Get all the A s.

    +
  2. +
  3. +

    For all the previously fetched A s get the B s.

    +
  4. +
+
+
+
+

10.2.2. Repeat Step

+
+
+
@Test
+public void testStrategy2RepeatStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b4");
+    Vertex b5 = this.sqlgGraph.addVertex(T.label, "B", "name", "b5");
+    Vertex b6 = this.sqlgGraph.addVertex(T.label, "B", "name", "b6");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4");
+    Vertex x = this.sqlgGraph.addVertex(T.label, "X", "name", "hallo");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    a1.addEdge("ab", b3);
+    a2.addEdge("ab", b4);
+    a2.addEdge("ab", b5);
+    a2.addEdge("ab", b6);
+
+    b1.addEdge("bx", x);
+
+    b4.addEdge("bc", c1);
+    b4.addEdge("bc", c2);
+    b4.addEdge("bc", c3);
+
+    c1.addEdge("cx", x);
+
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> t = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .repeat(__.out())
+            .until(__.out().has("name", "hallo"))
+            .values("name");
+    printTraversalForm(t);
+
+    List<String> names = t.toList();
+    for (String name: names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), RepeatStep([VertexStep(OUT,vertex), RepeatEndStep],until([VertexStep(OUT,vertex), HasStep([name.eq(hallo)])]),emit(false)), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
post-strategy:[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgRepeatStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel], SqlgRepeatEndStepBarrier],until([SqlgVertexStep@[sqlgPathFakeLabel]]),emit(false)), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A" (1)
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index" (2)
+
+SELECT
+	"index" as "index",
+	"public"."V_X"."ID" AS "alias1",
+	"public"."V_X"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bx" ON "public"."V_B"."ID" = "public"."E_bx"."public.B__O" INNER JOIN
+	"public"."V_X" ON "public"."E_bx"."public.X__I" = "public"."V_X"."ID" INNER JOIN
+	(VALUES(3, 1),(2, 2),(1, 3),(6, 4),(5, 5),(4, 6)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId"
+WHERE
+	( "public"."V_X"."name" = ?)
+ORDER BY
+	"index" (3)
+
+SELECT
+	"index" as "index",
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" INNER JOIN
+	(VALUES(3, 1),(2, 2),(1, 3),(6, 4),(5, 5),(4, 6)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId"
+WHERE
+	( "public"."V_C"."name" = ?)
+ORDER BY
+	"index" (4)
+
+SELECT
+	"index" as "index",
+	"public"."V_X"."ID" AS "alias1",
+	"public"."V_X"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bx" ON "public"."V_B"."ID" = "public"."E_bx"."public.B__O" INNER JOIN
+	"public"."V_X" ON "public"."E_bx"."public.X__I" = "public"."V_X"."ID" INNER JOIN
+	(VALUES(3, 3),(2, 4),(6, 5),(5, 6),(4, 7)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId"
+ORDER BY
+	"index" (5)
+
+SELECT
+	"index" as "index",
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID" INNER JOIN
+	(VALUES(3, 3),(2, 4),(6, 5),(5, 6),(4, 7)) AS tmp ("tmpId", "index") ON "public"."V_B"."ID" = tmp."tmpId"
+ORDER BY
+	"index" (6)
+
+SELECT
+	"index" as "index",
+	"public"."V_X"."ID" AS "alias1",
+	"public"."V_X"."name" AS "alias2"
+FROM
+	"public"."V_C" INNER JOIN
+	"public"."E_cx" ON "public"."V_C"."ID" = "public"."E_cx"."public.C__O" INNER JOIN
+	"public"."V_X" ON "public"."E_cx"."public.X__I" = "public"."V_X"."ID" INNER JOIN
+	(VALUES(3, 7),(2, 8),(1, 9)) AS tmp ("tmpId", "index") ON "public"."V_C"."ID" = tmp."tmpId"
+WHERE
+	( "public"."V_X"."name" = ?)
+ORDER BY
+	"index" (7)
+
+SELECT
+	"index" as "index",
+	"public"."V_X"."ID" AS "alias1",
+	"public"."V_X"."name" AS "alias2"
+FROM
+	"public"."V_C" INNER JOIN
+	"public"."E_cx" ON "public"."V_C"."ID" = "public"."E_cx"."public.C__O" INNER JOIN
+	"public"."V_X" ON "public"."E_cx"."public.X__I" = "public"."V_X"."ID" INNER JOIN
+	(VALUES(3, 8),(2, 9)) AS tmp ("tmpId", "index") ON "public"."V_C"."ID" = tmp."tmpId"
+ORDER BY
+	"index" (8)
+
+
+
+
    +
  1. +

    Get all the A s.

    +
  2. +
  3. +

    Get all the B s for the incoming A s. This represent the first out iteration of the repeat.

    +
  4. +
  5. +

    The until traversal executed for all the incoming B s going out to X.

    +
  6. +
  7. +

    The until traversal executed for all the incoming B s going out to C.

    +
  8. +
  9. +

    Get all the X for the incoming B s. This is the second out iteration of the repeat.

    +
  10. +
  11. +

    Get all the C for the incoming B s. This is the second out iteration of the repeat.

    +
  12. +
  13. +

    The until traversal executed for all the incoming C s going out to X.

    +
  14. +
  15. +

    Get all the X for the incoming C s. This is the third out iteration of the repeat.

    +
  16. +
+
+
+
output
+
+
b1
+c1
+
+
+
+
+

10.2.3. Optional Step

+
+
+
@Test
+public void testStrategy2OptionalStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+
+
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .optional(
+                __.repeat(
+                        __.out()
+                ).times(2)
+            )
+            .values("name");
+    printTraversalForm(traversal);
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OptionalStep([RepeatStep([VertexStep(OUT,vertex), RepeatEndStep],until(loops(2)),emit(false))]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgOptionalStepBarrier([SqlgRepeatStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel], SqlgRepeatEndStepBarrier],until(loops(2)),emit(false))]), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+SELECT
+	3 as "index",
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	"public"."V_B"."ID" = 2
+ORDER BY
+	"index"
+
+SELECT
+	4 as "index",
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	"public"."V_B"."ID" = 1
+ORDER BY
+	"index"
+
+
+
+
output
+
+
a2
+c1
+
+
+
+
+

10.2.4. Choose Step

+
+
+
@Test
+public void testStrategy2ChooseStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "a3");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "a4");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V()
+            .hasLabel("A")
+            .choose(
+                    v -> v.label().equals("A"),
+                    __.out(),
+                    __.in()
+            ).values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+

+
+
+
+
After optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), ChooseStep([LambdaFilterStep(lambda), HasNextStep],{false=[[VertexStep(IN,vertex), EndStep]], true=[[VertexStep(OUT,vertex), EndStep]]}), PropertiesStep([name],value)]
+
+
+
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgChooseStepBarrier([LambdaFilterStep(lambda)],{false=[[SqlgVertexStep, EndStep]], true=[[SqlgVertexStep@[~gremlin.incidentToAdjacent], EndStep]]}), PropertiesStep([name],value)]
+
+
+
+
output
+
+
a4
+a3
+
+
+
+
+

10.2.5. Local Step

+
+
+
@Test
+public void testStrategy2LocalStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    Vertex c11 = this.sqlgGraph.addVertex(T.label, "C", "name", "c11");
+    Vertex c12 = this.sqlgGraph.addVertex(T.label, "C", "name", "c12");
+    Vertex c13 = this.sqlgGraph.addVertex(T.label, "C", "name", "c13");
+    Vertex c21 = this.sqlgGraph.addVertex(T.label, "C", "name", "c21");
+    Vertex c22 = this.sqlgGraph.addVertex(T.label, "C", "name", "c22");
+    Vertex c23 = this.sqlgGraph.addVertex(T.label, "C", "name", "c23");
+    Vertex c31 = this.sqlgGraph.addVertex(T.label, "C", "name", "c31");
+    Vertex c32 = this.sqlgGraph.addVertex(T.label, "C", "name", "c32");
+    Vertex c33 = this.sqlgGraph.addVertex(T.label, "C", "name", "c33");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    a1.addEdge("ab", b3);
+    b1.addEdge("bc", c11);
+    b1.addEdge("bc", c12);
+    b1.addEdge("bc", c13);
+    b2.addEdge("bc", c21);
+    b2.addEdge("bc", c22);
+    b2.addEdge("bc", c23);
+    b3.addEdge("bc", c31);
+    b3.addEdge("bc", c32);
+    b3.addEdge("bc", c33);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V(a1)
+            .local(
+                    __.out().limit(1).out()
+            ).values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[v[public.A:::1]]), LocalStep([VertexStep(OUT,vertex), RangeGlobalStep(0,1), VertexStep(OUT,vertex)]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], LocalStep([SqlgVertexStep@[sqlgPathOrderRangeLabel], SqlgVertexStep@[sqlgPathFakeLabel]]), PropertiesStep([name],value)]
+
+
+
+
+
+
+
+
+
output
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+WHERE
+	( "public"."V_A"."ID" = ?)
+
+SELECT
+	1 as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID"
+WHERE
+	"public"."V_A"."ID" = 1
+ORDER BY
+	"index"
+LIMIT 1 OFFSET 0 (1)
+
+SELECT
+	1 as "index",
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_B" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	"public"."V_B"."ID" = 1
+ORDER BY
+	"index"
+
+
+
+
    +
  1. +

    In this case the query is simple enough for the LIMIT to be executed on the database.

    +
  2. +
+
+
+
+

10.2.6. And Step

+
+
+
@Test
+public void testStrategy2AndStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    a1.addEdge("ab", b1);
+    a1.addEdge("abb", b1);
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    a2.addEdge("abb", b2);
+    Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    a3.addEdge("abbb", b3);
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal().V().hasLabel("A").and(
+            __.out("ab"),
+            __.out("abb")
+    ).values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), AndStep([[VertexStep(OUT,[ab],vertex)], [VertexStep(OUT,[abb],vertex)]]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgAndStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2),(3, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_abb" ON "public"."V_A"."ID" = "public"."E_abb"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_abb"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2),(3, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+
+
+
output
+
+
a1
+
+
+
+
+

10.2.7. Or Step

+
+
+
@Test
+public void testStrategy2OrStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    a1.addEdge("ab", b1);
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    a2.addEdge("abb", b2);
+    Vertex a3 = this.sqlgGraph.addVertex(T.label, "A", "name", "a3");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    a3.addEdge("abbb", b3);
+    Vertex a4 = this.sqlgGraph.addVertex(T.label, "A", "name", "a4");
+    Vertex b4 = this.sqlgGraph.addVertex(T.label, "B", "name", "b4");
+    a4.addEdge("abbbb", b4);
+
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .or(
+                    __.out("ab"),
+                    __.out("abb"),
+                    __.out("abbb")
+            ).values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), OrStep([[VertexStep(OUT,[ab],vertex)], [VertexStep(OUT,[abb],vertex)], [VertexStep(OUT,[abbb],vertex)]]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgOrStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]], [SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2),(3, 3),(4, 4)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_abb" ON "public"."V_A"."ID" = "public"."E_abb"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_abb"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(2, 1),(3, 2),(4, 3)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_abbb" ON "public"."V_A"."ID" = "public"."E_abbb"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_abbb"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(3, 1),(4, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+
+
+
output
+
+
a1
+a2
+a3
+
+
+
+
+

10.2.8. Not Step

+
+
+
@Test
+public void testStrategy2NotStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    a1.addEdge("ab", b1);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .not(
+                    __.out()
+            ).values("name");
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), NotStep([VertexStep(OUT,vertex)]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgNotStepBarrier([[SqlgVertexStep@[sqlgPathFakeLabel]]]), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+
+
+
output
+
+
a2
+
+
+
+
+

10.2.9. Where Step

+
+
+
@Test
+public void testStrategy2WhereStep() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex a2 = this.sqlgGraph.addVertex(T.label, "A", "name", "a2");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex b3 = this.sqlgGraph.addVertex(T.label, "B", "name", "b3");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    a1.addEdge("ab", b3);
+    a2.addEdge("ab", b1);
+    this.sqlgGraph.tx().commit();
+
+    Traversal<Vertex, String> traversal = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .where(
+                    __.out()
+            ).values("name");
+    printTraversalForm(traversal);
+
+    List<String> names = traversal.toList();
+    for (String name : names) {
+        System.out.println(name);
+    }
+}
+
+
+
+
Before optimization
+
+
[GraphStep(vertex,[]), HasStep([~label.eq(A)]), TraversalFilterStep([VertexStep(OUT,vertex)]), PropertiesStep([name],value)]
+
+
+
+
After optimization
+
+
[SqlgGraphStep(vertex,[])@[sqlgPathFakeLabel], SqlgTraversalFilterStepBarrier([SqlgVertexStep@[sqlgPathFakeLabel]]), PropertiesStep([name],value)]
+
+
+
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."name" AS "alias2"
+FROM
+	"public"."V_A"
+
+SELECT
+	"index" as "index",
+	"public"."V_B"."ID" AS "alias1",
+	"public"."V_B"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	(VALUES(1, 1),(2, 2)) AS tmp ("tmpId", "index") ON "public"."V_A"."ID" = tmp."tmpId"
+ORDER BY
+	"index"
+
+
+
+
+
+

10.3. Predicates

+
+

TinkerPop’s Compare and +Contains predicates are optimized +to execute on the database.

+
+
+

10.3.1. Compare predicate

+
+
+
@Test
+public void showComparePredicates() {
+    Vertex a1 = this.sqlgGraph.addVertex(T.label, "A", "name", "a1");
+    Vertex b1 = this.sqlgGraph.addVertex(T.label, "B", "name", "b1");
+    Vertex b2 = this.sqlgGraph.addVertex(T.label, "B", "name", "b2");
+    Vertex c1 = this.sqlgGraph.addVertex(T.label, "C", "name", "c1");
+    Vertex c2 = this.sqlgGraph.addVertex(T.label, "C", "name", "c2");
+    Vertex c3 = this.sqlgGraph.addVertex(T.label, "C", "name", "c3");
+    Vertex c4 = this.sqlgGraph.addVertex(T.label, "C", "name", "c4");
+    a1.addEdge("ab", b1);
+    a1.addEdge("ab", b2);
+    b1.addEdge("bc", c1);
+    b1.addEdge("bc", c2);
+    b2.addEdge("bc", c3);
+    b2.addEdge("bc", c4);
+    this.sqlgGraph.tx().commit();
+
+    List<String> result = this.sqlgGraph.traversal()
+            .V().hasLabel("A")
+            .out().has("name", P.eq("b1"))
+            .out().has("name", P.eq("c2")) (1)
+            .<String>values("name")
+            .toList();
+    for (String name : result) {
+        System.out.println(name);
+    }
+}
+
+
+
+
    +
  1. +

    The P predicates will resolve on the database as a sql where clause.

    +
  2. +
+
+
+
sql
+
+
SELECT
+	"public"."V_C"."ID" AS "alias1",
+	"public"."V_C"."name" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	"public"."E_ab" ON "public"."V_A"."ID" = "public"."E_ab"."public.A__O" INNER JOIN
+	"public"."V_B" ON "public"."E_ab"."public.B__I" = "public"."V_B"."ID" INNER JOIN
+	"public"."E_bc" ON "public"."V_B"."ID" = "public"."E_bc"."public.B__O" INNER JOIN
+	"public"."V_C" ON "public"."E_bc"."public.C__I" = "public"."V_C"."ID"
+WHERE
+	( "public"."V_B"."name" = ?) AND ( "public"."V_C"."name" = ?)
+
+
+
+

The same pattern is used for all the +Compare predicates.

+
+
+
+

10.3.2. Contains predicate

+
+

Sqlg’s implementation of Contains +is slightly more complex.

+
+
+

For Postgresql, MSSqlServer and HSQLDB a join onto a values expression is used.

+
+
+

For H2 and MariaDB a regular in clause is used.

+
+
+
+
@Test
+public void showContainsPredicate() {
+    List<Integer> numbers = new ArrayList<>(10000);
+    for (int i = 0; i < 10000; i++) {
+        this.sqlgGraph.addVertex(T.label, "A", "number", i);
+        numbers.add(i);
+    }
+    this.sqlgGraph.tx().commit();
+
+    List<Vertex> persons = this.sqlgGraph.traversal().V()
+            .hasLabel("A")
+            .has("number", P.within(numbers))
+            .toList();
+
+    assertEquals(10000, persons.size());
+}
+
+
+
+
sql
+
+
SELECT
+	"public"."V_A"."ID" AS "alias1",
+	"public"."V_A"."number" AS "alias2"
+FROM
+	"public"."V_A" INNER JOIN
+	(VALUES (0::INTEGER), (1::INTEGER), ... (9998::INTEGER), (9999::INTEGER)) as tmp1(within) on "public"."V_A"."number" = tmp1.within
+
+
+
+

This pattern makes P.within and p.without very fast even with millions of values being passed into the query. +For the case of there being only one value Sqlg will use an equals instead of a values statement or an in statement.

+
+
+
+

10.3.3. Text predicate

+
+ + + + + +
+
Note
+
+Sqlg assumes a case-sensitive collation. +MSSqlServer does not default to a case-sensitive collation. +Create the database with CREATE DATABASE sqlgraphdb COLLATE sql_latin1_general_cp1_cs_as +
+
+
+

Sqlg includes its own Text predicate for full text queries.

+
+
+
    +
  • +

    Text.contains (case sensitive string contains)

    +
  • +
  • +

    Text.ncontains (case sensitive string does not contain)

    +
  • +
  • +

    Text.containsCIS (case insensitive string contains)

    +
  • +
  • +

    Text.ncontainsCIS (case insensitive string does not contain)

    +
  • +
  • +

    Text.startsWith (case sensitive string starts with)

    +
  • +
  • +

    Text.nstartsWith (case sensitive string does not start with)

    +
  • +
  • +

    Text.endsWith (case sensitive string ends with)

    +
  • +
  • +

    Text.nendsWith (case sensitive string does not end with)

    +
  • +
+
+
+
+
@Test
+public void showTextPredicate() {
+    Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John XXX Doe");
+    Vertex peter = this.sqlgGraph.addVertex(T.label, "Person", "name", "Peter YYY Snow");
+    this.sqlgGraph.tx().commit();
+
+    List<Vertex> persons = this.sqlgGraph.traversal().V()
+            .hasLabel("Person")
+            .has("name", Text.contains("XXX")).toList();
+
+    assertEquals(1, persons.size());
+    assertEquals(john, persons.get(0));
+}
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."ID" AS "alias1",
+	"public"."V_Person"."name" AS "alias2"
+FROM
+	"public"."V_Person"
+WHERE
+	( "public"."V_Person"."name" like ?)
+
+
+
+
+ +
+

Full text search is supported on postgresql.

+
+
+

This is shown under full text indexing

+
+
+
+

10.3.5. DateTime queries

+
+

LocalDateTime, LocalDate and LocalTime queries are supported.

+
+
+
+
@Test
+public void showSearchOnLocalDateTime() {
+    LocalDateTime born1 = LocalDateTime.of(1990, 1, 1, 1, 1, 1);
+    LocalDateTime born2 = LocalDateTime.of(1990, 1, 1, 1, 1, 2);
+    LocalDateTime born3 = LocalDateTime.of(1990, 1, 1, 1, 1, 3);
+    Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John", "born", born1);
+    Vertex peter = this.sqlgGraph.addVertex(T.label, "Person", "name", "Peter", "born", born2);
+    Vertex paul = this.sqlgGraph.addVertex(T.label, "Person", "name", "Paul", "born", born3);
+    this.sqlgGraph.tx().commit();
+
+    List<Vertex> persons = this.sqlgGraph.traversal().V().hasLabel("Person")
+            .has("born", P.eq(born1))
+            .toList();
+    assertEquals(1, persons.size());
+    assertEquals(john, persons.get(0));
+
+    persons = this.sqlgGraph.traversal().V().hasLabel("Person")
+            .has("born", P.between(LocalDateTime.of(1990, 1, 1, 1, 1, 1), LocalDateTime.of(1990, 1, 1, 1, 1, 3)))
+            .toList();
+    //P.between is inclusive to exclusive
+    assertEquals(2, persons.size());
+    assertTrue(persons.contains(john));
+    assertTrue(persons.contains(peter));
+}
+
+
+
+
sql
+
+
SELECT
+	"public"."V_Person"."ID" AS "alias1",
+	"public"."V_Person"."born" AS "alias2",
+	"public"."V_Person"."name" AS "alias3"
+FROM
+	"public"."V_Person"
+WHERE
+	( "public"."V_Person"."born" >= ?) AND ( "public"."V_Person"."born" < ?)
+
+
+
+
+
+
+
+

11. Batch Mode

+
+
+

Sqlg supports 3 distinct batch modes. Normal, streaming and streaming with lock. Batch modes are only implemented on Postgresql. +Batch mode is activated on the transaction object itself. After every commit the batchMode needs to be reactivated.

+
+
+

Sqlg introduces an extra method on the transaction, flush().

+
+
+
    +
  • +

    In normal batch mode flush() will send all the data to Postgresql, assign id(s) and clear the cache.

    +
  • +
  • +

    In streaming mode flush() will close the OutputStream that the data has been written to.

    +
  • +
  • +

    In streaming mode with lock flush() will close the OutputStream that the data has been written to and assign id(s).

    +
  • +
+
+
+

The Postgresql 'copy' command is used to bulk insert data.

+
+
+

11.1. Normal batch mode

+
+

In normal batch mode the standard TinkerPop modification api can be used. Normal batch mode caches all modifications in memory +and on commit() or flush() sends the modifications to the server.

+
+
+

Because all modifications are held in memory it is important to call commit() or flush() to prevent an OutOfMemoryError.

+
+
+

In batch mode vertices and edges returned from Graph.addVertex and vertex.addEdge respectively do not yet have their id(s) assigned to them. +This is because the new vertices and edges are cached in memory and are only sent to Postgresql on commit() or flush(). +After commit() or flush() the new vertices and edges have their id(s) assigned.

+
+
+

The transaction must be manually placed in normal batch mode. i.e. SqlgGraph.tx().normalBatchModeOn() must occur before any batch processing. +After every commit() the transaction reverts to a regular transaction and must be placed in normal batch mode again +for batch processing to continue.

+
+
+

Vertices and edges can be created and updated and removed as per normal making normal batch mode easy to use.

+
+
+ + + + + +
+
Note
+
+Sqlg does not query the cache. If a gremlin query is executed while in batch mode the batch is first flushed. +Take care not to query the graph while in batch mode as flushing often will defeat the purpose of batching in the first place. +
+
+
+
custom api
+
+
sqlgGraph.tx().normalBatchModeOn();
+sqlgGraph.tx().flush();
+
+
+
+

Create 10 000 000 Persons each with a car. 20 000 000 vertices and 10 000 000 edges in total.

+
+
+
+
@Test
+public void showNormalBatchMode() {
+    StopWatch stopWatch = new StopWatch();
+    stopWatch.start();
+    this.sqlgGraph.tx().normalBatchModeOn();
+    for (int i = 1; i <= 10_000_000; i++) {
+        Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John" + i);
+        Vertex car = this.sqlgGraph.addVertex(T.label, "Car", "name", "Dodge" + i);
+        person.addEdge("drives", car);
+        if (i % 100_000 == 0) { # (1)
+            this.sqlgGraph.tx().flush(); # (1)
+        }
+    }
+    this.sqlgGraph.tx().commit();
+    stopWatch.stop();
+    System.out.println(stopWatch.toString());
+}
+
+
+
+
    +
  1. +

    To preserve memory commit or flush every so often.

    +
  2. +
+
+
+
output without edge foreign keys
+
+
Time taken: 0:05:48.889
+
+
+
+
output with edge foreign keys
+
+
Time taken: 0:02:33.313
+
+
+
+
memory
+

image of tinkerpop-classic

+
+
+
+

11.2. Streaming batch mode

+
+

Streaming batch writes any new vertex or edge immediately to Postgresql via its stdin api. I.e. the data is written +directly to a Postgresql jdbc driver OutputStream.

+
+
+

Streaming batch mode does not use the Graph.addVertex method. Instead SqlgGraph.streamVertex is defined.

+
+
+

The transaction must be placed in streaming batch mode manually before any streaming batch modification can happen. SqlgGraph.tx().streamingBatchModeOn() +After every commit() the transaction reverts to normal mode and must be placed into streaming batch mode again +for streaming batch mode to continue.

+
+
+

The benefit of streaming mode is that the memory consumption is very low as nothing is cached. It is also somewhat faster than +the normal batch mode (+/- 25% faster).

+
+
+

However the caveat is that, per transaction/thread only one label/table can be written between consecutive calls to SqlgTransaction.flush(). +Further it is not possible to assign an id to the vertex or element. As such the SqlgGraph.streamVertex method returns void.

+
+
+
custom api
+
+
sqlgGraph.tx().streamingBatchModeOn();
+
+
+
+

Create 10 000 000 Persons and 10 000 000 cars.

+
+
+
+
@Test
+public void showStreamingBatchMode() {
+    StopWatch stopWatch = new StopWatch();
+    stopWatch.start();
+    //enable streaming mode
+    this.sqlgGraph.tx().streamingBatchModeOn();
+    for (int i = 1; i <= 10_000_000; i++) {
+        this.sqlgGraph.streamVertex(T.label, "Person", "name", "John" + i);
+    }
+    this.sqlgGraph.tx().flush(); # (1)
+    for (int i = 1; i <= 10_000_000; i++) {
+        this.sqlgGraph.streamVertex(T.label, "Car", "name", "Dodge" + i);
+    }
+    this.sqlgGraph.tx().commit();
+    stopWatch.stop();
+    System.out.println(stopWatch.toString());
+}
+
+
+
+
    +
  1. +

    flushing is needed before starting streaming Car. Only only one label/table can stream at a time.

    +
  2. +
+
+
+
output
+
+
Time taken: 0:00:42.014
+
+
+
+
memory
+

image of tinkerpop-classic

+
+
+
+

11.3. Bulk edge creation

+
+

To create an edge via the normal api a handle to the Vertex is needed. +This is not always the case. In particula if the SqlgGraph.streamVertex api is used no handle to the Vertex is returned.

+
+
+

For this scenario there is a bulk edge creation method.

+
+
+
+
public <L, R> void bulkAddEdges(String outVertexLabel, String inVertexLabel, String edgeLabel, Pair<String, String> idFields, Collection<Pair<L, R>> uids) {
+
+
+
+
    +
  • +

    outLabel and inLabel specifies the out and in vertex labels that the edges will be between.

    +
  • +
  • +

    edgeLabel is the label of the edges to be created.

    +
  • +
  • +

    idFields specifies the fields that uniquely identify the out and in vertex.

    +
  • +
  • +

    uids are the actual unique identifies for each out/in vertex pairing.

    +
  • +
+
+
+

Sqlg will then first copy the uids into a temporary table. Then it joins the temporary table on the out and in vertex tables +to retrieve the in and out ids. +These ids are then inserted into the edge table. +All this happens on Postgresql, having minimal processing and memory impact on the java process.

+
+
+

The unique identifiers still have to be kept in memory, but its is not necessary to have the actual out and in vertices in memory.

+
+
+ + + + + +
+
Note
+
+The unique identifiers do not need to be the vertices’s id. It can be any property as long as it is unique. +
+
+
+
+
@Test
+public void showBulkEdgeCreation() {
+    StopWatch stopWatch = new StopWatch();
+    stopWatch.start();
+    int count = 0;
+    for (int i = 1; i <= 10; i++) {
+        List<Pair<String, String>> identifiers = new ArrayList<>();
+        this.sqlgGraph.tx().streamingBatchModeOn();
+        for (int j = 1; j <= 1_000_000; j++) {
+            this.sqlgGraph.streamVertex(T.label, "Person", "name", "John" + count, "personUid", String.valueOf(count));
+        }
+        this.sqlgGraph.tx().flush();
+        for (int j = 1; j <= 1_000_000; j++) {
+            this.sqlgGraph.streamVertex(T.label, "Car", "name", "Dodge" + count, "carUid", String.valueOf(count));
+            identifiers.add(Pair.of(String.valueOf(count), String.valueOf(count++)));
+        }
+        this.sqlgGraph.tx().flush();
+        this.sqlgGraph.bulkAddEdges("Person", "Car", "drives", Pair.of("personUid", "carUid"), identifiers);
+        this.sqlgGraph.tx().commit();
+    }
+    stopWatch.stop();
+    System.out.println("Time taken: " + stopWatch.toString());
+}
+
+
+
+
output (with edge foreign keys)
+
+
Time taken: 0:10:03.397
+
+
+
+
output (without edge foreign keys)
+
+
Time taken: 0:03:45.951
+
+
+
+
memory
+

image of tinkerpop-classic

+
+
+
+

11.4. Streaming with lock batch mode

+
+

Streaming with lock batch mode is similar to streaming batch mode. The difference being that the label/table being written to is +locked. Locking the table ensures that no concurrent changes will occur on the table. This allows Sqlg to query the id sequence and +assigned ids to the elements.

+
+
+

This means that the normal Vertex vertex = graph.addVertex(…​) method can be used. This is useful if a pointer to the new vertices are needed.

+
+
+

The transaction must be placed into streaming with lock batch mode manually before any streaming with lock batch modification can happen. +SqlgGraph.tx().streamingWithLockBatchModeOn() After every commit() the transaction reverts to normal mode and must +be placed into streaming batch mode again for streaming batch mode to continue.

+
+
+
custom api
+
+
sqlgGraph.tx().streamingWithLockBatchModeOn();
+
+
+
+
+
@Test
+public void showStreamingWithLockBulkEdgeCreation() {
+    StopWatch stopWatch = new StopWatch();
+    stopWatch.start();
+    int count = 0;
+    for (int i = 1; i <= 10; i++) {
+        List<Vertex> persons = new ArrayList<>();
+        this.sqlgGraph.tx().streamingWithLockBatchModeOn();
+        for (int j = 1; j <= 1_000_000; j++) {
+            Vertex person = this.sqlgGraph.addVertex(T.label, "Person", "name", "John" + count);
+            persons.add(person);
+        }
+        this.sqlgGraph.tx().flush();
+        List<Vertex> cars = new ArrayList<>();
+        for (int j = 1; j <= 1_000_000; j++) {
+            Vertex car = this.sqlgGraph.addVertex(T.label, "Car", "name", "Dodge" + count++);
+            cars.add(car);
+        }
+        this.sqlgGraph.tx().flush();
+        Iterator<Vertex> carIter = cars.iterator();
+        for (Vertex person : persons) {
+            person.addEdge("drives", carIter.next());
+        }
+        this.sqlgGraph.tx().commit();
+    }
+    stopWatch.stop();
+    System.out.println(stopWatch.toString());
+}
+
+
+
+
output without edge foreign keys
+
+
Time taken: 0:02:42.363
+
+
+
+
memory
+

image of tinkerpop-classic

+
+
+
+
+
+

12. Topology

+
+
+

Sqlg stores the graph’s topology information in the graph itself as a graph. +The topology is stored in the sqlg_schema schema.

+
+
+
UML diagram of Sqlg’s topology.
+

image of Sqlg’s topology

+
+
+

TinkerPop has no notion of schema or topology. However any TinkerPop graph has an implicit schema. +Sqlg manages the schema as a first class construct.

+
+
+

Sqlg follows the normal TinkerPop semantics in that the schema does not need to be defined upfront. +Every graph modification first checks to see if the element’s schema (label,name) exists. +If not, it will create the element’s schema. For Postgresql this works well as it supports transactional schema creation/modification.

+
+
+ + + + + +
+
Warning
+
+Hsqldb, H2 and MariaDb do not support transactional schema creation/modification. They will both silently commit the +transaction and continue. This breaks the user’s transaction boundaries. For Hsqldb, H2 and MariaDb it is recommended to +create the schema upfront. +
+
+
+

It is possible to query and traverse the topology as a normal TinkerPop graph. +To query the topology the TopologyStrategy is used. To facilitate ease of use, SqlgGraph.topology() method is added to enable the strategy. +Being able to query the topology is helpful to understand a graph’s structure.

+
+
+
+
@Test
+public void showTopologyTraversals() {
+    Io.Builder<GraphSONIo> builder = GraphSONIo.build(GraphSONVersion.V3_0); (1)
+    final GraphReader reader = sqlgGraph.io(builder).reader().create();
+    try (final InputStream stream = AbstractGremlinTest.class.getResourceAsStream("/tinkerpop-modern-v3d0.json")) {
+        reader.readGraph(stream, sqlgGraph);
+    } catch (IOException e) {
+        Assert.fail(e.getMessage());
+    }
+    System.out.println("//All vertex labels");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) # (2)
+            .forEachRemaining(
+                    v -> System.out.println(v.<String>value(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME))
+            );
+
+    System.out.println("//All edge labels");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL)
+            .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE) # (3)
+            .forEachRemaining(
+                    v -> System.out.println(v.<String>value(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME))
+            );
+
+    System.out.println("//'person' properties");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL)
+            .has(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME, "person") # (4)
+            .out(Topology.SQLG_SCHEMA_VERTEX_PROPERTIES_EDGE) # (5)
+            .forEachRemaining(
+                    v -> {
+                        System.out.print(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : ");
+                        System.out.println(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_TYPE));
+                    }
+            );
+
+    System.out.println("//'software' properties");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL)
+            .has(Topology.SQLG_SCHEMA_VERTEX_LABEL_NAME, "software")
+            .out(Topology.SQLG_SCHEMA_VERTEX_PROPERTIES_EDGE)
+            .forEachRemaining(
+                    v -> {
+                        System.out.print(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : ");
+                        System.out.println(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_TYPE));
+                    }
+            );
+
+    System.out.println("//'created' properties");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL) # (6)
+            .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE) # (7)
+            .has(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME, "created") # (8)
+            .out(Topology.SQLG_SCHEMA_EDGE_PROPERTIES_EDGE) # (9)
+            .forEachRemaining(
+                    v -> {
+                        System.out.print(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : ");
+                        System.out.println(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_TYPE));
+                    }
+            );
+
+    System.out.println("//'knows' properties");
+    sqlgGraph.topology().V()
+            .hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_VERTEX_LABEL)
+            .out(Topology.SQLG_SCHEMA_OUT_EDGES_EDGE)
+            .has(Topology.SQLG_SCHEMA_EDGE_LABEL_NAME, "knows")
+            .out(Topology.SQLG_SCHEMA_EDGE_PROPERTIES_EDGE)
+            .forEachRemaining(
+                    v -> {
+                        System.out.print(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_NAME) + " : ");
+                        System.out.println(v.<String>value(Topology.SQLG_SCHEMA_PROPERTY_TYPE));
+                    }
+            );
+
+}
+
+
+
+
    +
  1. +

    Use TinkerPop’s i.o. infrastructure to load the modern graph.

    +
  2. +
  3. +

    Find all VertexLabels, they are in sqlg_schema.vertex

    +
  4. +
  5. +

    Traverse out on the out_edges edge to find all the edges. 'WARNING' this may produce duplicates as a single edge label +may have many different distinct out vertex labels.

    +
  6. +
  7. +

    Find the person vertex.

    +
  8. +
  9. +

    Traverse out on the vertex_property edge to find the 'person' vertex labels properties.

    +
  10. +
  11. +

    Find all vertex labels. i.e. vertices in sqlg_schema.vertex

    +
  12. +
  13. +

    Traverse the out_edges edge.

    +
  14. +
  15. +

    Filter the out edges for only the 'created' edges.

    +
  16. +
  17. +

    Traverse the edge_properties edge to find the 'created' edge’s properties.

    +
  18. +
+
+
+
output
+
+
//All vertex labels
+person
+software
+//All edge labels
+knows
+created
+//'person' properties
+name : STRING
+age : INTEGER
+//'software' properties
+name : STRING
+lang : STRING
+//'created' properties
+weight : DOUBLE
+//'knows' properties
+weight : DOUBLE
+
+
+
+

12.1. Topology eager creation

+
+

It is often useful to create the topology upfront. The topology creation api is accessed via the Topology object. +It is a singleton. Topology topology = sqlgGraph.getTopology(); +To create new topology objects use the ensureXXX methods. They will return the a topology object representing the specific +topology element. i.e. Schema, VertexLabel, EdgeLabel, PropertyColumn, Index or GlobalUniqueIndex

+
+
+ + + + + +
+
Note
+
+The ensureXXX methods will create the topology object if it does not exists. +If it does exist it will simply return the relevant topology object. +On any topology object one can call isCommitted or isUncommitted to check the state of the object. +committed indicates that it already exists. uncommitted indicates that it has been created in the current active transaction. +
+
+
+
eg
+
+
@Test
+public void createModernTopology() {
+    Topology topology = this.sqlgGraph.getTopology(); # (1)
+    VertexLabel personVertexLabel = topology.ensureVertexLabelExist("public", "person", new HashMap<String, PropertyType>() {{
+        put("name", PropertyType.STRING);
+        put("age", PropertyType.INTEGER);
+    }}); # (2)
+    VertexLabel softwareVertexLabel = topology.ensureVertexLabelExist("public", "software", new HashMap<String, PropertyType>() {{
+        put("name", PropertyType.STRING);
+        put("lang", PropertyType.STRING);
+    }});
+    EdgeLabel createdEdgeLabel = personVertexLabel.ensureEdgeLabelExist("created", softwareVertexLabel, new HashMap<String, PropertyType>() {{
+        put("weight", PropertyType.DOUBLE);
+    }}); # (3)
+    EdgeLabel knowsEdgeLabel = personVertexLabel.ensureEdgeLabelExist("knows", personVertexLabel, new HashMap<String, PropertyType>() {{
+        put("weight", PropertyType.DOUBLE);
+    }});
+    this.sqlgGraph.tx().commit(); # (4)
+}
+
+
+
+
    +
  1. +

    Get the Topology object.

    +
  2. +
  3. +

    Create the 'person' VertexLabel. The HashMap<String, PropertyType> defines the 'person''s properties.

    +
  4. +
  5. +

    Create the 'created' EdgeLabel. The format is outVertexLabel.ensureEdgeLabelExist(name, inVertexLabel, properties)

    +
  6. +
  7. +

    Be sure to commit the transaction. Postgresql and MSSqlServer supports transactional schema creation. Hsqldb,H2 and MariaDB do not.

    +
  8. +
+
+
+
+
@Test
+public void generalTopologyCreationWithSchema() {
+    Schema schema = this.sqlgGraph.getTopology().ensureSchemaExist("Humans"); # (1)
+    VertexLabel personVertexLabel = schema.ensureVertexLabelExist("Person", new HashMap<String, PropertyType>() {{
+        put("name", PropertyType.STRING);
+        put("date", PropertyType.LOCALDATE);
+    }}); # (2)
+    this.sqlgGraph.tx().commit();
+}
+
+
+
+
    +
  1. +

    Create the 'Humans' schema

    +
  2. +
  3. +

    Create the 'Person' VertexLabel via the Schema object.

    +
  4. +
+
+
+

Sqlg keeps an in-memory cache of the graphs entire topology. It is possible query this cache directly.

+
+
+
+
@Test
+public void queryCache() {
+    loadModern();
+    Optional<Schema> publicSchema = this.sqlgGraph.getTopology().getSchema(this.sqlgGraph.getSqlDialect().getPublicSchema()); # (1)
+    assertTrue(publicSchema.isPresent());
+    Schema publicSchemaViaShortCut = this.sqlgGraph.getTopology().getPublicSchema(); # (2)
+    Optional<VertexLabel> personVertexLabel = publicSchema.get().getVertexLabel("person"); # (3)
+    assertTrue(personVertexLabel.isPresent());
+    Optional<EdgeLabel> createEdgeLabel = personVertexLabel.get().getOutEdgeLabel("created"); # (4)
+    assertTrue(createEdgeLabel.isPresent());
+    Optional<EdgeLabel> knowsEdgeLabel = personVertexLabel.get().getOutEdgeLabel("knows"); # (5)
+    assertTrue(knowsEdgeLabel.isPresent());
+
+    Optional<PropertyColumn> namePropertyColumn = personVertexLabel.get().getProperty("name"); # (6)
+    assertTrue(namePropertyColumn.isPresent());
+    assertEquals(PropertyType.STRING, namePropertyColumn.get().getPropertyType()); # (7)
+    Optional<PropertyColumn> agePropertyColumn = personVertexLabel.get().getProperty("age");
+    assertTrue(agePropertyColumn.isPresent());
+    assertEquals(PropertyType.INTEGER, agePropertyColumn.get().getPropertyType());
+    Optional<PropertyColumn> weightPropertyColumn = createEdgeLabel.get().getProperty("weight");
+    assertTrue(weightPropertyColumn.isPresent());
+    assertEquals(PropertyType.DOUBLE, weightPropertyColumn.get().getPropertyType());
+}
+
+
+
+
    +
  1. +

    Get the 'public' schema object.

    +
  2. +
  3. +

    Because the 'public' schema will always exist there is a shortcut method to get it.

    +
  4. +
  5. +

    Use the 'Schema' object the get the 'person' VertexLabel

    +
  6. +
  7. +

    Use the 'person' VertexLabel to get its 'created' out edge.

    +
  8. +
  9. +

    Use the 'person' VertexLabel to get its 'knows' out edge.

    +
  10. +
  11. +

    Use the 'person' VertexLabel to get its 'name' property. Properties are represented by the PropertyColumn class.

    +
  12. +
  13. +

    On the PropertyColumn object one can get the PropertyType. PropertyType is an enum representing all data types supported by Sqlg.

    +
  14. +
+
+
+
+

12.2. User supplied identifiers

+
+

You can define your own identifiers for a VertexLabel or EdgeLabel. This will result in Sqlg generating primary keys on the specified identifiers instead of using an auto generated sequence.

+
+
+
eg.
+
+
@Test
+public void testUserSuppliedIds() {
+    VertexLabel personVertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist(
+            "Person",
+            new LinkedHashMap<>() {{
+                put("name", PropertyType.STRING);
+                put("surname", PropertyType.STRING);
+                put("nickname", PropertyType.STRING);
+            }},
+            ListOrderedSet.listOrderedSet(Arrays.asList("name", "surname")) # (1)
+    );
+    personVertexLabel.ensureEdgeLabelExist(
+            "marriedTo",
+            personVertexLabel,
+            new LinkedHashMap<>() {{
+                put("place", PropertyType.STRING);
+                put("when", PropertyType.LOCALDATETIME);
+            }},
+            ListOrderedSet.listOrderedSet(List.of("place", "when")) # (2)
+    );
+    this.sqlgGraph.tx().commit();
+
+    Vertex john = this.sqlgGraph.addVertex(T.label, "Person", "name", "John", "surname", "Longfellow", "nickname", "Longboy");
+    Vertex sue = this.sqlgGraph.addVertex(T.label, "Person", "name", "Sue", "surname", "Pretty");
+    john.addEdge("marriedTo", sue, "place", "Timbuck2", "when", LocalDateTime.now());
+    this.sqlgGraph.tx().commit();
+
+    List<Vertex> marriedTo = this.sqlgGraph.traversal().V().hasLabel("Person")
+            .has("name", "John")
+            .out("marriedTo")
+            .toList();
+    Assert.assertEquals(1, marriedTo.size());
+    Assert.assertEquals(sue, marriedTo.get(0));
+}
+
+
+
+
    +
  1. +

    Specify the name and surname properties as the primary key for the Person vertex label.

    +
  2. +
  3. +

    Specify the place and when properties as the primary key for the marriedTo edge label.

    +
  4. +
+
+
+

This will generate a table with name and surname, and place and when as composite primary keys.

+
+
+
+
CREATE TABLE public."V_Person"
+(
+    name text COLLATE pg_catalog."default" NOT NULL,
+    surname text COLLATE pg_catalog."default" NOT NULL,
+    nickname text COLLATE pg_catalog."default",
+    CONSTRAINT "V_Person_pkey" PRIMARY KEY (name, surname)
+);
+
+CREATE TABLE public."E_marriedTo"
+(
+    place text COLLATE pg_catalog."default" NOT NULL,
+    "when" timestamp without time zone NOT NULL,
+    "public.Person.name__I" text COLLATE pg_catalog."default",
+    "public.Person.surname__I" text COLLATE pg_catalog."default",
+    "public.Person.name__O" text COLLATE pg_catalog."default",
+    "public.Person.surname__O" text COLLATE pg_catalog."default",
+    CONSTRAINT "E_marriedTo_pkey" PRIMARY KEY (place, "when"),
+    CONSTRAINT "E_marriedTo_public.Person.name__I_public.Person.surname__I_fkey" FOREIGN KEY ("public.Person.name__I", "public.Person.surname__I")
+        REFERENCES public."V_Person" (name, surname) MATCH SIMPLE
+        ON UPDATE NO ACTION
+        ON DELETE NO ACTION
+        DEFERRABLE,
+    CONSTRAINT "E_marriedTo_public.Person.name__O_public.Person.surname__O_fkey" FOREIGN KEY ("public.Person.name__O", "public.Person.surname__O")
+        REFERENCES public."V_Person" (name, surname) MATCH SIMPLE
+        ON UPDATE NO ACTION
+        ON DELETE NO ACTION
+        DEFERRABLE
+)
+
+
+
+

The gremlin query will execute the following sql,

+
+
+
+
SELECT
+	a2."alias1", a2."alias2", a2."alias3"
+FROM (
+SELECT
+	"public"."E_marriedTo"."public.Person.name__I" AS "public.E_marriedTo.public.Person.name__I",
+	"public"."E_marriedTo"."public.Person.surname__I" AS "public.E_marriedTo.public.Person.surname__I"
+FROM
+	"public"."V_Person" INNER JOIN
+	"public"."E_marriedTo" ON "public"."V_Person"."name" = "public"."E_marriedTo"."public.Person.name__O" AND "public"."V_Person"."surname" = "public"."E_marriedTo"."public.Person.surname__O"
+WHERE
+	( "public"."V_Person"."name" = ?)
+) a1 INNER JOIN (
+SELECT
+	"public"."V_Person"."name" AS "alias1",
+	"public"."V_Person"."surname" AS "alias2",
+	"public"."V_Person"."nickname" AS "alias3"
+FROM
+	"public"."V_Person"
+) a2 ON a1."public.E_marriedTo.public.Person.name__I" = a2."alias1" AND a1."public.E_marriedTo.public.Person.surname__I" = a2."alias2"
+
+
+
+
+
+
+

13. Postgresql Partitioning

+
+
+

Sqlg supports postgresql partitioning. To partition a table it needs to be created upfront using the Topology api. +Sqlg currently supports RANGE, LIST and HASH partitions.

+
+
+

13.1. Range partitioning

+
+
+
@Test
+public void testPartitioningRange() {
+    Schema publicSchema = this.sqlgGraph.getTopology().getPublicSchema(); # (1)
+    VertexLabel partitionedVertexLabel = publicSchema.ensurePartitionedVertexLabelExist(
+            "Measurement",
+            new LinkedHashMap<String, PropertyType>() {{
+                put("date", PropertyType.LOCALDATE);
+                put("temp", PropertyType.INTEGER);
+            }},
+            ListOrderedSet.listOrderedSet(Collections.singletonList("date")),
+            PartitionType.RANGE, #(2)
+            "date"); # (3)
+    partitionedVertexLabel.ensureRangePartitionExists("measurement1", "'2016-07-01'", "'2016-08-01'"); # (4)
+    partitionedVertexLabel.ensureRangePartitionExists("measurement2", "'2016-08-01'", "'2016-09-01'"); # (5)
+    this.sqlgGraph.tx().commit();
+
+    LocalDate localDate1 = LocalDate.of(2016, 7, 1);
+    this.sqlgGraph.addVertex(T.label, "Measurement", "date", localDate1);
+    LocalDate localDate2 = LocalDate.of(2016, 8, 1);
+    this.sqlgGraph.addVertex(T.label, "Measurement", "date", localDate2);
+    this.sqlgGraph.tx().commit();
+
+    Assert.assertEquals(2, this.sqlgGraph.traversal().V().hasLabel("Measurement").count().next(), 0);
+    Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate1).count().next(), 0);
+    Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate2).count().next(), 0);
+
+    Partition partition = this.sqlgGraph.getTopology().getPublicSchema().getVertexLabel("Measurement").get().getPartition("measurement1").get(); # (6)
+    partition.remove(); # (7)
+    this.sqlgGraph.tx().commit();
+
+    Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").count().next(), 0);
+    Assert.assertEquals(0, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate1).count().next(), 0);
+    Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Measurement").has("date", localDate2).count().next(), 0);
+
+    Assert.assertEquals(1, this.sqlgGraph.topology().V().hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_PARTITION).count().next(), 0); # (8)
+}
+
+
+
+
    +
  1. +

    Get the 'public' schema object.

    +
  2. +
  3. +

    Indicates a RANGE partition.

    +
  4. +
  5. +

    Create a VertexLabel with a range partition on the date field.

    +
  6. +
  7. +

    Create a named partition for the range '2016-07-01' to '2016-08-01'.

    +
  8. +
  9. +

    Create a named partition for the range '2016-08-01' to '2016-09-01'.

    +
  10. +
  11. +

    Using the Topology api get the measurement1 partition.

    +
  12. +
  13. +

    Remove the measurement1 partition.

    +
  14. +
  15. +

    Assert that `Sqlg`s topology only has one partition.

    +
  16. +
+
+
+
+

13.2. List partitioning

+
+
+
//the partitionExpression 'left(lower(name), 1)' is to complex for the query planner to optimize.
+//i.e. select * from Cities where name = 'asdasd' willscan all partitions.
+@Test
+public void testPartitioningList() {
+    Schema publicSchema = this.sqlgGraph.getTopology().getPublicSchema();
+    VertexLabel partitionedVertexLabel = publicSchema.ensurePartitionedVertexLabelExist("Cities",
+            new LinkedHashMap<String, PropertyType>() {{
+                put("name", PropertyType.STRING);
+                put("population", PropertyType.LONG);
+            }},
+            ListOrderedSet.listOrderedSet(Collections.singletonList("name")),
+            PartitionType.LIST, # (1)
+            "left(lower(name), 1)"); # (2)
+    partitionedVertexLabel.ensureListPartitionExists("Cities_a", "'a'"); # (3)
+    partitionedVertexLabel.ensureListPartitionExists("Cities_b", "'b'");
+    partitionedVertexLabel.ensureListPartitionExists("Cities_c", "'c'");
+    partitionedVertexLabel.ensureListPartitionExists("Cities_d", "'d'");
+    this.sqlgGraph.tx().commit();
+
+    this.sqlgGraph.tx().normalBatchModeOn();
+    for (int i = 0; i < 100; i++) {
+        this.sqlgGraph.addVertex(T.label, "Cities", "name", "aasbc", "population", 1000L);
+    }
+    this.sqlgGraph.addVertex(T.label, "Cities", "name", "basbc", "population", 1000L);
+    for (int i = 0; i < 100; i++) {
+        this.sqlgGraph.addVertex(T.label, "Cities", "name", "casbc", "population", 1000L);
+    }
+    this.sqlgGraph.addVertex(T.label, "Cities", "name", "dasbc", "population", 1000L);
+    this.sqlgGraph.tx().commit();
+
+    Assert.assertEquals(202, this.sqlgGraph.traversal().V().hasLabel("Cities").count().next(), 0);
+    Assert.assertEquals(100, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "aasbc").count().next(), 0);
+    Assert.assertEquals(1, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "basbc").count().next(), 0);
+    Assert.assertEquals(100, this.sqlgGraph.traversal().V().hasLabel("Cities").has("name", "casbc").count().next(), 0);
+
+    Partition partition = this.sqlgGraph.getTopology().getPublicSchema().getVertexLabel("Cities").get().getPartition("Cities_a").get();
+    partition.remove();
+    this.sqlgGraph.tx().commit();
+
+    Assert.assertEquals(102, this.sqlgGraph.traversal().V().hasLabel("Cities").count().next(), 0);
+    Assert.assertEquals(3, this.sqlgGraph.topology().V().hasLabel(Topology.SQLG_SCHEMA + "." + Topology.SQLG_SCHEMA_PARTITION).count().next(), 0);
+}
+
+
+
+
    +
  1. +

    Indicates a LIST partition.

    +
  2. +
  3. +

    The partition expression.

    +
  4. +
  5. +

    Create a named partition for the list entry 'a'.

    +
  6. +
+
+
+
+

13.3. Hash partitioning

+
+
+
@Test
+public void testPartitioningHash() {
+        VertexLabel vertexLabel = this.sqlgGraph.getTopology().getPublicSchema().ensurePartitionedVertexLabelExist(
+                "A",
+                new LinkedHashMap<>() {{
+                    put("uid1", PropertyType.INTEGER);
+                    put("name", PropertyType.STRING);
+                    put("surname", PropertyType.STRING);
+                }},
+                ListOrderedSet.listOrderedSet(List.of("uid1")),
+                PartitionType.HASH, (1)
+                "\"uid1\"" (2)
+        );
+        for (int i = 0; i < 10; i++) {
+            vertexLabel.ensureHashPartitionExists("hashPartition" + i, 10, i); (3)
+        }
+        this.sqlgGraph.tx().commit();
+        for (int i = 0; i < 1000; i++) {
+            this.sqlgGraph.addVertex(T.label, "A", "uid1", i, "name", "name" + i, "surname", "surname" + i);
+        }
+        this.sqlgGraph.tx().commit();
+        Assert.assertEquals(1000, this.sqlgGraph.traversal().V().hasLabel("A").count().next(), 0);
+
+        Connection connection = this.sqlgGraph.tx().getConnection();
+        try (Statement s = connection.createStatement()) {
+            ResultSet rs = s.executeQuery("select tableoid::regclass as partition_name, count(*) from \"V_A\" group by 1 order by 1;"); (4)
+            int count = 0;
+            Map<String, Long> partitionDistributionCount = new HashMap<>();
+            while (rs.next()) {
+                count++;
+                partitionDistributionCount.put(rs.getString(1), rs.getLong(2));
+            }
+            Assert.assertEquals(10, count); (5)
+            Assert.assertEquals(10, partitionDistributionCount.size());
+            for (int i = 0; i < 10; i++) {
+                Assert.assertTrue(partitionDistributionCount.containsKey("\"hashPartition" + i + "\""));
+            }
+            Assert.assertEquals(100, partitionDistributionCount.get("\"hashPartition0\""), 0);
+            Assert.assertEquals(92, partitionDistributionCount.get("\"hashPartition1\""), 0);
+            Assert.assertEquals(103, partitionDistributionCount.get("\"hashPartition2\""), 0);
+            Assert.assertEquals(88, partitionDistributionCount.get("\"hashPartition3\""), 0);
+            Assert.assertEquals(113, partitionDistributionCount.get("\"hashPartition4\""), 0);
+            Assert.assertEquals(90, partitionDistributionCount.get("\"hashPartition5\""), 0);
+            Assert.assertEquals(119, partitionDistributionCount.get("\"hashPartition6\""), 0);
+            Assert.assertEquals(92, partitionDistributionCount.get("\"hashPartition7\""), 0);
+            Assert.assertEquals(100, partitionDistributionCount.get("\"hashPartition8\""), 0);
+            Assert.assertEquals(103, partitionDistributionCount.get("\"hashPartition9\""), 0);
+        } catch (SQLException throwables) {
+            Assert.fail(throwables.getMessage());
+        }
+
+}
+
+
+
+
    +
  1. +

    Indicates a HASH partition.

    +
  2. +
  3. +

    The partition expression.

    +
  4. +
  5. +

    Create a named partition for the hash entry with it modulus and remainder.

    +
  6. +
  7. +

    Fetch the partitions for the assertion

    +
  8. +
  9. +

    Assert that there are as many partitions as the modulus

    +
  10. +
+
+
+
+
+
+

14. Sqlg ui

+
+
+

Sqlg includes a basic ui to visualize and delete/remove schema elements.

+
+
+ + + + + +
+
Warning
+
+Sqlg' ui is very dangerous as it allows for deletion of schema elements including RDBMS schemas. Use with care and ideally do not expose to a wide audience. +
+
+
+

14.1. Startup

+
+

The ui uses Sparkjava as its web framework.

+
+
+

There are two ways in which to start the ui.

+
+
+
Embedded
+

To use a completely standalone Jetty you can run the following code,

+
+
+
+
//SqlgUI.initialize(); (1)
+SqlgUI.initialize(8181);
+SqlgUI.set(sqlgGraph);
+
+
+
+
    +
  1. +

    The default port is 4567

    +
  2. +
+
+
+

To use an existing embedded Jetty you can use the following setup.

+
+
+
+
 //Define your filer
+FilterHolder filterHolderSqlgUI = contextHandler.addFilter("spark.servlet.SparkFilter", "/sqlg/*", EnumSet.of(DispatcherType.REQUEST));
+filterHolderSqlgUI.setInitParameter("applicationClass", "org.umlg.sqlg.ui.SparkResources");
+
+//Websocket servlet
+ServletHolder websocketServletHolder = new ServletHolder(new SqlgWebsocketServlet());
+websocketServletHolder.setName("Sqlg-ui websocket servlet");
+contextHandler.addServlet(websocketServletHolder, "/sqlg/data/v1/websocket");
+
+...
+
+SqlgUI.set(sqlgGraph);
+
+
+
+
Webserver
+
+
<filter>
+  <filter-name>SparkFilter</filter-name>
+  <filter-class>spark.servlet.SparkFilter</filter-class>
+  <init-param>
+    <param-name>applicationClass</param-name>
+    <param-value>com.company.YourApplication</param-value>
+  </init-param>
+</filter>
+<filter-mapping>
+  <filter-name>SparkFilter</filter-name>
+  <url-pattern>/*</url-pattern>
+</filter-mapping>
+
+
+
+ + + + + +
+
Warning
+
+The webserver mode has not been tested, nor do I know how the websocket will be made to work. +
+
+
+

The ui is accessible at

+
+
+
+
http://ip:port/sqlg/v1/
+
+
+
+
+

14.2. Authentication

+
+

The ui uses cookie authentication.

+
+
+

To define the users that are allowed to use the ui there must be a corresponding property in sqlg.properties

+
+
+
+
sqlg.ui.username.john=john_password
+sqlg.ui.username.peter=peter_password
+
+
+
+

For the user to be allowed to do any editing there must be the following property in sqlg.properties

+
+
+
+
sqlg.ui.username.john.edit=true
+sqlg.ui.username.peter.edit=true
+
+
+
+

There is one additional property which specifies how long the cookie remains valid for.

+
+
+
+
sqlg.ui.cookie.expiry=3600
+
+
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/sqlg.adoc b/sqlg-doc/docs/2.1.4/sqlg.adoc new file mode 100644 index 000000000..7ea7c91b5 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/sqlg.adoc @@ -0,0 +1,181 @@ += Sqlg Documentation +Pieter Martin +Version 2.1.4, November 2021: +:sectnums: +:toc: left +:toclevels: 4 +:experimental: +:description: Sqlg's documentation +:keywords: Sqlg, TinkerPop, Gremlin, Graph, Database +:imagesdir: ./img +:apidocs: apidocs/ +:tinkerpop-docs: http://tinkerpop.apache.org/docs/current/reference/ +:includedir: include + +https://github.com/pietermartin/sqlg[*Sqlg*] image:github/SVG/mark-github.svg[] is a implementation of http://tinkerpop.apache.org/[Apache TinkerPop] on a +http://en.wikipedia.org/wiki/Relational_database_management_system[RDBMS]. +Currently http://www.postgresql.org/[Postgresql], http://hsqldb.org/[HSQLDB], http://h2database.com[H2], +https://mariadb.org/[MariaDB], https://www.mysql.com/[MySQL] and +https://www.microsoft.com/en-us/sql-server/sql-server-2017[MSSqlServer] are supported. + +Sqlg has a github https://github.com/pietermartin/sqlg/discussions[discussions] page. + +include::{includedir}/introduction.adoc[] + +include::{includedir}/license.adoc[] + +include::{includedir}/supportedFeatures.adoc[] + +include::{includedir}/limitations.adoc[] + +include::{includedir}/gettingStarted.adoc[] + +include::{includedir}/dataTypes.adoc[] + +include::{includedir}/architecture.adoc[] + +include::{includedir}/indexes.adoc[] + +include::{includedir}/multipleJvm.adoc[] + +== Gremlin + +Sqlg optimizes a gremlin link:{tinkerpop-docs}#traversal[`traversal`] by analyzing the +link:{tinkerpop-docs}#graph-traversal-steps[`steps`] and where possible combining them into custom Sqlg steps. This can +significantly reduce the number of database calls. + +Sqlg has two strategies for optimizing TinkerPop steps. + + * Starting with the link:{tinkerpop-docs}#graph-step[`GraphStep`], +consecutive optimizable steps are folded into link:{apidocs}org/umlg/sqlg/step/SqlgGraphStep.html[`SqlgGraphStep`]. This +stops at the first unoptimizable step after which the second strategy is used. +* The second strategy is to `barrier` the incoming elements to the unoptimizable step. This means to exhaust the traversal/iterator +up to the step and cache all the incoming elements for this step. From here the step is executed for all the incoming +elements at once. This strategy effectively changes the semantics to a breath first retrieval. + +[NOTE] +Optimizing gremlin is an ongoing task as gremlin is a large language. + +[NOTE] +Turn sql logging on by setting `log4j.logger.org.umlg.sqlg=debug` + +=== Optimization (strategy 1) + +The following steps are optimized. Steps are + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> + +The combined step will then in turn generate the sql statements to retrieve the data. +It attempts to retrieve the data in as few distinct sql statements as possible. + +include::{includedir}/gremlin/strategy1/graphStep.adoc[] + +include::{includedir}/gremlin/strategy1/vertexStep.adoc[] + +include::{includedir}/gremlin/strategy1/hasStep.adoc[] + +include::{includedir}/gremlin/strategy1/andStep.adoc[] + +include::{includedir}/gremlin/strategy1/notStep.adoc[] + +include::{includedir}/gremlin/strategy1/repeatStep.adoc[] + +include::{includedir}/gremlin/strategy1/optionalStep.adoc[] + +include::{includedir}/gremlin/strategy1/chooseStep.adoc[] + +include::{includedir}/gremlin/strategy1/orderStep.adoc[] + +include::{includedir}/gremlin/strategy1/rangeStep.adoc[] + +include::{includedir}/gremlin/strategy1/limitStep.adoc[] + +include::{includedir}/gremlin/strategy1/dropStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducingSteps.adoc[] + +==== Reducing Steps + +link:{tinkerpop-docs}#a-note-on-barrier-steps[`Reducing Steps`] + +include::{includedir}/gremlin/strategy1/reducing/minStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/maxStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/sumStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/meanStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/countStep.adoc[] + +===== Group By + +link:{tinkerpop-docs}#group-step[`Group Step`]'s are optimized with sql's `group by` clause. + +include::{includedir}/gremlin/strategy1/reducing/groupByMinStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/groupByMaxStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/groupBySumStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/groupByMeanStep.adoc[] + +include::{includedir}/gremlin/strategy1/reducing/groupByCountStep.adoc[] + +=== Optimization (strategy 2) + +The following steps are optimized. Steps are + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> + +The combined step will then in turn generate the sql statements to retrieve the data. +It attempts to retrieve the data in as few distinct sql statements as possible. + +include::{includedir}/gremlin/strategy2/vertexStep.adoc[] + +include::{includedir}/gremlin/strategy2/repeatStep.adoc[] + +include::{includedir}/gremlin/strategy2/optionalStep.adoc[] + +include::{includedir}/gremlin/strategy2/chooseStep.adoc[] + +include::{includedir}/gremlin/strategy2/localStep.adoc[] + +include::{includedir}/gremlin/strategy2/andStep.adoc[] + +include::{includedir}/gremlin/strategy2/orStep.adoc[] + +include::{includedir}/gremlin/strategy2/notStep.adoc[] + +include::{includedir}/gremlin/strategy2/whereStep.adoc[] + +include::{includedir}/gremlin/predicates.adoc[] + +include::{includedir}/batchMode.adoc[] + +include::{includedir}/topology.adoc[] + +include::{includedir}/postgresqlPartitioning.adoc[] + +include::{includedir}/sqlgui.adoc[] diff --git a/sqlg-doc/docs/2.1.4/tocbot.min.js b/sqlg-doc/docs/2.1.4/tocbot.min.js new file mode 100644 index 000000000..4f2b53350 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/tocbot.min.js @@ -0,0 +1 @@ +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var l=t[o]={i:o,l:!1,exports:{}};return e[o].call(l.exports,l,l.exports,n),l.l=!0,l.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)n.d(o,l,function(t){return e[t]}.bind(null,l));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){(function(o){var l,r,i;!function(o,s){r=[],l=function(e){"use strict";var t,o,l,r=n(2),i={},s={},c=n(3),a=n(4),u=!!(e&&e.document&&e.document.querySelector&&e.addEventListener);if("undefined"==typeof window&&!u)return;var d=Object.prototype.hasOwnProperty;function f(e,t,n){var o,l;return t||(t=250),function(){var r=n||this,i=+new Date,s=arguments;o&&ie.fixedSidebarOffset?-1===n.className.indexOf(e.positionFixedClass)&&(n.className+=r+e.positionFixedClass):n.className=n.className.split(r+e.positionFixedClass).join("")}();var c,a=i;if(l&&null!==document.querySelector(e.tocSelector)&&a.length>0){n.call(a,function(t,n){return function t(n){var o=0;return n!==document.querySelector(e.contentSelector&&null!=n)&&(o=n.offsetTop,e.hasInnerContainers&&(o+=t(n.offsetParent))),o}(t)>s+e.headingsOffset+10?(c=a[0===n?n:n-1],!0):n===a.length-1?(c=a[a.length-1],!0):void 0});var u=document.querySelector(e.tocSelector).querySelectorAll("."+e.linkClass);t.call(u,function(t){t.className=t.className.split(r+e.activeLinkClass).join("")});var d=document.querySelector(e.tocSelector).querySelectorAll("."+e.listItemClass);t.call(d,function(t){t.className=t.className.split(r+e.activeListItemClass).join("")});var f=document.querySelector(e.tocSelector).querySelector("."+e.linkClass+".node-name--"+c.nodeName+'[href="'+e.basePath+"#"+c.id.replace(/([ #;&,.+*~':"!^$[\]()=>|/@])/g,"\\$1")+'"]');-1===f.className.indexOf(e.activeLinkClass)&&(f.className+=r+e.activeLinkClass);var m=f.parentNode;m&&-1===m.className.indexOf(e.activeListItemClass)&&(m.className+=r+e.activeListItemClass);var h=document.querySelector(e.tocSelector).querySelectorAll("."+e.listClass+"."+e.collapsibleClass);t.call(h,function(t){-1===t.className.indexOf(e.isCollapsedClass)&&(t.className+=r+e.isCollapsedClass)}),f.nextSibling&&-1!==f.nextSibling.className.indexOf(e.isCollapsedClass)&&(f.nextSibling.className=f.nextSibling.className.split(r+e.isCollapsedClass).join("")),function t(n){return-1!==n.className.indexOf(e.collapsibleClass)&&-1!==n.className.indexOf(e.isCollapsedClass)?(n.className=n.className.split(r+e.isCollapsedClass).join(""),t(n.parentNode.parentNode)):n}(f.parentNode.parentNode)}}}}},function(e,t){e.exports=function(e){var t=[].reduce;function n(e){return e[e.length-1]}function o(t){if(!(t instanceof window.HTMLElement))return t;if(e.ignoreHiddenElements&&(!t.offsetHeight||!t.offsetParent))return null;var n={id:t.id,children:[],nodeName:t.nodeName,headingLevel:function(e){return+e.nodeName.split("H").join("")}(t),textContent:e.headingLabelCallback?String(e.headingLabelCallback(t.textContent)):t.textContent.trim()};return e.includeHtml&&(n.childNodes=t.childNodes),e.headingObjectCallback?e.headingObjectCallback(n,t):n}return{nestHeadingsArray:function(l){return t.call(l,function(t,l){var r=o(l);return r&&function(t,l){for(var r=o(t),i=r.headingLevel,s=l,c=n(s),a=i-(c?c.headingLevel:0);a>0;)(c=n(s))&&void 0!==c.children&&(s=c.children),a--;i>=e.collapseDepth&&(r.isCollapsed=!0),s.push(r)}(r,t.nest),t},{nest:[]})},selectHeadings:function(t,n){var o=n;e.ignoreSelector&&(o=n.split(",").map(function(t){return t.trim()+":not("+e.ignoreSelector+")"}));try{return document.querySelector(t).querySelectorAll(o)}catch(e){return console.warn("Element not found: "+t),null}}}}},function(e,t){function n(e,t){var n=window.pageYOffset,o={duration:t.duration,offset:t.offset||0,callback:t.callback,easing:t.easing||d},l=document.querySelector('[id="'+decodeURI(e).split("#").join("")+'"]'),r=typeof e==="string"?o.offset+(e?l&&l.getBoundingClientRect().top||0:-(document.documentElement.scrollTop||document.body.scrollTop)):e,i=typeof o.duration==="function"?o.duration(r):o.duration,s,c;function a(e){c=e-s;window.scrollTo(0,o.easing(c,n,r,i));if(c0||"#"===e.href.charAt(e.href.length-1))&&(r(e.href)===l||r(e.href)+"#"===l)}(i.target)||i.target.className.indexOf("no-smooth-scroll")>-1||"#"===i.target.href.charAt(i.target.href.length-2)&&"!"===i.target.href.charAt(i.target.href.length-1)||-1===i.target.className.indexOf(e.linkClass))return;n(i.target.hash,{duration:t,offset:o,callback:function(){!function(e){var t=document.getElementById(e.substring(1));t&&(/^(?:a|select|input|button|textarea)$/i.test(t.tagName)||(t.tabIndex=-1),t.focus())}(i.target.hash)}})},!1)}()}}]); \ No newline at end of file diff --git a/sqlg-doc/docs/2.1.4/volnitsky.css b/sqlg-doc/docs/2.1.4/volnitsky.css new file mode 100644 index 000000000..42ae7d239 --- /dev/null +++ b/sqlg-doc/docs/2.1.4/volnitsky.css @@ -0,0 +1,435 @@ +/* + * AsciiDoc 'volnitsky' theme for xhtml11 and html5 backends. + * Based on css from http://volnitsky.com, which was in turn based on default + * theme from AsciiDoc + * + * FIXME: The styling is still a bit rough in places. + * + */ + +/* Default font. */ +body { + font-family: Georgia,"Times New Roman",Times,serif; +} + +/* Title font. */ +h1, h2, h3, h4, h5, h6, +div.title, caption.title, +thead, p.table.header, +#toctitle, +#author, #revnumber, #revdate, #revremark, +#footer { + font-family: Candara,Arial,sans-serif; +} + + +#toc a { + border-bottom: 1px dotted #999999; + color: #3A3A4D !important; + text-decoration: none !important; +} +#toc a:hover { + border-bottom: 1px solid #6D4100; + color: #6D4100 !important; + text-decoration: none !important; +} +a { color: #666688; text-decoration: none; border-bottom: 1px dotted #666688; } +a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } +a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } + +em { + font-style: italic; + color: #444466; +} + +strong { + font-weight: bold; + color: #444466; +} + +h1, h2, h3, h4, h5, h6 { + color: #666688; + margin-bottom: 0.5em; + line-height: 1.3; + letter-spacing:+0.15em; +} + +h1, h2, h3 { border-bottom: 2px solid #ccd; } +h2 { padding-top: 0.5em; } +h3 { float: left; } +h3 + * { clear: left; } + +div.sectionbody { + margin-left: 0; +} + +hr { + border: 1px solid #444466; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +#author { + color: #444466; + font-weight: bold; + font-size: 1.1em; +} + +#footer { + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} + +#footer-text { + float: left; + padding-bottom: 0.5em; +} + +#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.5em; + margin-bottom: 1.5em; +} + +div.admonitionblock { + margin-top: 2.5em; + margin-bottom: 2.5em; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #444466; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock { + padding-left: 2.0em; + margin-right: 10%; +} +div.verseblock > pre.content { + font-family: inherit; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #444466; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 2px solid silver; +} + +div.exampleblock > div.content { + border-left: 2px solid silver; + padding: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: #444466; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +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; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #444466; +} +thead { + font-weight: bold; + color: #444466; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: #444466; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +@media print { + #footer-badges { display: none; } +} + +#toctitle { + color: #666688; + font-size: 1.2em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; } +div.toclevel1 { margin-top: 0.3em; margin-left: 0; font-size: 1.0em; } +div.toclevel2 { margin-top: 0.25em; margin-left: 2em; font-size: 0.9em; } +div.toclevel3 { margin-left: 4em; font-size: 0.8em; } +div.toclevel4 { margin-left: 6em; font-size: 0.8em; } + +body { + margin: 1em 5%; + max-width: 55em; + padding-left: 0; + +} + +.monospaced, tt, div.listingblock > div.content { + font-family: Consolas, "Andale Mono", "Courier New", monospace; + color: #004400; + background: #f4f4f4; + max-width: 80em; + line-height: 1.2em; +} + +.paragraph p { + line-height: 1.5em; + margin-top: 1em; +} + +.paragraph p, li, dd, .content { max-width: 45em; } +.admonitionblock { max-width: 35em; } + +div.sectionbody div.ulist > ul > li { + list-style-type: square; + color: #aaa; +} + div.sectionbody div.ulist > ul > li > * { + color: black; + /*font-size: 50%;*/ + } + + +div.sectionbody div.ulist > ul > li div.ulist > ul > li { + color: #ccd ; +} + div.sectionbody div.ulist > ul > li div.ulist > ul > li > * { + color: black ; + } + +em { + font-style: normal ! important; + font-weight: bold ! important; + color: #662222 ! important; + letter-spacing:+0.08em ! important; +} + + +/* + * html5 specific + * + * */ + +table.tableblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +thead, p.tableblock.header { + font-weight: bold; + color: #666688; +} +p.tableblock { + margin-top: 0; +} +table.tableblock { + border-width: 3px; + border-spacing: 0px; + border-style: solid; + border-color: #444466; + border-collapse: collapse; +} +th.tableblock, td.tableblock { + border-width: 1px; + padding: 4px; + border-style: solid; + border-color: #444466; +} + +table.tableblock.frame-topbot { + border-left-style: hidden; + border-right-style: hidden; +} +table.tableblock.frame-sides { + border-top-style: hidden; + border-bottom-style: hidden; +} +table.tableblock.frame-none { + border-style: hidden; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + + diff --git a/sqlg-doc/src/main/java/org/umlg/sqlg/doc/AsciiDoctor.java b/sqlg-doc/src/main/java/org/umlg/sqlg/doc/AsciiDoctor.java index 3a122b6cb..61e65f50e 100644 --- a/sqlg-doc/src/main/java/org/umlg/sqlg/doc/AsciiDoctor.java +++ b/sqlg-doc/src/main/java/org/umlg/sqlg/doc/AsciiDoctor.java @@ -24,7 +24,7 @@ public static void main(String[] args) { private void createDocs() { // String version = "2.0.1"; - String version = "2.1.3"; + String version = "2.1.4"; Asciidoctor asciidoctor = create(); try { File file = new File("sqlg-doc/docs/" + version + "/sqlg.adoc");